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 25a5da5b836..7ddeecaeae5 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -204,15 +204,32 @@ public class TestCardRenderDialog extends MageDialog { game.addPlayer(playerOpponent, deck); List cardViews = new ArrayList<>(); - ///* + /* // test morphed cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "263", 0, 0, 0, false)); // mountain cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true)); // Judith, the Scourge Diva - //*/ cardViews.add(createHandCard(game, playerYou.getId(), "DIS", "153")); // Odds // Ends (split card) cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "38")); // Animating Faerie (adventure card) cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, false, false)); // face down cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", true, false, true)); // morphed cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, true, false)); // manifested + //*/ + + /* //test emblems + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false)); // Noxious Groodion + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false)); // Knight of Sorrows + cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false)); // Huntmaster of the Fells, transforms + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "221", 0, 0, 0, false)); // Bedeck // Bedazzle + cardViews.add(createPermanentCard(game, playerYou.getId(), "XLN", "234", 0, 0, 0, false)); // Conqueror's Galleon + cardViews.add(createEmblem(new AjaniAdversaryOfTyrantsEmblem())); // Emblem Ajani + cardViews.add(createPlane(new AkoumPlane())); // Plane - Akoum + //*/ + + //* //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 + cardViews.add(createHandCard(game, playerYou.getId(), "ZNR", "134")); // Akoum Warrior + //*/ // duplicate cards if (checkBoxGenerateManyCards.isSelected()) { @@ -226,17 +243,6 @@ public class TestCardRenderDialog extends MageDialog { } } - /* - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false)); // Noxious Groodion - - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false)); // Knight of Sorrows - cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false)); // Huntmaster of the Fells, transforms - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "221", 0, 0, 0, false)); // Bedeck // Bedazzle - cardViews.add(createPermanentCard(game, playerYou.getId(), "XLN", "234", 0, 0, 0, false)); // Conqueror's Galleon - cardViews.add(createEmblem(new AjaniAdversaryOfTyrantsEmblem())); // Emblem Ajani - cardViews.add(createPlane(new AkoumPlane())); // Plane - Akoum - //*/ - BigCard big = new BigCard(); CardsView view = new CardsView(cardViews); cardsPanel.loadCards(view, big, game.getId()); 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 5349653ee3e..13232664739 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 @@ -579,7 +579,10 @@ public class MageActionCallback implements ActionCallback { break; case ALTERNATE: if (cardView.getAlternateName() != null) { - if (cardView instanceof PermanentView && !cardView.isFlipCard() && !cardView.canTransform() && ((PermanentView) cardView).isCopy()) { + if (cardView instanceof PermanentView + && !cardView.isFlipCard() + && !cardView.canTransform() + && ((PermanentView) cardView).isCopy()) { image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal()); } else { image = ImageCache.getImageOriginalAlternateName(cardView); 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 44d167a91e4..638f1e99850 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 @@ -819,9 +819,12 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, this.setTemporary(null); } } + + // switch card names for render String temp = this.getGameCard().getAlternateName(); this.getGameCard().setAlternateName(this.getGameCard().getOriginalName()); this.getGameCard().setOriginalName(temp); + updateArtImage(); } 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 0cb327fa3af..38a347d5b8f 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 @@ -86,9 +86,9 @@ public enum ScryfallImageSource implements CardImageSource { } } - // double faced card + // double faced cards (modal double faces cards too) // the front face can be downloaded normally - // the back face is prepared beforehand + // the back face is prepared before hand if (baseUrl == null && card.isTwoFacedCard() && !card.isSecondSide()) { baseUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/" + card.getCollectorId() + "/" + localizedCode + "?format=image"; 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 7790f8ac532..16fcf6c6130 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 @@ -449,12 +449,30 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements if (card.getFlipCardName() == null || card.getFlipCardName().trim().isEmpty()) { throw new IllegalStateException("Flipped card can't have empty name."); } - CardDownloadData cardDownloadData = new CardDownloadData(card.getFlipCardName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), card.isNightCard()); + CardDownloadData cardDownloadData = new CardDownloadData( + card.getFlipCardName(), + card.getSetCode(), + card.getCardNumber(), + card.usesVariousArt(), + 0, "", "", false, card.isDoubleFaced(), card.isNightCard()); cardDownloadData.setFlipCard(true); cardDownloadData.setFlippedSide(true); cardDownloadData.setType2(isType2); allCardsUrls.add(cardDownloadData); } + if (card.isModalDoubleFacesCard()) { + if (card.getModalDoubleFacesSecondSideName() == null || card.getModalDoubleFacesSecondSideName().trim().isEmpty()) { + throw new IllegalStateException("MDF card can't have empty name."); + } + CardDownloadData cardDownloadData = new CardDownloadData( + card.getModalDoubleFacesSecondSideName(), + card.getSetCode(), + card.getCardNumber(), + card.usesVariousArt(), + 0, "", "", false, true, true); + cardDownloadData.setType2(isType2); + allCardsUrls.add(cardDownloadData); + } } else if (card.getCardNumber().isEmpty() || "0".equals(card.getCardNumber())) { logger.error("Card has no collector ID and won't be sent to client: " + card.getName()); } else if (card.getSetCode().isEmpty()) { diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index 15dabfd8c16..b54fa545949 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -1,8 +1,6 @@ package mage.view; import com.google.gson.annotations.Expose; -import java.util.*; -import java.util.stream.Collectors; import mage.MageObject; import mage.ObjectColor; import mage.abilities.Abilities; @@ -31,6 +29,9 @@ import mage.target.Targets; import mage.util.CardUtil; import mage.util.SubTypeList; +import java.util.*; +import java.util.stream.Collectors; + /** * @author BetaSteward_at_googlemail.com */ @@ -75,7 +76,7 @@ public class CardView extends SimpleCardView { protected CardView ability; protected int type; - protected boolean transformable; + protected boolean transformable; // can toggle one card side to another (transformable cards, modal double faces) protected CardView secondCardFace; protected boolean transformed; @@ -95,6 +96,8 @@ public class CardView extends SimpleCardView { protected List rightSplitRules; protected String rightSplitTypeLine; + protected boolean isModalDoubleFacesCard; + protected ArtRect artRect = ArtRect.NORMAL; protected List targets; @@ -187,6 +190,8 @@ public class CardView extends SimpleCardView { this.rightSplitRules = cardView.rightSplitRules == null ? null : new ArrayList<>(cardView.rightSplitRules); this.rightSplitTypeLine = cardView.rightSplitTypeLine; + this.isModalDoubleFacesCard = cardView.isModalDoubleFacesCard; + this.artRect = cardView.artRect; this.targets = cardView.targets == null ? null : new ArrayList<>(cardView.targets); this.pairedCard = cardView.pairedCard; @@ -299,7 +304,7 @@ public class CardView extends SimpleCardView { } SplitCard splitCard = null; - if (card.isSplitCard()) { + if (card instanceof SplitCard) { splitCard = (SplitCard) card; rotate = (card.getSpellAbility().getSpellAbilityType()) != SpellAbilityType.SPLIT_AFTERMATH; } else if (card instanceof Spell) { @@ -316,6 +321,10 @@ public class CardView extends SimpleCardView { case SPLIT_RIGHT: rotate = true; break; + case MODAL_LEFT: + case MODAL_RIGHT: + rotate = false; + break; } } @@ -334,6 +343,12 @@ public class CardView extends SimpleCardView { fullCardName = card.getName(); // split card contains full name as normal this.manaCostLeft = splitCard.getLeftHalfCard().getManaCost().getSymbols(); this.manaCostRight = splitCard.getRightHalfCard().getManaCost().getSymbols(); + } else if (card instanceof ModalDoubleFacesCard) { + this.isModalDoubleFacesCard = true; + ModalDoubleFacesCard mainCard = ((ModalDoubleFacesCard) card); + fullCardName = mainCard.getLeftHalfCard().getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + mainCard.getRightHalfCard().getName(); + this.manaCostLeft = mainCard.getLeftHalfCard().getManaCost().getSymbols(); + this.manaCostRight = mainCard.getRightHalfCard().getManaCost().getSymbols(); } else if (card instanceof AdventureCard) { AdventureCard adventureCard = ((AdventureCard) card); AdventureCardSpell adventureCardSpell = ((AdventureCardSpell) adventureCard.getSpellCard()); @@ -402,7 +417,6 @@ public class CardView extends SimpleCardView { this.subTypes = card.getSubtype(game); this.superTypes = card.getSuperType(); this.color = card.getColor(game); - this.transformable = card.isTransformable(); this.flipCard = card.isFlipCard(); this.faceDown = !showFaceUp; @@ -421,7 +435,7 @@ public class CardView extends SimpleCardView { this.tokenDescriptor = card.getTokenDescriptor(); } // - // set code und card number for token copies to get the image + // set code and card number for token copies to get the image this.rules = card.getRules(game); this.type = ((PermanentToken) card).getToken().getTokenType(); } else { @@ -429,18 +443,30 @@ public class CardView extends SimpleCardView { this.isToken = false; } + // transformable, double faces cards + this.transformable = card.isTransformable(); + Card secondSideCard = card.getSecondCardFace(); if (secondSideCard != null) { this.secondCardFace = new CardView(secondSideCard); 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) { + this.transformable = true; // enable GUI day/night button + ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card; + this.secondCardFace = new CardView(mdfCard.getRightHalfCard()); + this.alternateName = mdfCard.getRightHalfCard().getName(); + this.originalName = card.getName(); + } + if (card instanceof Spell) { this.mageObjectType = MageObjectType.SPELL; Spell spell = (Spell) card; @@ -455,6 +481,7 @@ public class CardView extends SimpleCardView { // Determine what part of the art to slice out for spells on the stack which originate // from a split, fuse, or aftermath split card. + // Modal double faces cards draws as normal cards SpellAbilityType ty = spell.getSpellAbility().getSpellAbilityType(); if (ty == SpellAbilityType.SPLIT_RIGHT || ty == SpellAbilityType.SPLIT_LEFT || ty == SpellAbilityType.SPLIT_FUSED) { // Needs a special art rect @@ -871,7 +898,7 @@ public class CardView extends SimpleCardView { } /** - * Name of the other side (transform), flipped, or copying card name. + * Name of the other side (transform), flipped, modal double faces card or copying card name. * * @return name */ diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java index 0d86bc7525f..b4b0582418e 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java @@ -1,9 +1,6 @@ package mage.deck; -import mage.cards.Card; -import mage.cards.ExpansionSet; -import mage.cards.Sets; -import mage.cards.SplitCard; +import mage.cards.*; import mage.cards.decks.Constructed; import mage.cards.decks.Deck; import mage.cards.decks.DeckValidatorErrorType; @@ -199,21 +196,27 @@ public class TinyLeaders extends Constructed { return false; } - //905.5b - Converted mana cost must be 3 or less + // 906.5b + // Each card must have a converted mana cost of three or less. Cards with {x} in their mana cost count X + // as zero for this purpose. Split cards are legal only if both of their halves would be legal independently. + List costs = new ArrayList<>(); if (card instanceof SplitCard) { - if (((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() > 3) { - addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid cost (" + ((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() + ')', true); - return false; - } - if (((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() > 3) { - addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid cost (" + ((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() + ')', true); - return false; - } - } else if (card.getManaCost().convertedManaCost() > 3) { - addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ')', true); - return false; + costs.add(((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost()); + costs.add(((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost()); + } else if (card instanceof ModalDoubleFacesCard) { + costs.add(((ModalDoubleFacesCard) card).getLeftHalfCard().getManaCost().convertedManaCost()); + costs.add(((ModalDoubleFacesCard) card).getRightHalfCard().getManaCost().convertedManaCost()); + } else { + costs.add(card.getManaCost().convertedManaCost()); } - return true; + + return costs.stream().allMatch(cost -> { + if (cost > 3) { + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid cost (" + cost + ')', true); + return false; + } + return true; + }); } /** diff --git a/Mage.Sets/src/mage/cards/a/AgadeemTheUndercrypt.java b/Mage.Sets/src/mage/cards/a/AgadeemTheUndercrypt.java deleted file mode 100644 index 7b29c4fee10..00000000000 --- a/Mage.Sets/src/mage/cards/a/AgadeemTheUndercrypt.java +++ /dev/null @@ -1,42 +0,0 @@ -package mage.cards.a; - -import mage.abilities.common.AsEntersBattlefieldAbility; -import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.effects.common.TapSourceUnlessPaysEffect; -import mage.abilities.mana.BlackManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class AgadeemTheUndercrypt extends CardImpl { - - public AgadeemTheUndercrypt(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // As Agadeem, the Undercrypt enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. - this.addAbility(new AsEntersBattlefieldAbility( - new TapSourceUnlessPaysEffect(new PayLifeCost(3)), - "you may pay 3 life. If you don't, it enters the battlefield tapped" - )); - - // {T}: Add {B}. - this.addAbility(new BlackManaAbility()); - } - - private AgadeemTheUndercrypt(final AgadeemTheUndercrypt card) { - super(card); - } - - @Override - public AgadeemTheUndercrypt copy() { - return new AgadeemTheUndercrypt(this); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AgadeemsAwakening.java b/Mage.Sets/src/mage/cards/a/AgadeemsAwakening.java index e446934e3f8..c42c81fdfba 100644 --- a/Mage.Sets/src/mage/cards/a/AgadeemsAwakening.java +++ b/Mage.Sets/src/mage/cards/a/AgadeemsAwakening.java @@ -2,11 +2,16 @@ package mage.cards.a; import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; +import mage.abilities.mana.BlackManaAbility; import mage.cards.Card; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.game.Game; @@ -18,22 +23,39 @@ import java.util.UUID; import java.util.stream.Collectors; /** - * @author TheElk801 + * @author JayDi85 */ -public final class AgadeemsAwakening extends CardImpl { +public final class AgadeemsAwakening extends ModalDoubleFacesCard { public AgadeemsAwakening(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{B}{B}{B}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{X}{B}{B}{B}", + "Agadeem, the Undercrypt", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.a.AgadeemTheUndercrypt.class; + // 1. + // Agadeem's Awakening + // Sorcery // Return from your graveyard to the battlefield any number of target creature cards that each have a different converted mana cost X or less. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect().setText( + this.getLeftHalfCard().getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect().setText( "return from your graveyard to the battlefield any number of target creature cards " + "that each have a different converted mana cost X or less" )); - this.getSpellAbility().setTargetAdjuster(AgadeemsAwakeningAdjuster.instance); + this.getLeftHalfCard().getSpellAbility().setTargetAdjuster(AgadeemsAwakeningAdjuster.instance); + + // 2. + // Agadeem, the Undercrypt + // Land + + // As Agadeem, the Undercrypt enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. + this.getRightHalfCard().addAbility(new AsEntersBattlefieldAbility( + new TapSourceUnlessPaysEffect(new PayLifeCost(3)), + "you may pay 3 life. If you don't, it enters the battlefield tapped" + )); + + // {T}: Add {B}. + this.getRightHalfCard().addAbility(new BlackManaAbility()); } private AgadeemsAwakening(final AgadeemsAwakening card) { diff --git a/Mage.Sets/src/mage/cards/a/AgelessSentinels.java b/Mage.Sets/src/mage/cards/a/AgelessSentinels.java index d893d50db00..892e01369a8 100644 --- a/Mage.Sets/src/mage/cards/a/AgelessSentinels.java +++ b/Mage.Sets/src/mage/cards/a/AgelessSentinels.java @@ -1,28 +1,22 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BlocksSourceTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.LoseAbilitySourceEffect; -import mage.constants.SubType; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.FlyingAbility; 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.*; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class AgelessSentinels extends CardImpl { @@ -43,7 +37,7 @@ public final class AgelessSentinels extends CardImpl { // When Ageless Sentinels blocks, it becomes a Bird Giant, and it loses defender. Ability ability = new BlocksSourceTriggeredAbility(new AgelessSentinelsEffect(), false, false, true); Effect effect = new LoseAbilitySourceEffect(DefenderAbility.getInstance(), Duration.WhileOnBattlefield); - effect.setText("and it loses defender"); + effect.setText(", and it loses defender"); ability.addEffect(effect); this.addAbility(ability); } @@ -60,8 +54,8 @@ public final class AgelessSentinels extends CardImpl { private class AgelessSentinelsEffect extends ContinuousEffectImpl { public AgelessSentinelsEffect() { - super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); - staticText = "it becomes a Bird Giant,"; + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature); + staticText = "it becomes a Bird Giant"; } public AgelessSentinelsEffect(final AgelessSentinelsEffect effect) { @@ -74,27 +68,16 @@ public final class AgelessSentinels extends CardImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent == null) { return false; } - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getSubtype(game).clear(); - permanent.getSubtype(game).add(SubType.BIRD, SubType.GIANT); - } - break; - } + permanent.removeAllCreatureTypes(game); + permanent.addSubType(game, SubType.BIRD, SubType.GIANT); return true; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public boolean hasLayer(Layer layer) { return layer == Layer.TypeChangingEffects_4; diff --git a/Mage.Sets/src/mage/cards/a/AkoumTeeth.java b/Mage.Sets/src/mage/cards/a/AkoumTeeth.java deleted file mode 100644 index 4db7ed2a8a2..00000000000 --- a/Mage.Sets/src/mage/cards/a/AkoumTeeth.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.a; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.RedManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class AkoumTeeth extends CardImpl { - - public AkoumTeeth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Akoum Teeth enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {R}. - this.addAbility(new RedManaAbility()); - } - - private AkoumTeeth(final AkoumTeeth card) { - super(card); - } - - @Override - public AkoumTeeth copy() { - return new AkoumTeeth(this); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AkoumWarrior.java b/Mage.Sets/src/mage/cards/a/AkoumWarrior.java index ee9d7a09958..0e47d1e5b66 100644 --- a/Mage.Sets/src/mage/cards/a/AkoumWarrior.java +++ b/Mage.Sets/src/mage/cards/a/AkoumWarrior.java @@ -1,32 +1,44 @@ package mage.cards.a; import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.keyword.TrampleAbility; -import mage.cards.CardImpl; +import mage.abilities.mana.RedManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class AkoumWarrior extends CardImpl { +public final class AkoumWarrior extends ModalDoubleFacesCard { public AkoumWarrior(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}"); + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.MINOTAUR, SubType.WARRIOR}, "{5}{R}", + "Akoum Teeth", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.subtype.add(SubType.MINOTAUR); - this.subtype.add(SubType.WARRIOR); - this.power = new MageInt(4); - this.toughness = new MageInt(5); - - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.a.AkoumTeeth.class; + // 1. + // Akoum Warrior + // Creature — Minotaur Warrior + this.getLeftHalfCard().setPT(new MageInt(4), new MageInt(5)); // Trample - this.addAbility(TrampleAbility.getInstance()); + this.getLeftHalfCard().addAbility(TrampleAbility.getInstance()); + + // 2. + // Akoum Teeth + // Land + + // Akoum Teeth enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {R}. + this.getRightHalfCard().addAbility(new RedManaAbility()); } private AkoumWarrior(final AkoumWarrior card) { diff --git a/Mage.Sets/src/mage/cards/a/AlphaStatus.java b/Mage.Sets/src/mage/cards/a/AlphaStatus.java index 844141280db..ec6fdd63ee6 100644 --- a/Mage.Sets/src/mage/cards/a/AlphaStatus.java +++ b/Mage.Sets/src/mage/cards/a/AlphaStatus.java @@ -61,7 +61,7 @@ class AlphaStatusDynamicValue implements DynamicValue { if (enchanted != null) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, game)) { if (!permanent.getId().equals(enchanted.getId())) { - if (enchanted.shareSubtypes(permanent, game)) { + if (enchanted.shareCreatureTypes(permanent, game)) { xValue += 2; } } diff --git a/Mage.Sets/src/mage/cards/a/AmarethTheLustrous.java b/Mage.Sets/src/mage/cards/a/AmarethTheLustrous.java new file mode 100644 index 00000000000..ef0d895baa8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AmarethTheLustrous.java @@ -0,0 +1,99 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AmarethTheLustrous extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("another permanent"); + + static { + filter.add(AnotherPredicate.instance); + } + + public AmarethTheLustrous(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{U}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever another permanent enters the battlefield under your control, look at the top card of your library. If it shares a card type with that permanent, you may reveal that card and put it into your hand. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new AmarethTheLustrousEffect(), filter)); + } + + private AmarethTheLustrous(final AmarethTheLustrous card) { + super(card); + } + + @Override + public AmarethTheLustrous copy() { + return new AmarethTheLustrous(this); + } +} + +class AmarethTheLustrousEffect extends OneShotEffect { + + AmarethTheLustrousEffect() { + super(Outcome.Benefit); + staticText = "look at the top card of your library. If it shares a card type with that permanent, " + + "you may reveal that card and put it into your hand"; + } + + private AmarethTheLustrousEffect(final AmarethTheLustrousEffect effect) { + super(effect); + } + + @Override + public AmarethTheLustrousEffect copy() { + return new AmarethTheLustrousEffect(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("Top card of library", card, game); + Object obj = getValue("permanentEnteringBattlefield"); + Permanent permanent = null; + if (obj instanceof Permanent) { + permanent = (Permanent) obj; + } + if (permanent == null + || card.getCardType().stream().noneMatch(permanent.getCardType()::contains) + || !player.chooseUse(Outcome.DrawCard, "Reveal " + card.getName() + " and put it into your hand?", source, game)) { + return false; + } + player.revealCards(source, new CardsImpl(card), game); + player.moveCards(card, Zone.HAND, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AmoeboidChangeling.java b/Mage.Sets/src/mage/cards/a/AmoeboidChangeling.java index fc593bf8496..604487800b8 100644 --- a/Mage.Sets/src/mage/cards/a/AmoeboidChangeling.java +++ b/Mage.Sets/src/mage/cards/a/AmoeboidChangeling.java @@ -7,6 +7,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainAllCreatureTypesTargetEffect; import mage.abilities.effects.common.continuous.LoseAllCreatureTypesTargetEffect; import mage.abilities.keyword.ChangelingAbility; import mage.cards.CardImpl; @@ -27,13 +28,13 @@ public final class AmoeboidChangeling extends CardImpl { this.toughness = new MageInt(1); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // {tap}: Target creature gains all creature types until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new GainAbilityTargetEffect(ChangelingAbility.getInstance(), Duration.EndOfTurn, null, false, Layer.TypeChangingEffects_4, SubLayer.NA), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility( + new GainAllCreatureTypesTargetEffect(Duration.EndOfTurn), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); - this.addAbility(ability); // {tap}: Target creature loses all creature types until end of turn. diff --git a/Mage.Sets/src/mage/cards/a/AmorphousAxe.java b/Mage.Sets/src/mage/cards/a/AmorphousAxe.java index 65acf2bc68a..6732ef2d53f 100644 --- a/Mage.Sets/src/mage/cards/a/AmorphousAxe.java +++ b/Mage.Sets/src/mage/cards/a/AmorphousAxe.java @@ -3,12 +3,10 @@ package mage.cards.a; 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.ChangelingAbility; +import mage.abilities.effects.common.continuous.GainAllCreatureTypesAttachedEffect; 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; @@ -26,9 +24,7 @@ public final class AmorphousAxe extends CardImpl { // Equipped creature gets +3/+0 and is every creature type. Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(3, 0)); - ability.addEffect(new GainAbilityAttachedEffect( - ChangelingAbility.getInstance(), AttachmentType.EQUIPMENT - ).setText("and is every creature type")); + ability.addEffect(new GainAllCreatureTypesAttachedEffect()); this.addAbility(ability); // Equip {3} diff --git a/Mage.Sets/src/mage/cards/a/AmphinMutineer.java b/Mage.Sets/src/mage/cards/a/AmphinMutineer.java new file mode 100644 index 00000000000..7a6467d532d --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AmphinMutineer.java @@ -0,0 +1,95 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.EncoreAbility; +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.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SalamnderWarriorToken; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AmphinMutineer extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("non-Salamander creature"); + + static { + filter.add(Predicates.not(SubType.SALAMANDER.getPredicate())); + } + + public AmphinMutineer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.SALAMANDER); + this.subtype.add(SubType.PIRATE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Amphin Mutineer enters the battlefield, exile up to one target non-Salamander creature. That creature's controller creates a 4/3 blue Salamander Warrior creature token. + Ability ability = new EntersBattlefieldTriggeredAbility(new AmphinMutineerEffect()); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + this.addAbility(ability); + + // Encore {4}{U}{U} + this.addAbility(new EncoreAbility(new ManaCostsImpl<>("{4}{U}{U}"))); + } + + private AmphinMutineer(final AmphinMutineer card) { + super(card); + } + + @Override + public AmphinMutineer copy() { + return new AmphinMutineer(this); + } +} + +class AmphinMutineerEffect extends OneShotEffect { + + AmphinMutineerEffect() { + super(Outcome.Benefit); + staticText = "exile up to one target non-Salamander creature. " + + "That creature's controller creates a 4/3 blue Salamander Warrior creature token"; + } + + private AmphinMutineerEffect(final AmphinMutineerEffect effect) { + super(effect); + } + + @Override + public AmphinMutineerEffect copy() { + return new AmphinMutineerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + Player player = game.getPlayer(permanent.getControllerId()); + if (player == null) { + return false; + } + player.moveCards(permanent, Zone.EXILED, source, game); + new SalamnderWarriorToken().putOntoBattlefield(1, game, source.getSourceId(), player.getId()); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/ApexDevastator.java b/Mage.Sets/src/mage/cards/a/ApexDevastator.java new file mode 100644 index 00000000000..0aec3ef3cab --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ApexDevastator.java @@ -0,0 +1,46 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.keyword.CascadeAbility; +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 ApexDevastator extends CardImpl { + + public ApexDevastator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{8}{G}{G}"); + + this.subtype.add(SubType.HYDRA); + this.subtype.add(SubType.CHIMERA); + this.power = new MageInt(10); + this.toughness = new MageInt(10); + + // Cascade + this.addAbility(new CascadeAbility(false)); + + // Cascade + this.addAbility(new CascadeAbility(false)); + + // Cascade + this.addAbility(new CascadeAbility(false)); + + // Cascade + this.addAbility(new CascadeAbility()); + } + + private ApexDevastator(final ApexDevastator card) { + super(card); + } + + @Override + public ApexDevastator copy() { + return new ApexDevastator(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArcaneAdaptation.java b/Mage.Sets/src/mage/cards/a/ArcaneAdaptation.java index fb1a7691d33..a788844dcbf 100644 --- a/Mage.Sets/src/mage/cards/a/ArcaneAdaptation.java +++ b/Mage.Sets/src/mage/cards/a/ArcaneAdaptation.java @@ -116,8 +116,8 @@ class ConspyEffect extends ContinuousEffectImpl { List creatures = game.getBattlefield().getAllActivePermanents( new FilterControlledCreaturePermanent(), source.getControllerId(), game); for (Permanent creature : creatures) { - if (creature != null && !creature.hasSubtype(subType, game)) { - creature.getSubtype(game).add(subType); + if (creature != null) { + creature.addSubType(game, subType); } } return true; diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfCoronation.java b/Mage.Sets/src/mage/cards/a/ArchonOfCoronation.java new file mode 100644 index 00000000000..91159f4bb74 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArchonOfCoronation.java @@ -0,0 +1,91 @@ +package mage.cards.a; + +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.BecomesMonarchSourceEffect; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ArchonOfCoronation extends CardImpl { + + public ArchonOfCoronation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}"); + + this.subtype.add(SubType.ARCHON); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Archon of Coronation enters the battlefield, you become the monarch. + this.addAbility(new EntersBattlefieldTriggeredAbility(new BecomesMonarchSourceEffect())); + + // As long as you're the monarch, damage doesn't cause you to lose life. + this.addAbility(new SimpleStaticAbility(new ArchonOfCoronationEffect())); + } + + private ArchonOfCoronation(final ArchonOfCoronation card) { + super(card); + } + + @Override + public ArchonOfCoronation copy() { + return new ArchonOfCoronation(this); + } +} + +class ArchonOfCoronationEffect extends ReplacementEffectImpl { + + ArchonOfCoronationEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "As long as you're the monarch, damage doesn't cause you to lose life. " + + "(When a creature deals combat damage to you, its controller still becomes the monarch.)"; + } + + private ArchonOfCoronationEffect(final ArchonOfCoronationEffect effect) { + super(effect); + } + + @Override + public ArchonOfCoronationEffect copy() { + return new ArchonOfCoronationEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_CAUSES_LIFE_LOSS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()) + && source.isControlledBy(game.getMonarchId()); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(0); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java b/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java index 615f5d7e243..54a880253dc 100644 --- a/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java +++ b/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java @@ -1,42 +1,37 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.Effect; +import mage.abilities.effects.common.TapSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import static mage.constants.Layer.TypeChangingEffects_4; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author spjspj */ public final class ArixmethesSlumberingIsle extends CardImpl { + private static final Condition condition + = new SourceHasCounterCondition(CounterType.SLUMBER, 1, Integer.MAX_VALUE); + public ArixmethesSlumberingIsle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); @@ -46,15 +41,18 @@ public final class ArixmethesSlumberingIsle extends CardImpl { this.toughness = new MageInt(12); // Arixmethes, Slumbering Isle enters the battlefield tapped with five slumber counters on it. - this.addAbility(new EntersBattlefieldTappedAbility()); - Effect effect = new AddCountersSourceEffect(CounterType.SLUMBER.createInstance(5)); - this.addAbility(new EntersBattlefieldAbility(effect, "with five slumber counters")); + Ability ability = new EntersBattlefieldAbility( + new TapSourceEffect(true), false, null, + "{this} enters the battlefield tapped with five slumber counters on it", null + ); + ability.addEffect(new AddCountersSourceEffect(CounterType.SLUMBER.createInstance(5))); + this.addAbility(ability); // As long as Arixmethes, Slumbering Isle has a slumber counter on it, it's a land. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new ArixmethesIsLandEffect(), - new SourceHasCounterCondition(CounterType.SLUMBER, 1, Integer.MAX_VALUE), - "As long as {this} has a slumber counter on it, it's a land"))); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new ArixmethesIsLandEffect(), condition, + "As long as {this} has a slumber counter on it, it's a land" + ))); // Whenever you cast a spell, you may remove a slumber counter from Arixmethes. this.addAbility(new SpellCastControllerTriggeredAbility(new RemoveCounterSourceEffect(CounterType.SLUMBER.createInstance(1)), true)); @@ -75,44 +73,28 @@ public final class ArixmethesSlumberingIsle extends CardImpl { class ArixmethesIsLandEffect extends ContinuousEffectImpl { - public ArixmethesIsLandEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); - this.staticText = "As long as {this} has a slumber counter on it, it's a land"; + ArixmethesIsLandEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); } - public ArixmethesIsLandEffect(final ArixmethesIsLandEffect effect) { + private ArixmethesIsLandEffect(final ArixmethesIsLandEffect effect) { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public ArixmethesIsLandEffect copy() { return new ArixmethesIsLandEffect(this); } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); - - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - permanent.getCardType().clear(); - permanent.addCardType(CardType.LAND); - permanent.getSubtype(game).clear(); - break; - } - return true; + if (permanent == null) { + return false; } - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; + permanent.getCardType().clear(); + permanent.addCardType(CardType.LAND); + permanent.removeAllSubTypes(game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/a/ArmixFiligreeThrasher.java b/Mage.Sets/src/mage/cards/a/ArmixFiligreeThrasher.java new file mode 100644 index 00000000000..84cc6a13582 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArmixFiligreeThrasher.java @@ -0,0 +1,92 @@ +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.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.PartnerAbility; +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.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ArmixFiligreeThrasher extends CardImpl { + + public ArmixFiligreeThrasher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.GOLEM); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // 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, " + + "where X is the number of artifacts you control plus the number of artifact cards in your graveyard" + ); + this.addAbility(new AttacksTriggeredAbility(new DoWhenCostPaid( + ability, new DiscardCardCost(), "Discard a card?" + ), false)); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private ArmixFiligreeThrasher(final ArmixFiligreeThrasher card) { + super(card); + } + + @Override + public ArmixFiligreeThrasher copy() { + 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/AshayaSoulOfTheWild.java b/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java index 9f7c16964ab..07a535b4557 100644 --- a/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java +++ b/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java @@ -90,7 +90,7 @@ class AshayaSoulOfTheWildEffect extends ContinuousEffectImpl { permanent.addCardType(CardType.LAND); } if (!permanent.hasSubtype(SubType.FOREST, game)) { - permanent.getSubtype(game).add(SubType.FOREST); + permanent.addSubType(game, SubType.FOREST); if (!permanent.getAbilities(game).containsClass(GreenManaAbility.class)) { permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); } diff --git a/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java b/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java index bc67e69a248..71a24e4c960 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java +++ b/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java @@ -1,7 +1,6 @@ package mage.cards.a; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; @@ -24,8 +23,9 @@ import mage.target.common.TargetOpponent; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AshiokNightmareWeaver extends CardImpl { @@ -149,7 +149,7 @@ class AshiokNightmareWeaverPutIntoPlayEffect extends OneShotEffect { class AshiokNightmareWeaverAddTypeEffect extends ContinuousEffectImpl { public AshiokNightmareWeaverAddTypeEffect() { - super(Duration.Custom, Outcome.Neutral); + super(Duration.Custom, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); staticText = "That creature is a Nightmare in addition to its other types"; } @@ -162,34 +162,16 @@ class AshiokNightmareWeaverAddTypeEffect extends ContinuousEffectImpl { return new AshiokNightmareWeaverAddTypeEffect(this); } - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent creature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (creature != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - creature.getSubtype(game).add(SubType.NIGHTMARE); - } - break; - } - return true; - } else { - this.used = true; - } - return false; - } - @Override public boolean apply(Game game, Ability source) { - return false; + Permanent creature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); + if (creature == null) { + this.used = true; + return false; + } + creature.addSubType(game, SubType.NIGHTMARE); + return true; } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } - } class AshiokNightmareWeaverExileAllEffect extends OneShotEffect { diff --git a/Mage.Sets/src/mage/cards/a/AssemblyHall.java b/Mage.Sets/src/mage/cards/a/AssemblyHall.java index a495718139e..ad4186b86f7 100644 --- a/Mage.Sets/src/mage/cards/a/AssemblyHall.java +++ b/Mage.Sets/src/mage/cards/a/AssemblyHall.java @@ -1,6 +1,5 @@ package mage.cards.a; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,7 +11,6 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.CardsImpl; -import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterCard; @@ -23,9 +21,11 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class AssemblyHall extends CardImpl { @@ -84,7 +84,7 @@ class AssemblyHallEffect extends OneShotEffect { return false; } controller.revealCards("from hand :" + sourceObject.getName(), new CardsImpl(cardToReveal), game); - String nameToSearch = cardToReveal.isSplitCard() ? ((SplitCard) cardToReveal).getLeftHalfCard().getName() : cardToReveal.getName(); + String nameToSearch = CardUtil.getCardNameForSameNameSearch(cardToReveal); FilterCard filterCard = new FilterCard("card named " + nameToSearch); filterCard.add(new NamePredicate(nameToSearch)); return new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filterCard), true, true).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/a/AuroraPhoenix.java b/Mage.Sets/src/mage/cards/a/AuroraPhoenix.java new file mode 100644 index 00000000000..e7d1e805297 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AuroraPhoenix.java @@ -0,0 +1,57 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.keyword.CascadeAbility; +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.FilterSpell; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AuroraPhoenix extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("spell with cascade"); + + static { + filter.add(new AbilityPredicate(CascadeAbility.class)); + } + + public AuroraPhoenix(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); + + this.subtype.add(SubType.PHOENIX); + this.power = new MageInt(5); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Cascade + this.addAbility(new CascadeAbility()); + + // Whenever you cast a spell with cascade, return Aurora Phoenix from your graveyard to your hand. + this.addAbility(new SpellCastControllerTriggeredAbility( + Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), + filter, false, false + )); + } + + private AuroraPhoenix(final AuroraPhoenix card) { + super(card); + } + + @Override + public AuroraPhoenix copy() { + return new AuroraPhoenix(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AvernaTheChaosBloom.java b/Mage.Sets/src/mage/cards/a/AvernaTheChaosBloom.java new file mode 100644 index 00000000000..c6b21b3b701 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AvernaTheChaosBloom.java @@ -0,0 +1,74 @@ +package mage.cards.a; + +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AvernaTheChaosBloom extends CardImpl { + + public AvernaTheChaosBloom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // As you cascade, you may put a land card from among the exiled cards onto the battlefield tapped. + this.addAbility(new SimpleStaticAbility(new AvernaTheChaosBloomReplacementEffect())); + } + + private AvernaTheChaosBloom(final AvernaTheChaosBloom card) { + super(card); + } + + @Override + public AvernaTheChaosBloom copy() { + return new AvernaTheChaosBloom(this); + } +} + +class AvernaTheChaosBloomReplacementEffect extends ReplacementEffectImpl { + + AvernaTheChaosBloomReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "As you cascade, you may put a land card from among the exiled cards onto the battlefield tapped."; + } + + private AvernaTheChaosBloomReplacementEffect(final AvernaTheChaosBloomReplacementEffect effect) { + super(effect); + } + + @Override + public AvernaTheChaosBloomReplacementEffect copy() { + return new AvernaTheChaosBloomReplacementEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CASCADE_LAND; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(source.getControllerId()); + } + + @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/a/AvianChangeling.java b/Mage.Sets/src/mage/cards/a/AvianChangeling.java index f64019816d6..3cd09cacaf5 100644 --- a/Mage.Sets/src/mage/cards/a/AvianChangeling.java +++ b/Mage.Sets/src/mage/cards/a/AvianChangeling.java @@ -22,6 +22,7 @@ public final class AvianChangeling extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/b/BalaGedRecovery.java b/Mage.Sets/src/mage/cards/b/BalaGedRecovery.java index bfabd1b213f..a91b2ee0569 100644 --- a/Mage.Sets/src/mage/cards/b/BalaGedRecovery.java +++ b/Mage.Sets/src/mage/cards/b/BalaGedRecovery.java @@ -1,27 +1,44 @@ package mage.cards.b; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; -import mage.cards.CardImpl; +import mage.abilities.mana.GreenManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class BalaGedRecovery extends CardImpl { +public final class BalaGedRecovery extends ModalDoubleFacesCard { public BalaGedRecovery(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{2}{G}", + "Bala Ged Sanctuary", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.b.BalaGedSanctuary.class; + // 1. + // Bala Ged Recovery + // Sorcery // Return target card from your graveyard to your hand. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard()); + this.getLeftHalfCard().getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetCardInYourGraveyard()); + + // 2. + // Bala Ged Sanctuary + // Land + + // Bala Ged Sanctuary enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {G}. + this.getRightHalfCard().addAbility(new GreenManaAbility()); } private BalaGedRecovery(final BalaGedRecovery card) { diff --git a/Mage.Sets/src/mage/cards/b/BalaGedSanctuary.java b/Mage.Sets/src/mage/cards/b/BalaGedSanctuary.java deleted file mode 100644 index 097234668c2..00000000000 --- a/Mage.Sets/src/mage/cards/b/BalaGedSanctuary.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.b; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class BalaGedSanctuary extends CardImpl { - - public BalaGedSanctuary(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Bala Ged Sanctuary enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {G}. - this.addAbility(new GreenManaAbility()); - } - - private BalaGedSanctuary(final BalaGedSanctuary card) { - super(card); - } - - @Override - public BalaGedSanctuary copy() { - return new BalaGedSanctuary(this); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BeyeenCoast.java b/Mage.Sets/src/mage/cards/b/BeyeenCoast.java deleted file mode 100644 index bc50be824e9..00000000000 --- a/Mage.Sets/src/mage/cards/b/BeyeenCoast.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.b; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class BeyeenCoast extends CardImpl { - - public BeyeenCoast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Beyeen Coast enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {U}. - this.addAbility(new BlueManaAbility()); - } - - private BeyeenCoast(final BeyeenCoast card) { - super(card); - } - - @Override - public BeyeenCoast copy() { - return new BeyeenCoast(this); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BeyeenVeil.java b/Mage.Sets/src/mage/cards/b/BeyeenVeil.java index a9c006247c1..e75bf2cb55c 100644 --- a/Mage.Sets/src/mage/cards/b/BeyeenVeil.java +++ b/Mage.Sets/src/mage/cards/b/BeyeenVeil.java @@ -1,26 +1,43 @@ package mage.cards.b; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.continuous.BoostOpponentsEffect; -import mage.cards.CardImpl; +import mage.abilities.mana.BlueManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class BeyeenVeil extends CardImpl { +public final class BeyeenVeil extends ModalDoubleFacesCard { public BeyeenVeil(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{1}{U}", + "Beyeen Coast", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.b.BeyeenCoast.class; + // 1. + // Beyeen Veil + // Instant // Creatures your opponents control get -2/-0 until end of turn. - this.getSpellAbility().addEffect(new BoostOpponentsEffect(-2, 0, Duration.EndOfTurn)); + this.getLeftHalfCard().getSpellAbility().addEffect(new BoostOpponentsEffect(-2, 0, Duration.EndOfTurn)); + + // 2. + // Beyeen Coast + // Land + + // Beyeen Coast enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {U}. + this.getRightHalfCard().addAbility(new BlueManaAbility()); } private BeyeenVeil(final BeyeenVeil card) { diff --git a/Mage.Sets/src/mage/cards/b/BlackbloomBog.java b/Mage.Sets/src/mage/cards/b/BlackbloomBog.java deleted file mode 100644 index 613ee77470c..00000000000 --- a/Mage.Sets/src/mage/cards/b/BlackbloomBog.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.b; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.BlackManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class BlackbloomBog extends CardImpl { - - public BlackbloomBog(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Blackbloom Bog enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {B}. - this.addAbility(new BlackManaAbility()); - } - - private BlackbloomBog(final BlackbloomBog card) { - super(card); - } - - @Override - public BlackbloomBog copy() { - return new BlackbloomBog(this); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BlackbloomRogue.java b/Mage.Sets/src/mage/cards/b/BlackbloomRogue.java index 21f277ffeb8..6eaf184addf 100644 --- a/Mage.Sets/src/mage/cards/b/BlackbloomRogue.java +++ b/Mage.Sets/src/mage/cards/b/BlackbloomRogue.java @@ -1,13 +1,15 @@ package mage.cards.b; import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CardsInOpponentGraveyardCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.MenaceAbility; -import mage.cards.CardImpl; +import mage.abilities.mana.BlackManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; @@ -15,30 +17,40 @@ import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class BlackbloomRogue extends CardImpl { +public final class BlackbloomRogue extends ModalDoubleFacesCard { public BlackbloomRogue(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.ROGUE}, "{2}{B}", + "Blackbloom Bog", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.ROGUE); - this.power = new MageInt(2); - this.toughness = new MageInt(3); - - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.b.BlackbloomBog.class; + // 1. + // Blackbloom Rogue {2}{B} + // Creature — Human Rogue + this.getLeftHalfCard().setPT(new MageInt(2), new MageInt(3)); // Menace - this.addAbility(new MenaceAbility()); + this.getLeftHalfCard().addAbility(new MenaceAbility()); // Blackbloom Rogue gets +3/+0 as long as an opponent has eight or more cards in their graveyard. - this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + this.getLeftHalfCard().addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostSourceEffect(3, 0, Duration.WhileOnBattlefield), CardsInOpponentGraveyardCondition.EIGHT, "{this} gets +3/+0 as long as " + "an opponent has eight or more cards in their graveyard" )).addHint(CardsInOpponentGraveyardCondition.EIGHT.getHint())); + + // 2. + // Blackbloom Bog + // Land + + // Blackbloom Bog enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {B}. + this.getRightHalfCard().addAbility(new BlackManaAbility()); } private BlackbloomRogue(final BlackbloomRogue card) { diff --git a/Mage.Sets/src/mage/cards/b/BladesOfVelisVel.java b/Mage.Sets/src/mage/cards/b/BladesOfVelisVel.java index 997bc35794b..4829fbc5912 100644 --- a/Mage.Sets/src/mage/cards/b/BladesOfVelisVel.java +++ b/Mage.Sets/src/mage/cards/b/BladesOfVelisVel.java @@ -1,10 +1,7 @@ - package mage.cards.b; -import java.util.UUID; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainAllCreatureTypesTargetEffect; import mage.abilities.keyword.ChangelingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -13,26 +10,27 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class BladesOfVelisVel extends CardImpl { public BladesOfVelisVel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.INSTANT},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.INSTANT}, "{1}{R}"); this.subtype.add(SubType.SHAPESHIFTER); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); - + // Up to two target creatures each get +2/+0 and gain all creature types until end of turn. - Effect effect = new BoostTargetEffect(2,0, Duration.EndOfTurn); - effect.setText("Up to two target creatures each get +2/+0"); - this.getSpellAbility().addEffect(effect); - effect = new GainAbilityTargetEffect(ChangelingAbility.getInstance(), Duration.EndOfTurn, "and gain all creature types until end of turn"); - this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(0,2)); + this.getSpellAbility().addEffect(new BoostTargetEffect(2, 0, Duration.EndOfTurn) + .setText("Up to two target creatures each get +2/+0")); + this.getSpellAbility().addEffect(new GainAllCreatureTypesTargetEffect(Duration.EndOfTurn) + .setText("and gain all creature types until end of turn")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); } public BladesOfVelisVel(final BladesOfVelisVel card) { diff --git a/Mage.Sets/src/mage/cards/b/BloodMoon.java b/Mage.Sets/src/mage/cards/b/BloodMoon.java index 1cd11c4e497..f36a5d1d3e7 100644 --- a/Mage.Sets/src/mage/cards/b/BloodMoon.java +++ b/Mage.Sets/src/mage/cards/b/BloodMoon.java @@ -72,7 +72,7 @@ public final class BloodMoon extends CardImpl { // So the ability removing has to be done before Layer 6 // Lands have their mana ability intrinsically, so that is added in layer 4 land.getSubtype(game).removeAll(SubType.getLandTypes()); - land.getSubtype(game).add(SubType.MOUNTAIN); + land.addSubType(game, SubType.MOUNTAIN); land.removeAllAbilities(source.getSourceId(), game); land.addAbility(new RedManaAbility(), source.getSourceId(), game); break; diff --git a/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java b/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java index 71166b33c88..ce9e0eb92bc 100644 --- a/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java +++ b/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java @@ -85,7 +85,7 @@ class BludgeonBrawlAddSubtypeEffect extends ContinuousEffectImpl { List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game); for (Permanent permanent : permanents) { if (permanent != null) { - permanent.getSubtype(game).add(SubType.EQUIPMENT); + permanent.addSubType(game, SubType.EQUIPMENT); affectedPermanents.add(permanent.getId()); } } diff --git a/Mage.Sets/src/mage/cards/b/BolassCitadel.java b/Mage.Sets/src/mage/cards/b/BolassCitadel.java index 03c06175b47..a3f56f3094f 100644 --- a/Mage.Sets/src/mage/cards/b/BolassCitadel.java +++ b/Mage.Sets/src/mage/cards/b/BolassCitadel.java @@ -105,22 +105,39 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl { if (topCard == null || !topCard.getId().equals(objectIdToCast)) { return false; } - if (!topCard.isLand()) { - if (topCard instanceof SplitCard) { - SplitCardHalf leftCard = ((SplitCard) topCard).getLeftHalfCard(); - PayLifeCost lifeCost = new PayLifeCost(leftCard.getSpellAbility().getManaCosts().convertedManaCost()); - Costs leftCosts = new CostsImpl(); - leftCosts.add(lifeCost); - leftCosts.addAll(leftCard.getSpellAbility().getCosts()); - player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCosts); - SplitCardHalf rightCard = ((SplitCard) topCard).getRightHalfCard(); - lifeCost = new PayLifeCost(rightCard.getSpellAbility().getManaCosts().convertedManaCost()); - Costs rightCosts = new CostsImpl(); - rightCosts.add(lifeCost); - rightCosts.addAll(rightCard.getSpellAbility().getCosts()); - player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCosts); + if (topCard instanceof SplitCard || topCard instanceof ModalDoubleFacesCard) { + // double faces cards + Card card1; + Card card2; + if (topCard instanceof SplitCard) { + card1 = ((SplitCard) topCard).getLeftHalfCard(); + card2 = ((SplitCard) topCard).getRightHalfCard(); } else { + card1 = ((ModalDoubleFacesCard) topCard).getLeftHalfCard(); + card2 = ((ModalDoubleFacesCard) topCard).getRightHalfCard(); + } + + // left + if (!card1.isLand()) { + PayLifeCost lifeCost = new PayLifeCost(card1.getSpellAbility().getManaCosts().convertedManaCost()); + Costs newCosts = new CostsImpl(); + newCosts.add(lifeCost); + newCosts.addAll(card1.getSpellAbility().getCosts()); + player.setCastSourceIdWithAlternateMana(card1.getId(), null, newCosts); + } + + // right + if (!card2.isLand()) { + PayLifeCost lifeCost = new PayLifeCost(card2.getSpellAbility().getManaCosts().convertedManaCost()); + Costs newCosts = new CostsImpl(); + newCosts.add(lifeCost); + newCosts.addAll(card2.getSpellAbility().getCosts()); + player.setCastSourceIdWithAlternateMana(card2.getId(), null, newCosts); + } + } else { + // other single face cards + if (!topCard.isLand()) { if (affectedAbility == null) { affectedAbility = topCard.getSpellAbility(); } else { @@ -134,7 +151,6 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl { } } return true; - } return false; } diff --git a/Mage.Sets/src/mage/cards/b/BoulderloftPathway.java b/Mage.Sets/src/mage/cards/b/BoulderloftPathway.java deleted file mode 100644 index 57fed2f4e84..00000000000 --- a/Mage.Sets/src/mage/cards/b/BoulderloftPathway.java +++ /dev/null @@ -1,33 +0,0 @@ -package mage.cards.b; - -import mage.abilities.mana.WhiteManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class BoulderloftPathway extends CardImpl { - - public BoulderloftPathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // {T}: Add {W}. - this.addAbility(new WhiteManaAbility()); - } - - private BoulderloftPathway(final BoulderloftPathway card) { - super(card); - } - - @Override - public BoulderloftPathway copy() { - return new BoulderloftPathway(this); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BranchloftPathway.java b/Mage.Sets/src/mage/cards/b/BranchloftPathway.java index db55af7df1c..1c07c4ce651 100644 --- a/Mage.Sets/src/mage/cards/b/BranchloftPathway.java +++ b/Mage.Sets/src/mage/cards/b/BranchloftPathway.java @@ -1,25 +1,38 @@ package mage.cards.b; import mage.abilities.mana.GreenManaAbility; -import mage.cards.CardImpl; +import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class BranchloftPathway extends CardImpl { +public final class BranchloftPathway extends ModalDoubleFacesCard { public BranchloftPathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + super(ownerId, setInfo, + new CardType[]{CardType.LAND}, new SubType[]{}, "", + "Boulderloft Pathway", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.b.BoulderloftPathway.class; + // 1. + // Branchloft Pathway + // Land // {T}: Add {G}. - this.addAbility(new GreenManaAbility()); + this.getLeftHalfCard().addAbility(new GreenManaAbility()); + + // 2. + // Boulderloft Pathway + // Land + + // {T}: Add {W}. + this.getRightHalfCard().addAbility(new WhiteManaAbility()); } private BranchloftPathway(final BranchloftPathway card) { diff --git a/Mage.Sets/src/mage/cards/b/BrightclimbPathway.java b/Mage.Sets/src/mage/cards/b/BrightclimbPathway.java index ddda99ca753..177ace5d6c8 100644 --- a/Mage.Sets/src/mage/cards/b/BrightclimbPathway.java +++ b/Mage.Sets/src/mage/cards/b/BrightclimbPathway.java @@ -1,25 +1,38 @@ package mage.cards.b; +import mage.abilities.mana.BlackManaAbility; import mage.abilities.mana.WhiteManaAbility; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class BrightclimbPathway extends CardImpl { +public final class BrightclimbPathway extends ModalDoubleFacesCard { public BrightclimbPathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + super(ownerId, setInfo, + new CardType[]{CardType.LAND}, new SubType[]{}, "", + "Grimclimb Pathway", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.g.GrimclimbPathway.class; + // 1. + // Brightclimb Pathway + // Land // {T}: Add {W}. - this.addAbility(new WhiteManaAbility()); + this.getLeftHalfCard().addAbility(new WhiteManaAbility()); + + // 2. + // Grimclimb Pathway + // Land + + // {T}: Add {B}. + this.getRightHalfCard().addAbility(new BlackManaAbility()); } private BrightclimbPathway(final BrightclimbPathway card) { diff --git a/Mage.Sets/src/mage/cards/b/BrinelinTheMoonKraken.java b/Mage.Sets/src/mage/cards/b/BrinelinTheMoonKraken.java new file mode 100644 index 00000000000..bb99efe6294 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BrinelinTheMoonKraken.java @@ -0,0 +1,94 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +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.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BrinelinTheMoonKraken extends CardImpl { + + public BrinelinTheMoonKraken(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.KRAKEN); + this.power = new MageInt(6); + this.toughness = new MageInt(8); + + // When Brinelin, the Moon Kraken enters the battlefield or whenever you cast a spell with converted mana cost 6 or greater, you may return target nonland permanent to its owner's hand. + this.addAbility(new BrinelinTheMoonKrakenTriggeredAbility()); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private BrinelinTheMoonKraken(final BrinelinTheMoonKraken card) { + super(card); + } + + @Override + public BrinelinTheMoonKraken copy() { + return new BrinelinTheMoonKraken(this); + } +} + +class BrinelinTheMoonKrakenTriggeredAbility extends TriggeredAbilityImpl { + + BrinelinTheMoonKrakenTriggeredAbility() { + super(Zone.ALL, new ReturnToHandTargetEffect(), true); + this.addTarget(new TargetNonlandPermanent()); + } + + private BrinelinTheMoonKrakenTriggeredAbility(final BrinelinTheMoonKrakenTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST + || event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + switch (event.getType()) { + case SPELL_CAST: + if (!event.getPlayerId().equals(this.getControllerId()) + || game.getPermanent(getSourceId()) == null) { + return false; + } + Spell spell = game.getSpellOrLKIStack(event.getTargetId()); + return spell != null && spell.getConvertedManaCost() >= 6; + case ENTERS_THE_BATTLEFIELD: + return event.getTargetId().equals(getSourceId()); + default: + return false; + } + } + + @Override + public String getRule() { + return "When {this} enters the battlefield or whenever you cast a spell with converted mana cost " + + "6 or greater, you may return target nonland permanent to its owner's hand."; + } + + @Override + public BrinelinTheMoonKrakenTriggeredAbility copy() { + return new BrinelinTheMoonKrakenTriggeredAbility(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BronzehideLion.java b/Mage.Sets/src/mage/cards/b/BronzehideLion.java index 98dd760b607..810d851dc74 100644 --- a/Mage.Sets/src/mage/cards/b/BronzehideLion.java +++ b/Mage.Sets/src/mage/cards/b/BronzehideLion.java @@ -144,8 +144,8 @@ class BronzehideLionContinuousEffect extends ContinuousEffectImpl { case TypeChangingEffects_4: lion.getCardType().clear(); lion.addCardType(CardType.ENCHANTMENT); - lion.getSubtype(game).clear(); - lion.getSubtype(game).add(SubType.AURA); + lion.removeAllSubTypes(game); + lion.addSubType(game, SubType.AURA); break; case AbilityAddingRemovingEffects_6: List toRemove = new ArrayList<>(); diff --git a/Mage.Sets/src/mage/cards/c/CairnWanderer.java b/Mage.Sets/src/mage/cards/c/CairnWanderer.java index 335f1f37ea0..451bc7069d1 100644 --- a/Mage.Sets/src/mage/cards/c/CairnWanderer.java +++ b/Mage.Sets/src/mage/cards/c/CairnWanderer.java @@ -30,6 +30,7 @@ public final class CairnWanderer extends CardImpl { this.toughness = new MageInt(4); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // As long as a creature card with flying is in a graveyard, Cairn Wanderer has flying. The same is true for fear, first strike, double strike, deathtouch, haste, landwalk, lifelink, protection, reach, trample, shroud, and vigilance. diff --git a/Mage.Sets/src/mage/cards/c/CallToTheKindred.java b/Mage.Sets/src/mage/cards/c/CallToTheKindred.java index 09694cfa7b9..d55a6a13496 100644 --- a/Mage.Sets/src/mage/cards/c/CallToTheKindred.java +++ b/Mage.Sets/src/mage/cards/c/CallToTheKindred.java @@ -1,12 +1,9 @@ - package mage.cards.c; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.keyword.ChangelingAbility; import mage.abilities.keyword.EnchantAbility; import mage.cards.*; import mage.constants.CardType; @@ -15,27 +12,24 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.Predicate; -import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPermanent; +import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCreaturePermanent; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; /** - * * @author North */ public final class CallToTheKindred extends CardImpl { public CallToTheKindred(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 @@ -43,6 +37,7 @@ public final class CallToTheKindred extends CardImpl { this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + // At the beginning of your upkeep, you may look at the top five cards of your library. // If you do, you may put a creature card that shares a creature type with enchanted creature from among them onto the battlefield, // then you put the rest of those cards on the bottom of your library in any order. @@ -61,12 +56,14 @@ public final class CallToTheKindred extends CardImpl { class CallToTheKindredEffect extends OneShotEffect { - public CallToTheKindredEffect() { + CallToTheKindredEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "look at the top five cards of your library. If you do, you may put a creature card that shares a creature type with enchanted creature from among them onto the battlefield, then you put the rest of those cards on the bottom of your library in any order"; + this.staticText = "look at the top five cards of your library. If you do, you may put a creature card " + + "that shares a creature type with enchanted creature from among them onto the battlefield, " + + "then you put the rest of those cards on the bottom of your library in any order"; } - public CallToTheKindredEffect(final CallToTheKindredEffect effect) { + private CallToTheKindredEffect(final CallToTheKindredEffect effect) { super(effect); } @@ -90,36 +87,33 @@ class CallToTheKindredEffect extends OneShotEffect { } Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 5)); - controller.lookAtCards(enchantment.getIdName(), cards, game); - FilterCreatureCard filter = new FilterCreatureCard(); + filter.add(new CallToTheKindredPredicate(creature)); - if (!creature.getAbilities().contains(ChangelingAbility.getInstance())) { - StringBuilder sb = new StringBuilder("creature card with at least one subtype from: "); - List> subtypes = new ArrayList<>(); - for (SubType subtype : creature.getSubtype(game)) { - subtypes.add(subtype.getPredicate()); - sb.append(subtype).append(", "); - } - filter.add(Predicates.or(subtypes)); - sb.delete(sb.length() - 2, sb.length()); - filter.setMessage(sb.toString()); - } else { - filter.setMessage("creature card that shares a creature type with enchanted creature"); - } - - if (cards.count(filter, game) > 0 && controller.chooseUse(Outcome.DrawCard, "Do you wish to put a creature card onto the battlefield?", source, game)) { - TargetCard target = new TargetCard(Zone.LIBRARY, filter); - - if (controller.choose(Outcome.PutCreatureInPlay, cards, target, game)) { - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - cards.remove(card); - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - } + if (cards.count(filter, game) > 0) { + TargetCard target = new TargetCardInLibrary(0, 1, filter); + controller.choose(Outcome.PutCreatureInPlay, cards, target, game); + Card card = cards.get(target.getFirstTarget(), game); + if (card != null) { + cards.remove(card); + controller.moveCards(card, Zone.BATTLEFIELD, source, game); } } controller.putCardsOnBottomOfLibrary(cards, game, source, true); return true; } } + +class CallToTheKindredPredicate implements Predicate { + + private final Permanent permanent; + + CallToTheKindredPredicate(Permanent permanent) { + this.permanent = permanent; + } + + @Override + public boolean apply(Card input, Game game) { + return permanent != null && input != null && permanent.shareCreatureTypes(input, game); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CaptivatingVampire.java b/Mage.Sets/src/mage/cards/c/CaptivatingVampire.java index a5b8c7091b0..1edbe8ce50c 100644 --- a/Mage.Sets/src/mage/cards/c/CaptivatingVampire.java +++ b/Mage.Sets/src/mage/cards/c/CaptivatingVampire.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -21,8 +20,9 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public final class CaptivatingVampire extends CardImpl { @@ -37,7 +37,7 @@ public final class CaptivatingVampire extends CardImpl { } public CaptivatingVampire(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.VAMPIRE); this.power = new MageInt(2); @@ -90,11 +90,7 @@ class CaptivatingVampireEffect extends ContinuousEffectImpl { } break; case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (!permanent.hasSubtype(SubType.VAMPIRE, game)) { - permanent.getSubtype(game).add(SubType.VAMPIRE); - } - } + permanent.addSubType(game, SubType.VAMPIRE); break; } return true; diff --git a/Mage.Sets/src/mage/cards/c/CelestialDawn.java b/Mage.Sets/src/mage/cards/c/CelestialDawn.java index 4501d984e55..684e77ff93e 100644 --- a/Mage.Sets/src/mage/cards/c/CelestialDawn.java +++ b/Mage.Sets/src/mage/cards/c/CelestialDawn.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; @@ -18,14 +16,15 @@ import mage.filter.common.FilterLandPermanent; import mage.filter.common.FilterNonlandPermanent; import mage.game.Game; import mage.game.command.CommandObject; +import mage.game.command.Commander; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.ManaPoolItem; import mage.players.Player; -import mage.game.command.Commander; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class CelestialDawn extends CardImpl { @@ -89,8 +88,8 @@ class CelestialDawnToPlainsEffect extends ContinuousEffectImpl { land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); break; case TypeChangingEffects_4: - land.getSubtype(game).clear(); - land.getSubtype(game).add(SubType.PLAINS); + land.getSubtype(game).removeAll(SubType.getLandTypes()); + land.addSubType(game, SubType.PLAINS); break; } } diff --git a/Mage.Sets/src/mage/cards/c/ChameleonColossus.java b/Mage.Sets/src/mage/cards/c/ChameleonColossus.java index 283e0dce680..d2a7d205bfd 100644 --- a/Mage.Sets/src/mage/cards/c/ChameleonColossus.java +++ b/Mage.Sets/src/mage/cards/c/ChameleonColossus.java @@ -31,6 +31,7 @@ public final class ChameleonColossus extends CardImpl { this.toughness = new MageInt(4); // Changeling (This card is every creature type at all times.) + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Protection from black diff --git a/Mage.Sets/src/mage/cards/c/ChangelingBerserker.java b/Mage.Sets/src/mage/cards/c/ChangelingBerserker.java index b6ad46c5c2d..d718b8fe43a 100644 --- a/Mage.Sets/src/mage/cards/c/ChangelingBerserker.java +++ b/Mage.Sets/src/mage/cards/c/ChangelingBerserker.java @@ -24,6 +24,7 @@ public final class ChangelingBerserker extends CardImpl { this.toughness = new MageInt(3); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Haste diff --git a/Mage.Sets/src/mage/cards/c/ChangelingHero.java b/Mage.Sets/src/mage/cards/c/ChangelingHero.java index f7671612239..5265bce967c 100644 --- a/Mage.Sets/src/mage/cards/c/ChangelingHero.java +++ b/Mage.Sets/src/mage/cards/c/ChangelingHero.java @@ -24,6 +24,7 @@ public final class ChangelingHero extends CardImpl { this.toughness = new MageInt(4); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Champion a creature diff --git a/Mage.Sets/src/mage/cards/c/ChangelingOutcast.java b/Mage.Sets/src/mage/cards/c/ChangelingOutcast.java index fbbedddbb04..2557d03a256 100644 --- a/Mage.Sets/src/mage/cards/c/ChangelingOutcast.java +++ b/Mage.Sets/src/mage/cards/c/ChangelingOutcast.java @@ -24,6 +24,7 @@ public final class ChangelingOutcast extends CardImpl { this.toughness = new MageInt(1); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Changeling Outcast can't block and can't be blocked. diff --git a/Mage.Sets/src/mage/cards/c/ChangelingSentinel.java b/Mage.Sets/src/mage/cards/c/ChangelingSentinel.java index 08bd56e16fd..8e532ccce52 100644 --- a/Mage.Sets/src/mage/cards/c/ChangelingSentinel.java +++ b/Mage.Sets/src/mage/cards/c/ChangelingSentinel.java @@ -22,6 +22,7 @@ public final class ChangelingSentinel extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); this.addAbility(VigilanceAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/c/ChangelingTitan.java b/Mage.Sets/src/mage/cards/c/ChangelingTitan.java index 76d89150278..3701c9ae751 100644 --- a/Mage.Sets/src/mage/cards/c/ChangelingTitan.java +++ b/Mage.Sets/src/mage/cards/c/ChangelingTitan.java @@ -23,6 +23,7 @@ public final class ChangelingTitan extends CardImpl { this.toughness = new MageInt(7); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Champion a creature diff --git a/Mage.Sets/src/mage/cards/c/ChimericCoils.java b/Mage.Sets/src/mage/cards/c/ChimericCoils.java index a553b4c1ae8..a1473cf34b0 100644 --- a/Mage.Sets/src/mage/cards/c/ChimericCoils.java +++ b/Mage.Sets/src/mage/cards/c/ChimericCoils.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -15,17 +13,18 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author Plopman */ public final class ChimericCoils extends CardImpl { public ChimericCoils(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); // {X}{1}: Chimeric Coils becomes an X/X Construct artifact creature. Sacrifice it at the beginning of thhe next end step. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChimericCoilsEffect(), new ManaCostsImpl("{X}{1}")); + Ability ability = new SimpleActivatedAbility(new ChimericCoilsEffect(), new ManaCostsImpl("{X}{1}")); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect()))); this.addAbility(ability); } @@ -43,8 +42,8 @@ public final class ChimericCoils extends CardImpl { class ChimericCoilsEffect extends ContinuousEffectImpl { public ChimericCoilsEffect() { - super(Duration.EndOfTurn, Outcome.BecomeCreature); - setText(); + super(Duration.Custom, Outcome.BecomeCreature); + staticText = "{this} becomes an X/X Construct artifact creature"; } public ChimericCoilsEffect(final ChimericCoilsEffect effect) { @@ -59,24 +58,28 @@ class ChimericCoilsEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.addCardType(CardType.CREATURE); - permanent.getSubtype(game).add(SubType.CONSTRUCT); - } - break; - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - int xValue = source.getManaCostsToPay().getX(); - permanent.getPower().setValue(xValue); - permanent.getToughness().setValue(xValue); - } - } - return true; + if (permanent == null) { + return false; } - return false; + switch (layer) { + case TypeChangingEffects_4: + if (!permanent.isArtifact()) { + permanent.addCardType(CardType.ARTIFACT); + } + if (!permanent.isCreature()) { + permanent.addCardType(CardType.CREATURE); + } + permanent.removeAllCreatureTypes(game); + permanent.addSubType(game, SubType.CONSTRUCT); + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + int xValue = source.getManaCostsToPay().getX(); + permanent.getPower().setValue(xValue); + permanent.getToughness().setValue(xValue); + } + } + return true; } @Override @@ -84,10 +87,6 @@ class ChimericCoilsEffect extends ContinuousEffectImpl { return false; } - private void setText() { - staticText = duration.toString() + " {this} becomes an X/X Construct artifact creature"; - } - @Override public boolean hasLayer(Layer layer) { return layer == Layer.PTChangingEffects_7 || layer == Layer.TypeChangingEffects_4; diff --git a/Mage.Sets/src/mage/cards/c/ChimericStaff.java b/Mage.Sets/src/mage/cards/c/ChimericStaff.java index b7046aeb8ce..5be3990d587 100644 --- a/Mage.Sets/src/mage/cards/c/ChimericStaff.java +++ b/Mage.Sets/src/mage/cards/c/ChimericStaff.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -12,17 +10,18 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author Backfir3 */ public final class ChimericStaff extends CardImpl { public ChimericStaff(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // {X}: Chimeric Staff becomes an X/X Construct artifact creature until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChimericStaffEffect(), new ManaCostsImpl("{X}"))); + this.addAbility(new SimpleActivatedAbility(new ChimericStaffEffect(), new ManaCostsImpl("{X}"))); } public ChimericStaff(final ChimericStaff card) { @@ -39,7 +38,7 @@ class ChimericStaffEffect extends ContinuousEffectImpl { public ChimericStaffEffect() { super(Duration.EndOfTurn, Outcome.BecomeCreature); - setText(); + staticText = "{this} becomes an X/X Construct artifact creature until end of turn"; } public ChimericStaffEffect(final ChimericStaffEffect effect) { @@ -54,26 +53,28 @@ class ChimericStaffEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.addCardType(CardType.CREATURE); - permanent.getSubtype(game).add(SubType.CONSTRUCT); - } - break; - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - int xValue = source.getManaCostsToPay().getX(); - if (xValue != 0) { - permanent.getPower().setValue(xValue); - permanent.getToughness().setValue(xValue); - } - } - } - return true; + if (permanent == null) { + return false; } - return false; + switch (layer) { + case TypeChangingEffects_4: + if (!permanent.isArtifact()) { + permanent.addCardType(CardType.ARTIFACT); + } + if (!permanent.isCreature()) { + permanent.addCardType(CardType.CREATURE); + } + permanent.removeAllCreatureTypes(game); + permanent.addSubType(game, SubType.CONSTRUCT); + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + int xValue = source.getManaCostsToPay().getX(); + permanent.getPower().setValue(xValue); + permanent.getToughness().setValue(xValue); + } + } + return true; } @Override @@ -81,10 +82,6 @@ class ChimericStaffEffect extends ContinuousEffectImpl { return false; } - private void setText() { - staticText = duration.toString() + " {this} becomes an X/X Construct artifact creature"; - } - @Override public boolean hasLayer(Layer layer) { return layer == Layer.PTChangingEffects_7 || layer == Layer.TypeChangingEffects_4; diff --git a/Mage.Sets/src/mage/cards/c/ClearwaterPathway.java b/Mage.Sets/src/mage/cards/c/ClearwaterPathway.java index 086bf0a5047..7a2960bbb46 100644 --- a/Mage.Sets/src/mage/cards/c/ClearwaterPathway.java +++ b/Mage.Sets/src/mage/cards/c/ClearwaterPathway.java @@ -1,25 +1,38 @@ package mage.cards.c; +import mage.abilities.mana.BlackManaAbility; import mage.abilities.mana.BlueManaAbility; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class ClearwaterPathway extends CardImpl { +public final class ClearwaterPathway extends ModalDoubleFacesCard { public ClearwaterPathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + super(ownerId, setInfo, + new CardType[]{CardType.LAND}, new SubType[]{}, "", + "Murkwater Pathway", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.m.MurkwaterPathway.class; + // 1. + // Clearwater Pathway + // Land // {T}: Add {U}. - this.addAbility(new BlueManaAbility()); + this.getLeftHalfCard().addAbility(new BlueManaAbility()); + + // 2. + // Murkwater Pathway + // Land + + // {T}: Add {B}. + this.getRightHalfCard().addAbility(new BlackManaAbility()); } private ClearwaterPathway(final ClearwaterPathway card) { diff --git a/Mage.Sets/src/mage/cards/c/CoastlineMarauders.java b/Mage.Sets/src/mage/cards/c/CoastlineMarauders.java new file mode 100644 index 00000000000..b2a88f8ad33 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CoastlineMarauders.java @@ -0,0 +1,64 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +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.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.EncoreAbility; +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.FilterLandPermanent; +import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CoastlineMarauders extends CardImpl { + + private static final FilterPermanent filter = new FilterLandPermanent(); + + static { + filter.add(DefendingPlayerControlsPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + + public CoastlineMarauders(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.PIRATE); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever Coastline Marauders attacks, it gets +1/+0 until end of turn for each land defending player controls. + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect( + xValue, StaticValue.get(0), Duration.EndOfTurn, true + ).setText("it gets +1/+0 until end of turn for each land defending player controls"), false)); + + // Encore {4}{R}{R} + this.addAbility(new EncoreAbility(new ManaCostsImpl<>("{4}{R}{R}"))); + } + + private CoastlineMarauders(final CoastlineMarauders card) { + super(card); + } + + @Override + public CoastlineMarauders copy() { + return new CoastlineMarauders(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CoatOfArms.java b/Mage.Sets/src/mage/cards/c/CoatOfArms.java index 0a33e6ab696..f6801f87cf6 100644 --- a/Mage.Sets/src/mage/cards/c/CoatOfArms.java +++ b/Mage.Sets/src/mage/cards/c/CoatOfArms.java @@ -1,28 +1,26 @@ package mage.cards.c; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.keyword.ChangelingAbility; 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.util.SubTypeList; + +import java.util.List; +import java.util.UUID; /** - * * @author North */ public final class CoatOfArms extends CardImpl { public CoatOfArms(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // Each creature gets +1/+1 for each other creature on the battlefield that shares at least one creature type with it. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CoatOfArmsEffect())); @@ -40,7 +38,7 @@ public final class CoatOfArms extends CardImpl { class CoatOfArmsEffect extends ContinuousEffectImpl { - public CoatOfArmsEffect() { + public CoatOfArmsEffect() { super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); this.staticText = "Each creature gets +1/+1 for each other creature on the battlefield that shares at least one creature type with it"; } @@ -56,7 +54,9 @@ class CoatOfArmsEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - List permanents = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game); + List permanents = game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game + ); for (Permanent permanent : permanents) { int amount = getAmount(permanents, permanent, game); permanent.addPower(amount); @@ -67,20 +67,9 @@ class CoatOfArmsEffect extends ContinuousEffectImpl { private int getAmount(List permanents, Permanent target, Game game) { int amount = 0; - SubTypeList targetSubtype = target.getSubtype(game); - if (target.getAbilities().contains(ChangelingAbility.getInstance()) || target.isAllCreatureTypes()) { - return permanents.size() - 1; - } for (Permanent permanent : permanents) { - if (!permanent.getId().equals(target.getId())) { - for (SubType subtype : targetSubtype) { - if (subtype.getSubTypeSet() == SubTypeSet.CreatureType) { - if (permanent.hasSubtype(subtype, game)) { - amount++; - break; - } - } - } + if (!permanent.getId().equals(target.getId()) && permanent.shareCreatureTypes(target, game)) { + amount++; } } return amount; diff --git a/Mage.Sets/src/mage/cards/c/Conversion.java b/Mage.Sets/src/mage/cards/c/Conversion.java index afd4397c6a1..ac521592730 100644 --- a/Mage.Sets/src/mage/cards/c/Conversion.java +++ b/Mage.Sets/src/mage/cards/c/Conversion.java @@ -77,7 +77,7 @@ public final class Conversion extends CardImpl { case TypeChangingEffects_4: if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { land.getSubtype(game).removeAll(SubType.getLandTypes()); - land.getSubtype(game).add(SubType.PLAINS); + land.addSubType(game, SubType.PLAINS); land.removeAllAbilities(source.getSourceId(), game); land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); break; diff --git a/Mage.Sets/src/mage/cards/c/ConvincingMirage.java b/Mage.Sets/src/mage/cards/c/ConvincingMirage.java index 4254a33a642..842c557bf59 100644 --- a/Mage.Sets/src/mage/cards/c/ConvincingMirage.java +++ b/Mage.Sets/src/mage/cards/c/ConvincingMirage.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; @@ -18,8 +16,9 @@ import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetLandPermanent; +import java.util.UUID; + /** - * * @author North */ public final class ConvincingMirage extends CardImpl { @@ -32,8 +31,10 @@ public final class ConvincingMirage extends CardImpl { TargetPermanent auraTarget = new TargetLandPermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + // As Convincing Mirage enters the battlefield, choose a basic land type. this.addAbility(new AsEntersBattlefieldAbility(new ChooseBasicLandTypeEffect(Outcome.Neutral))); + // Enchanted land is the chosen type. Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); @@ -53,7 +54,7 @@ public final class ConvincingMirage extends CardImpl { class ConvincingMirageContinousEffect extends ContinuousEffectImpl { public ConvincingMirageContinousEffect() { - super(Duration.WhileOnBattlefield, Outcome.Neutral); + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); staticText = "Enchanted land is the chosen type"; } @@ -67,53 +68,59 @@ class ConvincingMirageContinousEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent enchantment = game.getPermanent(source.getSourceId()); + public void init(Ability source, Game game) { + super.init(source, game); SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); - if (enchantment != null && enchantment.getAttachedTo() != null && choice != null) { - Permanent land = game.getPermanent(enchantment.getAttachedTo()); - if (land != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - land.getSubtype(game).clear(); - land.getSubtype(game).add(choice); - } - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - land.getAbilities().clear(); - if (choice.equals(SubType.FOREST)) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.PLAINS)) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.MOUNTAIN)) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.ISLAND)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.SWAMP)) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - } - break; - } - return true; - } + switch (choice) { + case FOREST: + dependencyTypes.add(DependencyType.BecomeForest); + break; + case PLAINS: + dependencyTypes.add(DependencyType.BecomePlains); + break; + case MOUNTAIN: + dependencyTypes.add(DependencyType.BecomeMountain); + break; + case ISLAND: + dependencyTypes.add(DependencyType.BecomeIsland); + break; + case SWAMP: + dependencyTypes.add(DependencyType.BecomeSwamp); + break; } - return false; } @Override public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + Permanent enchantment = game.getPermanent(source.getSourceId()); + SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + if (enchantment == null || enchantment.getAttachedTo() == null || choice == null) { + return false; + } + Permanent land = game.getPermanent(enchantment.getAttachedTo()); + if (land == null) { + return false; + } + land.getSubtype(game).removeAll(SubType.getLandTypes()); + land.addSubType(game, choice); + land.removeAllAbilities(source.getSourceId(), game); + switch (choice) { + case FOREST: + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + break; + case PLAINS: + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + break; + case MOUNTAIN: + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + break; + case ISLAND: + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + break; + case SWAMP: + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + break; + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CourtOfBounty.java b/Mage.Sets/src/mage/cards/c/CourtOfBounty.java new file mode 100644 index 00000000000..3e6828b6f9f --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CourtOfBounty.java @@ -0,0 +1,57 @@ +package mage.cards.c; + +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.MonarchIsSourceControllerCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.BecomesMonarchSourceEffect; +import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CourtOfBounty extends CardImpl { + + private static final FilterCard filter = new FilterCard("a creature or land card"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.LAND.getPredicate() + )); + } + + public CourtOfBounty(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}"); + + // When Court of Bounty enters the battlefield, you become the monarch. + this.addAbility(new EntersBattlefieldTriggeredAbility(new BecomesMonarchSourceEffect())); + + // At the beginning of your upkeep, you may put a land card from your hand onto the battlefield. If you're the monarch, instead you may put a creature or land card from your hand onto the battlefield. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ConditionalOneShotEffect( + new PutCardFromHandOntoBattlefieldEffect(filter), + new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A), + MonarchIsSourceControllerCondition.instance, "you may put a land card " + + "from your hand onto the battlefield. If you're the monarch, " + + "instead you may put a creature or land card from your hand onto the battlefield" + ), TargetController.YOU, false)); + } + + private CourtOfBounty(final CourtOfBounty card) { + super(card); + } + + @Override + public CourtOfBounty copy() { + return new CourtOfBounty(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CourtOfCunning.java b/Mage.Sets/src/mage/cards/c/CourtOfCunning.java new file mode 100644 index 00000000000..1fe2533eac1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CourtOfCunning.java @@ -0,0 +1,84 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.BecomesMonarchSourceEffect; +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.players.Player; +import mage.target.Target; +import mage.target.TargetPlayer; + +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class CourtOfCunning extends CardImpl { + + public CourtOfCunning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}{U}"); + + // When Court of Cunning enters the battlefield, you become the monarch. + this.addAbility(new EntersBattlefieldTriggeredAbility(new BecomesMonarchSourceEffect())); + + // At the beginning of your upkeep, any number of target players each mill two cards. If you're the monarch, each of those players mills ten cards instead. + Ability ability = new BeginningOfUpkeepTriggeredAbility( + new CourtOfCunningEffect(), TargetController.YOU, false + ); + ability.addTarget(new TargetPlayer(0, Integer.MAX_VALUE, false)); + this.addAbility(ability); + } + + private CourtOfCunning(final CourtOfCunning card) { + super(card); + } + + @Override + public CourtOfCunning copy() { + return new CourtOfCunning(this); + } +} + +class CourtOfCunningEffect extends OneShotEffect { + + CourtOfCunningEffect() { + super(Outcome.Benefit); + staticText = "any number of target players each mill two cards. " + + "If you're the monarch, each of those players mills ten cards instead"; + } + + private CourtOfCunningEffect(final CourtOfCunningEffect effect) { + super(effect); + } + + @Override + public CourtOfCunningEffect copy() { + return new CourtOfCunningEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + boolean isMonarch = source.isControlledBy(game.getMonarchId()); + for (Player player : source + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .map(game::getPlayer) + .filter(Objects::nonNull) + .collect(Collectors.toList())) { + player.millCards(isMonarch ? 10 : 2, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CourtOfGrace.java b/Mage.Sets/src/mage/cards/c/CourtOfGrace.java new file mode 100644 index 00000000000..afc27ca7734 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CourtOfGrace.java @@ -0,0 +1,45 @@ +package mage.cards.c; + +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.MonarchIsSourceControllerCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.BecomesMonarchSourceEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.game.permanent.token.AngelToken; +import mage.game.permanent.token.SpiritWhiteToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CourtOfGrace extends CardImpl { + + public CourtOfGrace(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); + + // When Court of Grace enters the battlefield, you become the monarch. + this.addAbility(new EntersBattlefieldTriggeredAbility(new BecomesMonarchSourceEffect())); + + // At the beginning of your upkeep, create a 1/1 white Spirit creature token with flying. If you're the monarch, create a 4/4 white Angel creature token with flying instead. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ConditionalOneShotEffect( + new CreateTokenEffect(new AngelToken()), new CreateTokenEffect(new SpiritWhiteToken()), + MonarchIsSourceControllerCondition.instance, "create a 1/1 white Spirit creature token with flying. " + + "If you're the monarch, create a 4/4 white Angel creature token with flying instead" + ), TargetController.YOU, false)); + } + + private CourtOfGrace(final CourtOfGrace card) { + super(card); + } + + @Override + public CourtOfGrace copy() { + return new CourtOfGrace(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CradleToGrave.java b/Mage.Sets/src/mage/cards/c/CradleToGrave.java index 30d497fd7dd..2dcd87ae9d8 100644 --- a/Mage.Sets/src/mage/cards/c/CradleToGrave.java +++ b/Mage.Sets/src/mage/cards/c/CradleToGrave.java @@ -23,7 +23,7 @@ public final class CradleToGrave extends CardImpl { static { filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - filter.add(new EnteredThisTurnPredicate()); + filter.add(EnteredThisTurnPredicate.instance); } public CradleToGrave(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CragcrownPathway.java b/Mage.Sets/src/mage/cards/c/CragcrownPathway.java index 6ad29f7d253..efbd3bbc03d 100644 --- a/Mage.Sets/src/mage/cards/c/CragcrownPathway.java +++ b/Mage.Sets/src/mage/cards/c/CragcrownPathway.java @@ -1,26 +1,40 @@ package mage.cards.c; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.CardType; +import mage.constants.SubType; + import java.util.UUID; -import mage.abilities.keyword.TransformAbility; -import mage.abilities.mana.RedManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - /** - * @author TheElk801 + * @author JayDi85 */ -public final class CragcrownPathway extends CardImpl { +public final class CragcrownPathway extends ModalDoubleFacesCard { public CragcrownPathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + super(ownerId, setInfo, + new CardType[]{CardType.LAND}, new SubType[]{}, "", + "Timbercrown Pathway", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.secondSideCardClazz = mage.cards.t.TimbercrownPathway.class; + // 1. + // Cragcrown Pathway + // Land // {T}: Add {R}. - this.addAbility(new RedManaAbility()); - this.addAbility(new TransformAbility()); + this.getLeftHalfCard().addAbility(new RedManaAbility()); + this.getLeftHalfCard().addAbility(new TransformAbility()); + + // 2. + // Timbercrown Pathway + // Land + + // {T}: Add {G}. + this.getRightHalfCard().addAbility(new GreenManaAbility()); } private CragcrownPathway(final CragcrownPathway card) { diff --git a/Mage.Sets/src/mage/cards/c/CreepingRenaissance.java b/Mage.Sets/src/mage/cards/c/CreepingRenaissance.java index 57c5648c7e1..486f01c731c 100644 --- a/Mage.Sets/src/mage/cards/c/CreepingRenaissance.java +++ b/Mage.Sets/src/mage/cards/c/CreepingRenaissance.java @@ -1,25 +1,26 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlashbackAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.Choice; -import mage.choices.ChoiceImpl; +import mage.choices.ChoiceCardType; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TimingRule; import mage.constants.Zone; +import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; +import java.util.Arrays; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * * @author nantuko */ public final class CreepingRenaissance extends CardImpl { @@ -58,40 +59,27 @@ class CreepingRenaissanceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Choice typeChoice = new ChoiceImpl(); - typeChoice.setMessage("Choose permanent type"); - - typeChoice.getChoices().add(CardType.ARTIFACT.toString()); - typeChoice.getChoices().add(CardType.CREATURE.toString()); - typeChoice.getChoices().add(CardType.ENCHANTMENT.toString()); - typeChoice.getChoices().add(CardType.LAND.toString()); - typeChoice.getChoices().add(CardType.PLANESWALKER.toString()); - - if (controller.choose(Outcome.ReturnToHand, typeChoice, game)) { - String typeName = typeChoice.getChoice(); - CardType chosenType = null; - for (CardType cardType : CardType.values()) { - if (cardType.toString().equals(typeName)) { - chosenType = cardType; - } - } - if (chosenType != null) { - for (Card card : controller.getGraveyard().getCards(game)) { - if (card.getCardType().contains(chosenType)) { - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); - } - } - return true; - } - } + if (controller == null) { + return false; } - return false; + Choice typeChoice = new ChoiceCardType(true, Arrays.stream(CardType.values()).filter(CardType::isPermanentType).collect(Collectors.toList())); + typeChoice.setMessage("Choose a permanent type"); + + if (!controller.choose(Outcome.ReturnToHand, typeChoice, game)) { + return false; + } + String typeName = typeChoice.getChoice(); + CardType chosenType = CardType.fromString(typeChoice.getChoice()); + if (chosenType == null) { + return false; + } + FilterCard filter = new FilterCard(chosenType.toString().toLowerCase() + " card"); + filter.add(chosenType.getPredicate()); + return controller.moveCards(controller.getGraveyard().getCards(filter, source.getSourceId(), controller.getId(), game), Zone.HAND, source, game); } @Override public CreepingRenaissanceEffect copy() { return new CreepingRenaissanceEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/c/CribSwap.java b/Mage.Sets/src/mage/cards/c/CribSwap.java index 671bc13ae50..42ad6318a2e 100644 --- a/Mage.Sets/src/mage/cards/c/CribSwap.java +++ b/Mage.Sets/src/mage/cards/c/CribSwap.java @@ -28,6 +28,7 @@ public final class CribSwap extends CardImpl { this.subtype.add(SubType.SHAPESHIFTER); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Exile target creature. Its controller creates a 1/1 colorless Shapeshifter creature token with changeling. this.getSpellAbility().addEffect(new ExileTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/c/CrypticGateway.java b/Mage.Sets/src/mage/cards/c/CrypticGateway.java index de3c4d4ac75..9db7c1b83df 100644 --- a/Mage.Sets/src/mage/cards/c/CrypticGateway.java +++ b/Mage.Sets/src/mage/cards/c/CrypticGateway.java @@ -1,52 +1,40 @@ - package mage.cards.c; 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.OneShotEffect; import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; -import mage.abilities.keyword.ChangelingAbility; +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.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledPermanent; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; /** - * * @author spjspj */ public final class CrypticGateway extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control"); - - static { - filter.add(Predicates.not(TappedPredicate.instance)); - } - - TargetControlledPermanent target; - public CrypticGateway(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // Tap two untapped creatures you control: You may put a creature card from your hand that shares a creature type with each creature tapped this way onto the battlefield. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CrypticGatewayEffect(), new CrypticGatewayCost(new TargetControlledPermanent(filter)))); + this.addAbility(new SimpleActivatedAbility(new CrypticGatewayEffect(), new CrypticGatewayCost())); } public CrypticGateway(final CrypticGateway card) { @@ -61,29 +49,28 @@ public final class CrypticGateway extends CardImpl { class CrypticGatewayCost extends CostImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control"); - private UUID targetCreatureId = null; - private UUID targetCreatureId2 = null; - - TargetControlledPermanent target; + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent("untapped creatures you control"); static { filter.add(Predicates.not(TappedPredicate.instance)); } - public CrypticGatewayCost(TargetControlledPermanent target) { - this.target = target; + private final TargetControlledPermanent target = new TargetControlledPermanent(2, filter); + private CrypticGatewayPredicate predicate; + + public CrypticGatewayCost() { this.text = "Tap two untapped creatures you control"; } public CrypticGatewayCost(final CrypticGatewayCost cost) { super(cost); - this.target = cost.target.copy(); } @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { int numTargets = 0; + Set permanents = new HashSet<>(); while (numTargets < 2 && target.choose(Outcome.Tap, controllerId, sourceId, game)) { for (UUID targetId : target.getTargets()) { Permanent permanent = game.getPermanent(targetId); @@ -94,16 +81,13 @@ class CrypticGatewayCost extends CostImpl { if (paid) { numTargets++; target.clearChosen(); - } - for (Effect effect : ability.getEffects()) { - if (targetCreatureId == null) { - targetCreatureId = permanent.getId(); - } else if (targetCreatureId2 == null) { - targetCreatureId2 = permanent.getId(); - } + permanents.add(permanent); } } } + if (paid) { + this.predicate = new CrypticGatewayPredicate(permanents); + } return paid; } @@ -112,18 +96,14 @@ class CrypticGatewayCost extends CostImpl { return target.canChoose(controllerId, game); } + public CrypticGatewayPredicate getPredicate() { + return predicate; + } + @Override public CrypticGatewayCost copy() { return new CrypticGatewayCost(this); } - - public UUID getTargetCreatureId() { - return targetCreatureId; - } - - public UUID getTargetCreatureId2() { - return targetCreatureId2; - } } class CrypticGatewayEffect extends OneShotEffect { @@ -147,59 +127,33 @@ class CrypticGatewayEffect extends OneShotEffect { if (source.getCosts() == null) { return false; } - FilterCard filter = new FilterCreatureCard("creature card from your hand that shares a creature type with each creature tapped this way"); - for (Cost cost : source.getCosts()) { if (cost instanceof CrypticGatewayCost) { - UUID id = ((CrypticGatewayCost) cost).getTargetCreatureId(); - UUID id2 = ((CrypticGatewayCost) cost).getTargetCreatureId2(); - Permanent creature = game.getPermanentOrLKIBattlefield(id); - Permanent creature2 = game.getPermanentOrLKIBattlefield(id2); - - if (creature == null || creature2 == null) { - return false; - } - - boolean commonSubType = false; - boolean changeling = false; - boolean changeling2 = false; - if (creature.getAbilities().containsKey(ChangelingAbility.getInstance().getId()) || creature.isAllCreatureTypes()) { - changeling = true; - } - if (creature2.getAbilities().containsKey(ChangelingAbility.getInstance().getId()) || creature2.isAllCreatureTypes()) { - changeling2 = true; - } - - List subtypes = new ArrayList<>(); - - for (SubType subtype : creature.getSubtype(game)) { - if (creature2.hasSubtype(subtype, game) || changeling2) { - subtypes.add(subtype.getPredicate()); - commonSubType = true; - } - } - - for (SubType subtype : creature2.getSubtype(game)) { - if (creature.hasSubtype(subtype, game) || changeling) { - subtypes.add(subtype.getPredicate()); - commonSubType = true; - } - } - - if (changeling && changeling2) { - filter = new FilterCreatureCard("creature card from your hand that shares a creature type with each creature tapped this way"); - } else if (commonSubType) { - filter.add(Predicates.or(subtypes)); - } - - if (commonSubType) { - PutCardFromHandOntoBattlefieldEffect putIntoPlay = new PutCardFromHandOntoBattlefieldEffect(filter); - putIntoPlay.apply(game, source); - } + Predicate predicate = ((CrypticGatewayCost) cost).getPredicate(); + filter.add(predicate); + return new PutCardFromHandOntoBattlefieldEffect(filter).apply(game, source); } } - return false; } } + +class CrypticGatewayPredicate implements Predicate { + + private final Set permanents = new HashSet<>(); + + CrypticGatewayPredicate(Set permanents) { + this.permanents.addAll(permanents); + } + + @Override + public boolean apply(Card input, Game game) { + for (Permanent permanent : permanents) { + if (!permanent.shareCreatureTypes(input, game)) { + return false; + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DemonicLore.java b/Mage.Sets/src/mage/cards/d/DemonicLore.java new file mode 100644 index 00000000000..700785c14d4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DemonicLore.java @@ -0,0 +1,46 @@ +package mage.cards.d; + +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.MultipliedValue; +import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; +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.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DemonicLore extends CardImpl { + + private static final DynamicValue xValue = new MultipliedValue(CardsInControllerHandCount.instance, 2); + + public DemonicLore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + // When Demonic Lore enters the battlefield, draw three cards. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(3))); + + // At the beginning of your end step, you lose 2 life for each card in your hand. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new LoseLifeSourceControllerEffect(xValue) + .setText("you lose 2 life for each card in your hand"), + TargetController.YOU, false + )); + } + + private DemonicLore(final DemonicLore card) { + super(card); + } + + @Override + public DemonicLore copy() { + return new DemonicLore(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DescendantsPath.java b/Mage.Sets/src/mage/cards/d/DescendantsPath.java index 92445e71ac8..16357653c49 100644 --- a/Mage.Sets/src/mage/cards/d/DescendantsPath.java +++ b/Mage.Sets/src/mage/cards/d/DescendantsPath.java @@ -79,7 +79,7 @@ class DescendantsPathEffect extends OneShotEffect { FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); boolean found = false; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) { - if (card.shareSubtypes(permanent, game)) { + if (card.shareCreatureTypes(permanent, game)) { found = true; break; } diff --git a/Mage.Sets/src/mage/cards/d/DisruptingShoal.java b/Mage.Sets/src/mage/cards/d/DisruptingShoal.java index 8537b2b8cd5..0c60f8b5ab3 100644 --- a/Mage.Sets/src/mage/cards/d/DisruptingShoal.java +++ b/Mage.Sets/src/mage/cards/d/DisruptingShoal.java @@ -1,4 +1,3 @@ - package mage.cards.d; import mage.ObjectColor; @@ -11,7 +10,6 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; @@ -27,13 +25,12 @@ import mage.target.common.TargetCardInHand; import java.util.UUID; /** - * * @author LevelX2 */ public final class DisruptingShoal extends CardImpl { public DisruptingShoal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{X}{U}{U}"); + 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. @@ -86,16 +83,7 @@ class DisruptingShoalCounterTargetEffect extends OneShotEffect { for (Cost cost : sourceAbility.getCosts()) { if (cost.isPaid() && cost instanceof ExileFromHandCost) { for (Card card : ((ExileFromHandCost) cost).getCards()) { - if (card instanceof SplitCard) { - if (((SplitCard) card).getLeftHalfCard().getConvertedManaCost() == amount) { - return true; - } - if (((SplitCard) card).getRightHalfCard().getConvertedManaCost() == amount) { - return true; - } - } else if (card.getConvertedManaCost() == amount) { - return true; - } + return card.getConvertedManaCost() == amount; } return false; } diff --git a/Mage.Sets/src/mage/cards/d/DoubleHeader.java b/Mage.Sets/src/mage/cards/d/DoubleHeader.java index 7a483fb2820..c42c282c036 100644 --- a/Mage.Sets/src/mage/cards/d/DoubleHeader.java +++ b/Mage.Sets/src/mage/cards/d/DoubleHeader.java @@ -1,8 +1,5 @@ - package mage.cards.d; -import java.util.UUID; -import java.util.regex.Pattern; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -11,18 +8,21 @@ import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.cards.SplitCard; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.SpellAbilityType; +import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.stack.Spell; import mage.target.TargetPermanent; +import java.util.UUID; +import java.util.regex.Pattern; + /** - * * @author L_J */ public final class DoubleHeader extends CardImpl { @@ -34,7 +34,7 @@ public final class DoubleHeader extends CardImpl { } public DoubleHeader(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.subtype.add(SubType.DRAKE); this.power = new MageInt(2); @@ -68,14 +68,16 @@ class DoubleHeaderPredicate implements Predicate { public boolean apply(MageObject input, Game game) { String name = input.getName(); if (input instanceof SplitCard) { - return hasTwoWords(((SplitCard)input).getLeftHalfCard().getName()) || hasTwoWords(((SplitCard)input).getRightHalfCard().getName()); - } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED){ - SplitCard card = (SplitCard) ((Spell)input).getCard(); + return hasTwoWords(((SplitCard) input).getLeftHalfCard().getName()) || hasTwoWords(((SplitCard) input).getRightHalfCard().getName()); + } else if (input instanceof ModalDoubleFacesCard) { + return hasTwoWords(((ModalDoubleFacesCard) input).getLeftHalfCard().getName()) || hasTwoWords(((ModalDoubleFacesCard) input).getRightHalfCard().getName()); + } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { + SplitCard card = (SplitCard) ((Spell) input).getCard(); return hasTwoWords(card.getLeftHalfCard().getName()) || hasTwoWords(card.getRightHalfCard().getName()); } else { if (name.contains(" // ")) { String leftName = name.substring(0, name.indexOf(" // ")); - String rightName = name.substring(name.indexOf(" // ") + 4, name.length()); + String rightName = name.substring(name.indexOf(" // ") + 4); return hasTwoWords(leftName) || hasTwoWords(rightName); } else { return hasTwoWords(name); diff --git a/Mage.Sets/src/mage/cards/d/DrJuliusJumblemorph.java b/Mage.Sets/src/mage/cards/d/DrJuliusJumblemorph.java index eb47860166b..b01f8e5f38a 100644 --- a/Mage.Sets/src/mage/cards/d/DrJuliusJumblemorph.java +++ b/Mage.Sets/src/mage/cards/d/DrJuliusJumblemorph.java @@ -1,20 +1,31 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.abilities.keyword.ChangelingAbility; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.InfoEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; + +import java.util.UUID; /** - * * @author vereena42 & L_J */ public final class DrJuliusJumblemorph extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("a host"); + + static { + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, -1)); + } + public DrJuliusJumblemorph(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}"); this.addSuperType(SuperType.LEGENDARY); @@ -22,10 +33,17 @@ public final class DrJuliusJumblemorph extends CardImpl { this.toughness = new MageInt(4); // Dr. Julius Jumblemorph is every creature type (even if this card isn't on the battlefield). - this.addAbility(ChangelingAbility.getInstance()); - + this.setIsAllCreatureTypes(true); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect( + "{this} is every creature type (even if this card isn't on the battlefield)." + ))); + // Whenever a host enters the battlefield under your control, you may search your library and/or graveyard for a card with augment and combine it with that host. If you search your library this way, shuffle it. // TODO: Host currently isn't implemented, so this ability currently would never trigger + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new InfoEffect("you may search your library and/or graveyard for a card with augment " + + "and combine it with that host. If you search your library this way, shuffle it."), filter + )); } public DrJuliusJumblemorph(final DrJuliusJumblemorph card) { diff --git a/Mage.Sets/src/mage/cards/d/Dragonshift.java b/Mage.Sets/src/mage/cards/d/Dragonshift.java index 368a83fec51..990d8147b41 100644 --- a/Mage.Sets/src/mage/cards/d/Dragonshift.java +++ b/Mage.Sets/src/mage/cards/d/Dragonshift.java @@ -48,7 +48,7 @@ public final class Dragonshift extends CardImpl { .withColor("UR") .withSubType(SubType.DRAGON) .withAbility(FlyingAbility.getInstance()), - null, filter, Duration.EndOfTurn, true)); + null, filter, Duration.EndOfTurn, true, false, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DragonsoulKnight.java b/Mage.Sets/src/mage/cards/d/DragonsoulKnight.java index 86d66d5d9d8..50185afaf9f 100644 --- a/Mage.Sets/src/mage/cards/d/DragonsoulKnight.java +++ b/Mage.Sets/src/mage/cards/d/DragonsoulKnight.java @@ -1,7 +1,6 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -19,14 +18,15 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class DragonsoulKnight extends CardImpl { public DragonsoulKnight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.KNIGHT); @@ -37,12 +37,12 @@ public final class DragonsoulKnight extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // {W}{U}{B}{R}{G}: Until end of turn, Dragonsoul Knight becomes a Dragon, gets +5/+3, and gains flying and trample. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DragonsoulKnightEffect(), new ManaCostsImpl("{W}{U}{B}{R}{G}")); + Ability ability = new SimpleActivatedAbility(new DragonsoulKnightEffect(), new ManaCostsImpl("{W}{U}{B}{R}{G}")); Effect effect = new BoostSourceEffect(5, 3, Duration.EndOfTurn); - effect.setText("gets +5/+3"); + effect.setText(", gets +5/+3"); ability.addEffect(effect); effect = new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); - effect.setText("and gains flying"); + effect.setText(", and gains flying"); ability.addEffect(effect); effect = new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); effect.setText("and trample"); @@ -60,14 +60,14 @@ public final class DragonsoulKnight extends CardImpl { return new DragonsoulKnight(this); } - private class DragonsoulKnightEffect extends ContinuousEffectImpl { + private static class DragonsoulKnightEffect extends ContinuousEffectImpl { - public DragonsoulKnightEffect() { - super(Duration.EndOfTurn, Outcome.BecomeCreature); - setText(); + private DragonsoulKnightEffect() { + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature); + staticText = "Until end of turn, {this} becomes a Dragon"; } - public DragonsoulKnightEffect(final DragonsoulKnightEffect effect) { + private DragonsoulKnightEffect(final DragonsoulKnightEffect effect) { super(effect); } @@ -77,34 +77,14 @@ public final class DragonsoulKnight extends CardImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent == null) { return false; } - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getSubtype(game).clear(); - permanent.getSubtype(game).add(SubType.DRAGON); - } - break; - } + permanent.removeAllCreatureTypes(game); + permanent.addSubType(game, SubType.DRAGON); return true; } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - private void setText() { - staticText = "Until end of turn, {this} becomes a Dragon, "; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } } } diff --git a/Mage.Sets/src/mage/cards/d/DralnusCrusade.java b/Mage.Sets/src/mage/cards/d/DralnusCrusade.java index 343baac0ba4..7750a505ae2 100755 --- a/Mage.Sets/src/mage/cards/d/DralnusCrusade.java +++ b/Mage.Sets/src/mage/cards/d/DralnusCrusade.java @@ -16,7 +16,6 @@ import mage.game.permanent.Permanent; import java.util.UUID; /** - * * @author brikr */ public final class DralnusCrusade extends CardImpl { @@ -53,9 +52,7 @@ class DralnusCrusadeEffect extends ContinuousEffectImpl { for (Permanent permanent : game.getState().getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE_GOBLINS, source.getControllerId(), source.getSourceId(), game)) { switch (layer) { case TypeChangingEffects_4: - if (!permanent.hasSubtype(SubType.ZOMBIE, game)) { - permanent.getSubtype(game).add(SubType.ZOMBIE); - } + permanent.addSubType(game, SubType.ZOMBIE); break; case ColorChangingEffects_5: permanent.getColor(game).setColor(ObjectColor.BLACK); diff --git a/Mage.Sets/src/mage/cards/d/DranaTheLastBloodchief.java b/Mage.Sets/src/mage/cards/d/DranaTheLastBloodchief.java index d10f51dd1cb..5f29275b3e3 100644 --- a/Mage.Sets/src/mage/cards/d/DranaTheLastBloodchief.java +++ b/Mage.Sets/src/mage/cards/d/DranaTheLastBloodchief.java @@ -123,9 +123,7 @@ class DranaTheLastBloodchiefSubtypeEffect extends ContinuousEffectImpl { public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Permanent creature = mor.getPermanent(game); if (creature != null) { - if (!creature.hasSubtype(SubType.VAMPIRE, game)) { - creature.getSubtype(game).add(SubType.VAMPIRE); - } + creature.addSubType(game, SubType.VAMPIRE); return true; } else { this.used = true; diff --git a/Mage.Sets/src/mage/cards/e/EgoErasure.java b/Mage.Sets/src/mage/cards/e/EgoErasure.java index 3f7d2fba9a9..ecb93a19b4c 100644 --- a/Mage.Sets/src/mage/cards/e/EgoErasure.java +++ b/Mage.Sets/src/mage/cards/e/EgoErasure.java @@ -26,6 +26,7 @@ public final class EgoErasure extends CardImpl { this.subtype.add(SubType.SHAPESHIFTER); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); //Creatures target player controls get -2/+0 and lose all creature types until end of turn. @@ -76,7 +77,7 @@ class EgoErasureLoseEffect extends ContinuousEffectImpl { for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { Permanent permanent = it.next().getPermanent(game); if (permanent != null) { - permanent.getSubtype(game).retainAll(SubType.getLandTypes()); + permanent.removeAllCreatureTypes(game); } else { it.remove(); } diff --git a/Mage.Sets/src/mage/cards/e/EligethCrossroadsAugur.java b/Mage.Sets/src/mage/cards/e/EligethCrossroadsAugur.java new file mode 100644 index 00000000000..37ef12d0252 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EligethCrossroadsAugur.java @@ -0,0 +1,86 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.PartnerAbility; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EligethCrossroadsAugur extends CardImpl { + + public EligethCrossroadsAugur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPHINX); + this.power = new MageInt(5); + this.toughness = new MageInt(6); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // If you would scry a number of cards, draw that many cards instead. + this.addAbility(new SimpleStaticAbility(new EligethCrossroadsAugurReplacementEffect())); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private EligethCrossroadsAugur(final EligethCrossroadsAugur card) { + super(card); + } + + @Override + public EligethCrossroadsAugur copy() { + return new EligethCrossroadsAugur(this); + } +} + +class EligethCrossroadsAugurReplacementEffect extends ReplacementEffectImpl { + + EligethCrossroadsAugurReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If you would scry a number of cards, draw that many cards instead."; + } + + private EligethCrossroadsAugurReplacementEffect(final EligethCrossroadsAugurReplacementEffect effect) { + super(effect); + } + + @Override + public EligethCrossroadsAugurReplacementEffect copy() { + return new EligethCrossroadsAugurReplacementEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SURVEIL; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.drawCards(event.getAmount(), event.getSourceId(), game); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/ElsewhereFlask.java b/Mage.Sets/src/mage/cards/e/ElsewhereFlask.java index 0b4b0442c9a..13fe21ffbd0 100644 --- a/Mage.Sets/src/mage/cards/e/ElsewhereFlask.java +++ b/Mage.Sets/src/mage/cards/e/ElsewhereFlask.java @@ -1,8 +1,5 @@ - package mage.cards.e; -import java.util.Iterator; -import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -17,14 +14,15 @@ import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.choices.ChoiceBasicLandType; import mage.constants.*; -import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.Iterator; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class ElsewhereFlask extends CardImpl { @@ -36,7 +34,7 @@ public final class ElsewhereFlask extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); // Sacrifice Elsewhere Flask: Choose a basic land type. Each land you control becomes that type until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ElsewhereFlaskEffect(), new SacrificeSourceCost())); + this.addAbility(new SimpleActivatedAbility(new ElsewhereFlaskEffect(), new SacrificeSourceCost())); } public ElsewhereFlask(final ElsewhereFlask card) { @@ -80,13 +78,11 @@ class ElsewhereFlaskEffect extends OneShotEffect { class ElsewhereFlaskContinuousEffect extends ContinuousEffectImpl { - private static final FilterControlledPermanent filter = new FilterControlledLandPermanent(); - - public ElsewhereFlaskContinuousEffect() { - super(Duration.EndOfTurn, Outcome.Neutral); + ElsewhereFlaskContinuousEffect() { + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); } - public ElsewhereFlaskContinuousEffect(final ElsewhereFlaskContinuousEffect effect) { + private ElsewhereFlaskContinuousEffect(final ElsewhereFlaskContinuousEffect effect) { super(effect); } @@ -98,64 +94,68 @@ class ElsewhereFlaskContinuousEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { super.init(source, game); - if (this.affectedObjectsSet) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { - affectedObjectList.add(new MageObjectReference(permanent, game)); - } - } - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "_ElsewhereFlask")); - if (choice != null) { - for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { - Permanent land = it.next().getPermanent(game); - if (land != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - land.getSubtype(game).clear(); - land.getSubtype(game).add(choice); - } - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - land.getAbilities().clear(); - if (choice.equals(SubType.FOREST)) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.PLAINS)) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.MOUNTAIN)) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.ISLAND)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.SWAMP)) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - } - break; - } - } else { - it.remove(); - } - } - return true; + switch (choice) { + case FOREST: + dependencyTypes.add(DependencyType.BecomeForest); + break; + case PLAINS: + dependencyTypes.add(DependencyType.BecomePlains); + break; + case MOUNTAIN: + dependencyTypes.add(DependencyType.BecomeMountain); + break; + case ISLAND: + dependencyTypes.add(DependencyType.BecomeIsland); + break; + case SWAMP: + dependencyTypes.add(DependencyType.BecomeSwamp); + break; + } + if (this.affectedObjectsSet) { + game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + source.getControllerId(), source.getSourceId(), game + ).stream() + .map(permanent -> new MageObjectReference(permanent, game)) + .forEach(affectedObjectList::add); } - return false; } @Override public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "_ElsewhereFlask")); + if (choice == null) { + return false; + } + for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { + Permanent land = it.next().getPermanent(game); + if (land == null) { + it.remove(); + continue; + } + land.getSubtype(game).removeAll(SubType.getLandTypes()); + land.addSubType(game, choice); + land.removeAllAbilities(source.getSourceId(), game); + switch (choice) { + case FOREST: + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + break; + case PLAINS: + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + break; + case MOUNTAIN: + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + break; + case ISLAND: + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + break; + case SWAMP: + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + break; + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/e/EmberwildeCaptain.java b/Mage.Sets/src/mage/cards/e/EmberwildeCaptain.java new file mode 100644 index 00000000000..a1c9f4ad4d3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EmberwildeCaptain.java @@ -0,0 +1,107 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.BecomesMonarchSourceEffect; +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.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EmberwildeCaptain extends CardImpl { + + public EmberwildeCaptain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.DJINN); + this.subtype.add(SubType.PIRATE); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // When Emberwilde Captain enters the battlefield, you become the monarch. + this.addAbility(new EntersBattlefieldTriggeredAbility(new BecomesMonarchSourceEffect())); + + // Whenever an opponent attacks you while you're the monarch, Emberwilde Captain deals damage to that player equal to the number of cards in their hand. + this.addAbility(new EmberwildeCaptainTriggeredAbility()); + } + + private EmberwildeCaptain(final EmberwildeCaptain card) { + super(card); + } + + @Override + public EmberwildeCaptain copy() { + return new EmberwildeCaptain(this); + } +} + +class EmberwildeCaptainTriggeredAbility extends TriggeredAbilityImpl { + + public EmberwildeCaptainTriggeredAbility() { + super(Zone.BATTLEFIELD, new EmberwildeCaptainEffect(), false); + } + + public EmberwildeCaptainTriggeredAbility(final EmberwildeCaptainTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return game.getCombat().getDefenders().contains(getControllerId()) + && getSourceId().equals(game.getMonarchId()); + } + + @Override + public String getRule() { + return "Whenever an opponent attacks you while you're the monarch, " + + "{this} deals damage to that player equal to the number of cards in their hand."; + } + + @Override + public EmberwildeCaptainTriggeredAbility copy() { + return new EmberwildeCaptainTriggeredAbility(this); + } +} + +class EmberwildeCaptainEffect extends OneShotEffect { + + EmberwildeCaptainEffect() { + super(Outcome.Benefit); + } + + private EmberwildeCaptainEffect(final EmberwildeCaptainEffect effect) { + super(effect); + } + + @Override + public EmberwildeCaptainEffect copy() { + return new EmberwildeCaptainEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(game.getActivePlayerId()); + if (player == null || player.getHand().size() < 1) { + return false; + } + return player.damage(player.getHand().size(), source.getSourceId(), game) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EmeriaShatteredSkyclave.java b/Mage.Sets/src/mage/cards/e/EmeriaShatteredSkyclave.java deleted file mode 100644 index b437e297543..00000000000 --- a/Mage.Sets/src/mage/cards/e/EmeriaShatteredSkyclave.java +++ /dev/null @@ -1,42 +0,0 @@ -package mage.cards.e; - -import mage.abilities.common.AsEntersBattlefieldAbility; -import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.effects.common.TapSourceUnlessPaysEffect; -import mage.abilities.mana.WhiteManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class EmeriaShatteredSkyclave extends CardImpl { - - public EmeriaShatteredSkyclave(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // As Emeria, Shattered Skyclave enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. - this.addAbility(new AsEntersBattlefieldAbility( - new TapSourceUnlessPaysEffect(new PayLifeCost(3)), - "you may pay 3 life. If you don't, it enters the battlefield tapped" - )); - - // {T}: Add {W}. - this.addAbility(new WhiteManaAbility()); - } - - private EmeriaShatteredSkyclave(final EmeriaShatteredSkyclave card) { - super(card); - } - - @Override - public EmeriaShatteredSkyclave copy() { - return new EmeriaShatteredSkyclave(this); - } -} diff --git a/Mage.Sets/src/mage/cards/e/EmeriasCall.java b/Mage.Sets/src/mage/cards/e/EmeriasCall.java index 6fad79320c9..538bfb79736 100644 --- a/Mage.Sets/src/mage/cards/e/EmeriasCall.java +++ b/Mage.Sets/src/mage/cards/e/EmeriasCall.java @@ -1,10 +1,14 @@ package mage.cards.e; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.IndestructibleAbility; -import mage.cards.CardImpl; +import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; @@ -16,9 +20,9 @@ import mage.game.permanent.token.AngelWarriorToken; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class EmeriasCall extends CardImpl { +public final class EmeriasCall extends ModalDoubleFacesCard { private static final FilterPermanent filter = new FilterControlledCreaturePermanent("Non-Angel creatures you control"); @@ -28,14 +32,34 @@ public final class EmeriasCall extends CardImpl { } public EmeriasCall(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}{W}{W}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{4}{W}{W}{W}", + "Emeria, Shattered Skyclave", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); + + // 1. + // Emeria's Call + // Sorcery // Create two 4/4 white Angel Warrior creature tokens with flying. Non-Angel creatures you control gain indestructible until your next turn. - this.getSpellAbility().addEffect(new CreateTokenEffect(new AngelWarriorToken(), 2)); - this.getSpellAbility().addEffect(new GainAbilityAllEffect( + this.getLeftHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new AngelWarriorToken(), 2)); + this.getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityAllEffect( IndestructibleAbility.getInstance(), Duration.UntilYourNextTurn, filter )); + + // 2. + // Emeria, Shattered Skyclave + // Land + + // As Emeria, Shattered Skyclave enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. + this.getRightHalfCard().addAbility(new AsEntersBattlefieldAbility( + new TapSourceUnlessPaysEffect(new PayLifeCost(3)), + "you may pay 3 life. If you don't, it enters the battlefield tapped" + )); + + // {T}: Add {W}. + this.getRightHalfCard().addAbility(new WhiteManaAbility()); } private EmeriasCall(final EmeriasCall card) { diff --git a/Mage.Sets/src/mage/cards/e/EnchantedEvening.java b/Mage.Sets/src/mage/cards/e/EnchantedEvening.java index 940296f5030..a31e066c4e5 100644 --- a/Mage.Sets/src/mage/cards/e/EnchantedEvening.java +++ b/Mage.Sets/src/mage/cards/e/EnchantedEvening.java @@ -1,19 +1,18 @@ package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.Effect; 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.permanent.Permanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class EnchantedEvening extends CardImpl { @@ -22,11 +21,7 @@ public final class EnchantedEvening extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W/U}{W/U}"); // All permanents are enchantments in addition to their other types. - Effect effect = new EnchangedEveningEffect(CardType.ENCHANTMENT, - Duration.WhileOnBattlefield, new FilterPermanent()); - effect.setText("All permanents are enchantments in addition to their other types"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); - + this.addAbility(new SimpleStaticAbility(new EnchangedEveningEffect())); } public EnchantedEvening(final EnchantedEvening card) { @@ -39,31 +34,27 @@ public final class EnchantedEvening extends CardImpl { } // need to be enclosed class for dependent check of continuous effects - static class EnchangedEveningEffect extends ContinuousEffectImpl { + private static class EnchangedEveningEffect extends ContinuousEffectImpl { - private final CardType addedCardType; - private final FilterPermanent filter; - - public EnchangedEveningEffect(CardType addedCardType, Duration duration, FilterPermanent filter) { - super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); - this.addedCardType = addedCardType; - this.filter = filter; + private EnchangedEveningEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); this.dependencyTypes.add(DependencyType.EnchantmentAddingRemoving); this.dependencyTypes.add(DependencyType.AuraAddingRemoving); + this.staticText = "All permanents are enchantments in addition to their other types"; } - public EnchangedEveningEffect(final EnchangedEveningEffect effect) { + private EnchangedEveningEffect(final EnchangedEveningEffect effect) { super(effect); - this.addedCardType = effect.addedCardType; - this.filter = effect.filter; } @Override public boolean apply(Game game, Ability source) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { - if (permanent != null - && !permanent.getCardType().contains(addedCardType)) { - permanent.addCardType(addedCardType); + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_PERMANENT, source.getControllerId(), + source.getSourceId(), game + )) { + if (permanent != null) { + permanent.addCardType(CardType.ENCHANTMENT); } } return true; diff --git a/Mage.Sets/src/mage/cards/e/EverythingamajigC.java b/Mage.Sets/src/mage/cards/e/EverythingamajigC.java index 7a331d7a2ee..e6c37f1f672 100644 --- a/Mage.Sets/src/mage/cards/e/EverythingamajigC.java +++ b/Mage.Sets/src/mage/cards/e/EverythingamajigC.java @@ -9,8 +9,8 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.mana.ManaEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.abilities.effects.mana.ManaEffect; import mage.abilities.hint.common.MyTurnHint; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.CardImpl; @@ -127,7 +127,7 @@ class ChimericStaffEffect extends ContinuousEffectImpl { public ChimericStaffEffect() { super(Duration.EndOfTurn, Outcome.BecomeCreature); - setText(); + staticText = "{this} becomes an X/X Construct artifact creature until end of turn"; } public ChimericStaffEffect(final ChimericStaffEffect effect) { @@ -142,26 +142,28 @@ class ChimericStaffEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.addCardType(CardType.CREATURE); - permanent.getSubtype(game).add(SubType.CONSTRUCT); - } - break; - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - int xValue = source.getManaCostsToPay().getX(); - if (xValue != 0) { - permanent.getPower().setValue(xValue); - permanent.getToughness().setValue(xValue); - } - } - } - return true; + if (permanent == null) { + return false; } - return false; + switch (layer) { + case TypeChangingEffects_4: + if (!permanent.isArtifact()) { + permanent.addCardType(CardType.ARTIFACT); + } + if (!permanent.isCreature()) { + permanent.addCardType(CardType.CREATURE); + } + permanent.removeAllCreatureTypes(game); + permanent.addSubType(game, SubType.CONSTRUCT); + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + int xValue = source.getManaCostsToPay().getX(); + permanent.getPower().setValue(xValue); + permanent.getToughness().setValue(xValue); + } + } + return true; } @Override @@ -169,10 +171,6 @@ class ChimericStaffEffect extends ContinuousEffectImpl { return false; } - private void setText() { - staticText = duration.toString() + " {this} becomes an X/X Construct artifact creature"; - } - @Override public boolean hasLayer(Layer layer) { return layer == Layer.PTChangingEffects_7 || layer == Layer.TypeChangingEffects_4; diff --git a/Mage.Sets/src/mage/cards/e/EverythingamajigE.java b/Mage.Sets/src/mage/cards/e/EverythingamajigE.java index 6edc1f5d7ec..4af2d0abfab 100644 --- a/Mage.Sets/src/mage/cards/e/EverythingamajigE.java +++ b/Mage.Sets/src/mage/cards/e/EverythingamajigE.java @@ -1,8 +1,5 @@ - package mage.cards.e; -import java.util.UUID; - import mage.MageObject; import mage.Mana; import mage.abilities.Ability; @@ -13,14 +10,11 @@ import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.mana.SimpleManaAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.SplitCard; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SpellAbilityType; @@ -35,24 +29,25 @@ import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author Ketsuban */ public final class EverythingamajigE extends CardImpl { public EverythingamajigE(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // Zuran Orb // Sacrifice a land: You gain 2 life. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(2), new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_LAND_SHORT_TEXT)))); - + // Ashnod's Altar // Sacrifice a creature: Add {C}{C} to your mana pool. SacrificeTargetCost cost = new SacrificeTargetCost(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT)); - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, - new BasicManaEffect(Mana.ColorlessMana(2), CreaturesYouControlCount.instance), + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, + new BasicManaEffect(Mana.ColorlessMana(2), CreaturesYouControlCount.instance), cost)); // Urza's Hot Tub @@ -117,21 +112,23 @@ class UrzasHotTubPredicate implements Predicate { public boolean apply(MageObject input, Game game) { String name = input.getName(); if (input instanceof SplitCard) { - return sharesWordWithName(((SplitCard)input).getLeftHalfCard().getName()) || sharesWordWithName(((SplitCard)input).getRightHalfCard().getName()); - } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED){ - SplitCard card = (SplitCard) ((Spell)input).getCard(); + return sharesWordWithName(((SplitCard) input).getLeftHalfCard().getName()) || sharesWordWithName(((SplitCard) input).getRightHalfCard().getName()); + } else if (input instanceof ModalDoubleFacesCard) { + return sharesWordWithName(((ModalDoubleFacesCard) input).getLeftHalfCard().getName()) || sharesWordWithName(((ModalDoubleFacesCard) input).getRightHalfCard().getName()); + } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { + SplitCard card = (SplitCard) ((Spell) input).getCard(); return sharesWordWithName(card.getLeftHalfCard().getName()) || sharesWordWithName(card.getRightHalfCard().getName()); } else { if (name.contains(" // ")) { String leftName = name.substring(0, name.indexOf(" // ")); - String rightName = name.substring(name.indexOf(" // ") + 4, name.length()); + String rightName = name.substring(name.indexOf(" // ") + 4); return sharesWordWithName(leftName) || sharesWordWithName(rightName); } else { return sharesWordWithName(name); } } } - + private boolean sharesWordWithName(String str) { if (referenceName == null || referenceName.equals("")) { return false; diff --git a/Mage.Sets/src/mage/cards/e/ExquisiteHuntmaster.java b/Mage.Sets/src/mage/cards/e/ExquisiteHuntmaster.java new file mode 100644 index 00000000000..7bc0cf0bbc9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExquisiteHuntmaster.java @@ -0,0 +1,44 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.EncoreAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.ElfToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExquisiteHuntmaster extends CardImpl { + + public ExquisiteHuntmaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // When Exquisite Huntmaster dies, create a 1/1 green Elf Warrior creature token. + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ElfToken()))); + + // Encore {4}{B} + this.addAbility(new EncoreAbility(new ManaCostsImpl<>("{4}{B}"))); + } + + private ExquisiteHuntmaster(final ExquisiteHuntmaster card) { + super(card); + } + + @Override + public ExquisiteHuntmaster copy() { + return new ExquisiteHuntmaster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/Extirpate.java b/Mage.Sets/src/mage/cards/e/Extirpate.java index 9cc7d046b25..ae5b3d676cb 100644 --- a/Mage.Sets/src/mage/cards/e/Extirpate.java +++ b/Mage.Sets/src/mage/cards/e/Extirpate.java @@ -1,7 +1,5 @@ package mage.cards.e; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -9,7 +7,6 @@ import mage.abilities.keyword.SplitSecondAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SuperType; @@ -22,19 +19,22 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; + +import java.util.List; +import java.util.UUID; /** - * * @author jonubuu */ public final class Extirpate extends CardImpl { - + private static final FilterCard filter = new FilterCard("card in a graveyard other than a basic land card"); - + static { filter.add(Predicates.not(Predicates.and(CardType.LAND.getPredicate(), SuperType.BASIC.getPredicate()))); } - + public Extirpate(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); @@ -46,11 +46,11 @@ public final class Extirpate extends CardImpl { this.getSpellAbility().addEffect(new ExtirpateEffect()); this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter)); } - + public Extirpate(final Extirpate card) { super(card); } - + @Override public Extirpate copy() { return new Extirpate(this); @@ -58,7 +58,7 @@ public final class Extirpate extends CardImpl { } class ExtirpateEffect extends OneShotEffect { - + public ExtirpateEffect() { super(Outcome.Exile); this.staticText = "Choose target card in a graveyard other than " @@ -66,16 +66,16 @@ class ExtirpateEffect extends OneShotEffect { + "and library for any number of cards with the same name " + "as that card and exile them. Then that player shuffles their library"; } - + public ExtirpateEffect(final ExtirpateEffect effect) { super(effect); } - + @Override public ExtirpateEffect copy() { return new ExtirpateEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); @@ -90,7 +90,7 @@ class ExtirpateEffect extends OneShotEffect { // Exile all cards with the same name // Building a card filter with the name FilterCard filterNamedCard = new FilterCard(); - String nameToSearch = chosenCard.isSplitCard() ? ((SplitCard) chosenCard).getLeftHalfCard().getName() : chosenCard.getName(); + String nameToSearch = CardUtil.getCardNameForSameNameSearch(chosenCard); filterNamedCard.add(new NamePredicate(nameToSearch)); // The cards you're searching for must be found and exiled if they're in the graveyard because it's a public zone. @@ -133,5 +133,5 @@ class ExtirpateEffect extends OneShotEffect { } return false; } - + } diff --git a/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java b/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java index 45ca1108955..2fd5fa352fc 100644 --- a/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java +++ b/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java @@ -1,5 +1,6 @@ package mage.cards.e; +import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; @@ -21,7 +22,6 @@ import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; import java.util.UUID; -import mage.ApprovingObject; /** * @author spjspj @@ -127,9 +127,12 @@ class EyeOfTheStormEffect1 extends OneShotEffect { // Check if owner of card is still in game card = game.getCard(uuid); if (card != null && game.getPlayer(card.getOwnerId()) != null) { - if (card.isSplitCard()) { + if (card instanceof SplitCard) { copiedCards.add(((SplitCard) card).getLeftHalfCard()); copiedCards.add(((SplitCard) card).getRightHalfCard()); + } else if (card instanceof ModalDoubleFacesCard) { + copiedCards.add(((ModalDoubleFacesCard) card).getLeftHalfCard()); + copiedCards.add(((ModalDoubleFacesCard) card).getRightHalfCard()); } else { copiedCards.add(card); } diff --git a/Mage.Sets/src/mage/cards/e/EyeblightCullers.java b/Mage.Sets/src/mage/cards/e/EyeblightCullers.java new file mode 100644 index 00000000000..f09f5ec81b2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EyeblightCullers.java @@ -0,0 +1,43 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.ElfToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EyeblightCullers extends CardImpl { + + public EyeblightCullers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Eyeblight Cullers dies, create three 1/1 green Elf Warrior creature tokens, then mill three cards. + Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new ElfToken(), 3)); + ability.addEffect(new MillCardsControllerEffect(3).concatBy(", then")); + this.addAbility(ability); + } + + private EyeblightCullers(final EyeblightCullers card) { + super(card); + } + + @Override + public EyeblightCullers copy() { + return new EyeblightCullers(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FacesOfThePast.java b/Mage.Sets/src/mage/cards/f/FacesOfThePast.java index 518012e91c7..77fde7971c0 100644 --- a/Mage.Sets/src/mage/cards/f/FacesOfThePast.java +++ b/Mage.Sets/src/mage/cards/f/FacesOfThePast.java @@ -62,13 +62,13 @@ class FacesOfThePastEffect extends OneShotEffect { if (controller != null) { if (controller.chooseUse(outcome, "Tap all untapped creatures that share a creature type with " + targetPermanent.getLogName() + "? (Otherwise, untaps all tapped)", source, game)) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, game)) { - if (!permanent.isTapped() && targetPermanent.shareSubtypes(permanent, game)) { + if (!permanent.isTapped() && targetPermanent.shareCreatureTypes(permanent, game)) { permanent.tap(game); } } } else { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, game)) { - if (permanent.isTapped() && targetPermanent.shareSubtypes(permanent, game)) { + if (permanent.isTapped() && targetPermanent.shareCreatureTypes(permanent, game)) { permanent.untap(game); } } diff --git a/Mage.Sets/src/mage/cards/f/FallFromFavor.java b/Mage.Sets/src/mage/cards/f/FallFromFavor.java new file mode 100644 index 00000000000..ab19e5b6fc5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FallFromFavor.java @@ -0,0 +1,69 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.BecomesMonarchSourceEffect; +import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect; +import mage.abilities.effects.common.TapEnchantedEffect; +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.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FallFromFavor extends CardImpl { + + public FallFromFavor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + 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 Fall from Favor enters the battlefield, tap enchanted creature and you become the monarch. + ability = new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect()); + ability.addEffect(new BecomesMonarchSourceEffect().concatBy("and")); + this.addAbility(ability); + + // Enchanted creature doesn't untap during its controller's untap step unless that player is the monarch. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousRuleModifyingEffect( + new DontUntapInControllersUntapStepEnchantedEffect(), FallFromFavorCondition.instance + ).setText("enchanted creature doesn't untap during its controller's untap step unless that player is the monarch"))); + } + + private FallFromFavor(final FallFromFavor card) { + super(card); + } + + @Override + public FallFromFavor copy() { + return new FallFromFavor(this); + } +} + +enum FallFromFavorCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.getActivePlayerId().equals(game.getActivePlayerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FireBellyChangeling.java b/Mage.Sets/src/mage/cards/f/FireBellyChangeling.java index 2ee6cb5a130..e63b834cf52 100644 --- a/Mage.Sets/src/mage/cards/f/FireBellyChangeling.java +++ b/Mage.Sets/src/mage/cards/f/FireBellyChangeling.java @@ -27,6 +27,7 @@ public final class FireBellyChangeling extends CardImpl { this.toughness = new MageInt(1); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // {R}: Fire-Belly Changeling gets +1/+0 until end of turn. Activate this ability no more than twice each turn. diff --git a/Mage.Sets/src/mage/cards/f/FlamespeakerAdept.java b/Mage.Sets/src/mage/cards/f/FlamespeakerAdept.java index 91a974370eb..53ce27461e2 100644 --- a/Mage.Sets/src/mage/cards/f/FlamespeakerAdept.java +++ b/Mage.Sets/src/mage/cards/f/FlamespeakerAdept.java @@ -62,7 +62,7 @@ class ScryTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.SCRY; + return event.getType() == EventType.SCRIED; } @Override diff --git a/Mage.Sets/src/mage/cards/f/ForceOfDespair.java b/Mage.Sets/src/mage/cards/f/ForceOfDespair.java index f182f037d3d..bb2177fcab1 100644 --- a/Mage.Sets/src/mage/cards/f/ForceOfDespair.java +++ b/Mage.Sets/src/mage/cards/f/ForceOfDespair.java @@ -30,7 +30,7 @@ public final class ForceOfDespair extends CardImpl { static { filter.add(new ColorPredicate(ObjectColor.BLACK)); - filter2.add(new EnteredThisTurnPredicate()); + filter2.add(EnteredThisTurnPredicate.instance); } public ForceOfDespair(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/f/ForcefulDenial.java b/Mage.Sets/src/mage/cards/f/ForcefulDenial.java new file mode 100644 index 00000000000..42faa1ceb91 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/ForcefulDenial.java @@ -0,0 +1,36 @@ +package mage.cards.f; + +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.keyword.CascadeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ForcefulDenial extends CardImpl { + + public ForcefulDenial(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); + + // Cascade + this.addAbility(new CascadeAbility()); + + // Counter target spell. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); + } + + private ForcefulDenial(final ForcefulDenial card) { + super(card); + } + + @Override + public ForcefulDenial copy() { + return new ForcefulDenial(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java b/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java index b745f928008..42b58af29f4 100644 --- a/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java +++ b/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java @@ -30,7 +30,7 @@ public final class ForgeOfHeroes extends CardImpl { static { filter.add(CommanderPredicate.instance); - filter.add(new EnteredThisTurnPredicate()); + filter.add(EnteredThisTurnPredicate.instance); } public ForgeOfHeroes(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/g/GameTrailChangeling.java b/Mage.Sets/src/mage/cards/g/GameTrailChangeling.java index 6315d0e3454..4238a6deabe 100644 --- a/Mage.Sets/src/mage/cards/g/GameTrailChangeling.java +++ b/Mage.Sets/src/mage/cards/g/GameTrailChangeling.java @@ -22,6 +22,7 @@ public final class GameTrailChangeling extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); this.addAbility(TrampleAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/g/GhenArcanumWeaver.java b/Mage.Sets/src/mage/cards/g/GhenArcanumWeaver.java new file mode 100644 index 00000000000..05a174d1278 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GhenArcanumWeaver.java @@ -0,0 +1,61 @@ +package mage.cards.g; + +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.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.common.FilterControlledEnchantmentPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterEnchantmentCard; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GhenArcanumWeaver extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledEnchantmentPermanent("an enchantment"); + private static final FilterCard filter2 + = new FilterEnchantmentCard("enchantment card from your graveyard"); + + public GhenArcanumWeaver(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.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // {R}{W}{B}, {T}, Sacrifice an enchantment: Return target enchantment card from your graveyard to the battlefield. + Ability ability = new SimpleActivatedAbility( + new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{R}{W}{B}") + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + ability.addTarget(new TargetCardInYourGraveyard(filter2)); + this.addAbility(ability); + } + + private GhenArcanumWeaver(final GhenArcanumWeaver card) { + super(card); + } + + @Override + public GhenArcanumWeaver copy() { + return new GhenArcanumWeaver(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GhostOfRamirezDePietro.java b/Mage.Sets/src/mage/cards/g/GhostOfRamirezDePietro.java new file mode 100644 index 00000000000..fb3f746a148 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GhostOfRamirezDePietro.java @@ -0,0 +1,125 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.SimpleEvasionAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; +import mage.abilities.keyword.PartnerAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.ToughnessPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.target.common.TargetCardInGraveyard; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GhostOfRamirezDePietro extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creatures with toughness 3 or greater"); + private static final FilterCard filter2 + = new FilterCard("card in a graveyard that was discarded or put there from a library this turn"); + + static { + filter.add(new ToughnessPredicate(ComparisonType.MORE_THAN, 2)); + filter2.add(GhostOfRamirezDePietroPredicate.instance); + } + + public GhostOfRamirezDePietro(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.PIRATE); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Ghost of Ramirez DePietro can't be blocked by creatures with toughness 3 or greater. + this.addAbility(new SimpleEvasionAbility( + new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield) + )); + + // Whenever Ghost of Ramirez DePietro deals combat damage to a player, choose up to one target card in a graveyard that was discarded or put there from a library this turn. Put that card into its owner's hand. + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility( + new ReturnFromGraveyardToHandTargetEffect() + .setText("choose up to one target card in a graveyard that was discarded " + + "or put there from a library this turn. Put that card into its owner's hand"), + false + ); + ability.addTarget(new TargetCardInGraveyard(filter2)); + this.addAbility(ability, new GhostOfRamirezDePietroWatcher()); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private GhostOfRamirezDePietro(final GhostOfRamirezDePietro card) { + super(card); + } + + @Override + public GhostOfRamirezDePietro copy() { + return new GhostOfRamirezDePietro(this); + } +} + +enum GhostOfRamirezDePietroPredicate implements Predicate { + instance; + + @Override + public boolean apply(Card input, Game game) { + GhostOfRamirezDePietroWatcher watcher = game.getState().getWatcher(GhostOfRamirezDePietroWatcher.class); + return watcher != null && watcher.checkCard(input, game); + } +} + +class GhostOfRamirezDePietroWatcher extends Watcher { + + private final Set morSet = new HashSet<>(); + + GhostOfRamirezDePietroWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + switch (event.getType()) { + case ZONE_CHANGE: + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getFromZone() != Zone.LIBRARY + || zEvent.getToZone() != Zone.GRAVEYARD) { + break; + } + case DISCARDED_CARD: + morSet.add(new MageObjectReference(event.getTargetId(), game)); + default: + break; + } + } + + @Override + public void reset() { + super.reset(); + morSet.clear(); + } + + boolean checkCard(Card card, Game game) { + return morSet.stream().anyMatch(mor -> mor.refersTo(card, game)); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GhostlyChangeling.java b/Mage.Sets/src/mage/cards/g/GhostlyChangeling.java index 0f29fb30830..e5f5c1b4603 100644 --- a/Mage.Sets/src/mage/cards/g/GhostlyChangeling.java +++ b/Mage.Sets/src/mage/cards/g/GhostlyChangeling.java @@ -26,6 +26,7 @@ public final class GhostlyChangeling extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{1}{B}"))); } diff --git a/Mage.Sets/src/mage/cards/g/Glaciers.java b/Mage.Sets/src/mage/cards/g/Glaciers.java index dd24f30067c..59234594f8f 100644 --- a/Mage.Sets/src/mage/cards/g/Glaciers.java +++ b/Mage.Sets/src/mage/cards/g/Glaciers.java @@ -74,7 +74,7 @@ public final class Glaciers extends CardImpl { // the land mana ability is intrinsic, so apply at this layer not layer 6 if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { land.getSubtype(game).removeAll(SubType.getLandTypes()); - land.getSubtype(game).add(SubType.PLAINS); + land.addSubType(game, SubType.PLAINS); land.removeAllAbilities(source.getSourceId(), game); land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); break; diff --git a/Mage.Sets/src/mage/cards/g/GlasspoolMimic.java b/Mage.Sets/src/mage/cards/g/GlasspoolMimic.java index ffd0f243642..46a43dfb4e1 100644 --- a/Mage.Sets/src/mage/cards/g/GlasspoolMimic.java +++ b/Mage.Sets/src/mage/cards/g/GlasspoolMimic.java @@ -4,9 +4,11 @@ import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.CopyPermanentEffect; -import mage.cards.CardImpl; +import mage.abilities.mana.BlueManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.StaticFilters; @@ -17,27 +19,37 @@ import mage.util.functions.ApplyToPermanent; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class GlasspoolMimic extends CardImpl { +public final class GlasspoolMimic extends ModalDoubleFacesCard { public GlasspoolMimic(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.SHAPESHIFTER, SubType.ROGUE}, "{2}{U}", + "Glasspool Shore", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.subtype.add(SubType.SHAPESHIFTER); - this.subtype.add(SubType.ROGUE); - this.power = new MageInt(0); - this.toughness = new MageInt(0); - - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.g.GlasspoolShore.class; + // 1. + // Glasspool Mimic + // Creature — Shapeshifter Rogue + this.getLeftHalfCard().setPT(new MageInt(0), new MageInt(0)); // You may have Glasspool Mimic enter the battlefield as a copy of a creature you control, except it's a Shapeshifter Rogue in addition to its other types. - this.addAbility(new EntersBattlefieldAbility( + this.getLeftHalfCard().addAbility(new EntersBattlefieldAbility( new CopyPermanentEffect(StaticFilters.FILTER_CONTROLLED_CREATURE, new GlasspoolMimicApplier()), true, null, "You may have {this} enter the battlefield as a copy of " + "a creature you control, except it's a Shapeshifter Rogue in addition to its other types.", "" )); + + // 2. + // Glasspool Shore + // Land + + // Glasspool Shore enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {U}. + this.getRightHalfCard().addAbility(new BlueManaAbility()); } private GlasspoolMimic(final GlasspoolMimic card) { @@ -54,15 +66,15 @@ class GlasspoolMimicApplier extends ApplyToPermanent { @Override public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { - permanent.getSubtype(game).add(SubType.SHAPESHIFTER); - permanent.getSubtype(game).add(SubType.ROGUE); + permanent.addSubType(game, SubType.SHAPESHIFTER); + permanent.addSubType(game, SubType.ROGUE); return true; } @Override public boolean apply(Game game, MageObject mageObject, Ability source, UUID copyToObjectId) { - mageObject.getSubtype(game).add(SubType.SHAPESHIFTER); - mageObject.getSubtype(game).add(SubType.ROGUE); + mageObject.addSubType(game, SubType.SHAPESHIFTER); + mageObject.addSubType(game, SubType.ROGUE); return true; } } diff --git a/Mage.Sets/src/mage/cards/g/GlasspoolShore.java b/Mage.Sets/src/mage/cards/g/GlasspoolShore.java deleted file mode 100644 index 65f00a8f7aa..00000000000 --- a/Mage.Sets/src/mage/cards/g/GlasspoolShore.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.g; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class GlasspoolShore extends CardImpl { - - public GlasspoolShore(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Glasspool Shore enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {U}. - this.addAbility(new BlueManaAbility()); - } - - private GlasspoolShore(final GlasspoolShore card) { - super(card); - } - - @Override - public GlasspoolShore copy() { - return new GlasspoolShore(this); - } -} diff --git a/Mage.Sets/src/mage/cards/g/GnostroVoiceOfTheCrags.java b/Mage.Sets/src/mage/cards/g/GnostroVoiceOfTheCrags.java new file mode 100644 index 00000000000..efd915102f9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GnostroVoiceOfTheCrags.java @@ -0,0 +1,103 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +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.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.CastSpellLastTurnWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GnostroVoiceOfTheCrags extends CardImpl { + + public GnostroVoiceOfTheCrags(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CHIMERA); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {T}: Choose one. X is the number of spells you've cast this turn. + // • Scry X. + Ability ability = new SimpleActivatedAbility(new GnostroVoiceOfTheCragsEffect(), new TapSourceCost()); + ability.getModes().setChooseText("choose one. X is the number of spells you've cast this turn."); + + // • Gnostro, Voice of the Crags deals X damage to target creature. + Mode mode = new Mode(new DamageTargetEffect(GnostroVoiceOfTheCragsValue.instance) + .setText("{this} deals X damage to target creature")); + mode.addTarget(new TargetCreaturePermanent()); + ability.addMode(mode); + + // • You gain X life. + ability.addMode(new Mode(new GainLifeEffect(GnostroVoiceOfTheCragsValue.instance).setText("you gain X life"))); + } + + private GnostroVoiceOfTheCrags(final GnostroVoiceOfTheCrags card) { + super(card); + } + + @Override + public GnostroVoiceOfTheCrags copy() { + return new GnostroVoiceOfTheCrags(this); + } +} + +enum GnostroVoiceOfTheCragsValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + return watcher == null ? 0 : watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(sourceAbility.getControllerId()); + } + + @Override + public GnostroVoiceOfTheCragsValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} + +class GnostroVoiceOfTheCragsEffect extends OneShotEffect { + + GnostroVoiceOfTheCragsEffect() { + super(Outcome.Benefit); + staticText = "scry X"; + } + + private GnostroVoiceOfTheCragsEffect(final GnostroVoiceOfTheCragsEffect effect) { + super(effect); + } + + @Override + public GnostroVoiceOfTheCragsEffect copy() { + return new GnostroVoiceOfTheCragsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java b/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java index 8ada8cfce89..df45a74c2c3 100644 --- a/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java +++ b/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java @@ -88,8 +88,8 @@ class GodPharaohsGiftEffect extends OneShotEffect { token.getPower().modifyBaseValue(4); token.getToughness().modifyBaseValue(4); token.getColor(game).setColor(ObjectColor.BLACK); - token.getSubtype(game).clear(); - token.getSubtype(game).add(SubType.ZOMBIE); + token.removeAllCreatureTypes(game); + token.addSubType(game, SubType.ZOMBIE); if (token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId())) { Permanent tokenPermanent = game.getPermanent(token.getLastAddedToken()); if (tokenPermanent != null) { diff --git a/Mage.Sets/src/mage/cards/g/GorMuldrakAmphinologist.java b/Mage.Sets/src/mage/cards/g/GorMuldrakAmphinologist.java new file mode 100644 index 00000000000..23162f84293 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GorMuldrakAmphinologist.java @@ -0,0 +1,106 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControllerEffect; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.token.SalamnderWarriorToken; +import mage.game.permanent.token.Token; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GorMuldrakAmphinologist extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.SALAMANDER, "Salamanders"); + + public GorMuldrakAmphinologist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // You and permanents you control have protection from Salamanders. + Ability ability = new SimpleStaticAbility( + new GainAbilityControllerEffect(new ProtectionAbility(filter)).setText("you") + ); + ability.addEffect(new GainAbilityControlledEffect( + new ProtectionAbility(filter), Duration.WhileOnBattlefield + ).concatBy("and")); + this.addAbility(ability); + + // At the beginning of your end step, each player who controls the fewest creatures creates a 4/3 blue Salamander Warrior creature token. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new GorMuldrakAmphinologistEffect(), TargetController.YOU, false + )); + } + + private GorMuldrakAmphinologist(final GorMuldrakAmphinologist card) { + super(card); + } + + @Override + public GorMuldrakAmphinologist copy() { + return new GorMuldrakAmphinologist(this); + } +} + +class GorMuldrakAmphinologistEffect extends OneShotEffect { + + GorMuldrakAmphinologistEffect() { + super(Outcome.Benefit); + staticText = "each player who controls the fewest creatures " + + "creates a 4/3 blue Salamander Warrior creature token"; + } + + private GorMuldrakAmphinologistEffect(final GorMuldrakAmphinologistEffect effect) { + super(effect); + } + + @Override + public GorMuldrakAmphinologistEffect copy() { + return new GorMuldrakAmphinologistEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Map creatureMap = new HashMap<>(); + game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, + source.getControllerId(), game + ).stream() + .filter(Objects::nonNull) + .map(Controllable::getControllerId) + .forEach(uuid -> creatureMap.compute(uuid, (u, i) -> i == null ? 1 : Integer.sum(i, 1))); + int minValue = creatureMap.values().stream().mapToInt(x -> x).min().orElse(0); + minValue = Math.max(minValue, 0); + Token token = new SalamnderWarriorToken(); + for (Map.Entry entry : creatureMap.entrySet()) { + if (entry.getValue() > minValue) { + continue; + } + token.putOntoBattlefield(1, game, source.getSourceId(), entry.getKey()); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/Graveshifter.java b/Mage.Sets/src/mage/cards/g/Graveshifter.java index 279b8397c0f..dc2f3f3e3a0 100644 --- a/Mage.Sets/src/mage/cards/g/Graveshifter.java +++ b/Mage.Sets/src/mage/cards/g/Graveshifter.java @@ -27,6 +27,7 @@ public final class Graveshifter extends CardImpl { this.toughness = new MageInt(2); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // When Graveshifter enters the battlefield, you may return target creature card from your graveyard to your hand. diff --git a/Mage.Sets/src/mage/cards/g/GrimclimbPathway.java b/Mage.Sets/src/mage/cards/g/GrimclimbPathway.java deleted file mode 100644 index e3cbace1e54..00000000000 --- a/Mage.Sets/src/mage/cards/g/GrimclimbPathway.java +++ /dev/null @@ -1,33 +0,0 @@ -package mage.cards.g; - -import mage.abilities.mana.BlackManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class GrimclimbPathway extends CardImpl { - - public GrimclimbPathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // {T}: Add {B}. - this.addAbility(new BlackManaAbility()); - } - - private GrimclimbPathway(final GrimclimbPathway card) { - super(card); - } - - @Override - public GrimclimbPathway copy() { - return new GrimclimbPathway(this); - } -} diff --git a/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java b/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java index 4c56f634117..6312618db8d 100644 --- a/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java +++ b/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java @@ -1,9 +1,6 @@ package mage.cards.g; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardTargetCost; @@ -24,14 +21,17 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class GrimoireOfTheDead extends CardImpl { public GrimoireOfTheDead(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); addSuperType(SuperType.LEGENDARY); // {1}, {tap}, Discard a card: Put a study counter on Grimoire of the Dead. @@ -121,14 +121,10 @@ class GrimoireOfTheDeadEffect2 extends ContinuousEffectImpl { if (permanent != null) { switch (layer) { case ColorChangingEffects_5: - if (sublayer == SubLayer.NA) { - permanent.getColor(game).setBlack(true); - } + permanent.getColor(game).setBlack(true); break; case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getSubtype(game).add(SubType.ZOMBIE); - } + permanent.addSubType(game, SubType.ZOMBIE); break; } return true; diff --git a/Mage.Sets/src/mage/cards/g/GrimoireThief.java b/Mage.Sets/src/mage/cards/g/GrimoireThief.java index 5504fb40ef8..b9d10b886ff 100644 --- a/Mage.Sets/src/mage/cards/g/GrimoireThief.java +++ b/Mage.Sets/src/mage/cards/g/GrimoireThief.java @@ -1,9 +1,5 @@ package mage.cards.g; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -24,8 +20,12 @@ import mage.players.Player; import mage.target.common.TargetOpponent; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class GrimoireThief extends CardImpl { @@ -197,30 +197,24 @@ class GrimoireThiefCounterspellEffect extends OneShotEffect { } // then counter any with the same name as the card exiled with Grimoire Thief for (Card card : cards.getCards(game)) { - for (Iterator iterator = game.getStack().iterator(); iterator.hasNext();) { + for (Iterator iterator = game.getStack().iterator(); iterator.hasNext(); ) { StackObject stackObject = iterator.next(); MageObject mageObject = game.getObject(card.getId()); - // handle split cards + String name1; + String name2; if (mageObject instanceof SplitCard) { - if (stackObject instanceof Spell - && (stackObject.getName().contains(((SplitCard) mageObject).getLeftHalfCard().getName()) - || stackObject.getName().contains(((SplitCard) mageObject).getRightHalfCard().getName()))) { - Spell spell = (Spell) stackObject; - game.getStack().counter(stackObject.getId(), source.getSourceId(), game); - game.informPlayers(sourceObject.getLogName() - + ": the split-card spell named " - + spell.getName() - + " was countered."); - } + name1 = ((SplitCard) mageObject).getLeftHalfCard().getName(); + name2 = ((SplitCard) mageObject).getRightHalfCard().getName(); + } else { + // modal double faces cards, adventure cards -- all have one name in non stack/battlefield zone + name1 = mageObject.getName(); + name2 = name1; } - if (stackObject instanceof Spell - && stackObject.getName().contains(card.getName())) { + + if (CardUtil.haveSameNames(stackObject, name1, game) || CardUtil.haveSameNames(stackObject, name2, game)) { Spell spell = (Spell) stackObject; - game.getStack().counter(spell.getId(), source.getSourceId(), game); - game.informPlayers(sourceObject.getLogName() - + ": the spell named " - + spell.getName() - + " was countered."); + game.getStack().counter(stackObject.getId(), source.getSourceId(), game); + game.informPlayers(sourceObject.getLogName() + ": spell " + spell.getIdName() + " was countered."); } } } diff --git a/Mage.Sets/src/mage/cards/h/HagraBroodpit.java b/Mage.Sets/src/mage/cards/h/HagraBroodpit.java deleted file mode 100644 index 2cf540dfae5..00000000000 --- a/Mage.Sets/src/mage/cards/h/HagraBroodpit.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.h; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.BlackManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class HagraBroodpit extends CardImpl { - - public HagraBroodpit(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Hagra Broodpit enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {B}. - this.addAbility(new BlackManaAbility()); - } - - private HagraBroodpit(final HagraBroodpit card) { - super(card); - } - - @Override - public HagraBroodpit copy() { - return new HagraBroodpit(this); - } -} diff --git a/Mage.Sets/src/mage/cards/h/HagraMauling.java b/Mage.Sets/src/mage/cards/h/HagraMauling.java index 51cfa3c5d59..e0c1fd0a4ba 100644 --- a/Mage.Sets/src/mage/cards/h/HagraMauling.java +++ b/Mage.Sets/src/mage/cards/h/HagraMauling.java @@ -1,15 +1,18 @@ package mage.cards.h; import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; import mage.abilities.hint.ConditionHint; import mage.abilities.hint.Hint; -import mage.cards.CardImpl; +import mage.abilities.mana.BlackManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.FilterPermanent; @@ -23,27 +26,41 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class HagraMauling extends CardImpl { +public final class HagraMauling extends ModalDoubleFacesCard { private static final Hint hint = new ConditionHint( HagraMaulingCondition.instance, "An opponent controls no basic lands" ); public HagraMauling(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{B}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{2}{B}{B}", + "Hagra Broodpit", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.h.HagraBroodpit.class; + // 1. + // Hagra Mauling + // Instant // This spell costs {1} less to cast if an opponent controls no basic lands. - this.addAbility(new SimpleStaticAbility( + this.getLeftHalfCard().addAbility(new SimpleStaticAbility( Zone.ALL, new SpellCostReductionSourceEffect(1, HagraMaulingCondition.instance).setCanWorksOnStackOnly(true) ).setRuleAtTheTop(true)); // Destroy target creature. - this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addHint(hint); + this.getLeftHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getLeftHalfCard().getSpellAbility().addHint(hint); + + // 2. + // Hagra Broodpit + // Land + + // Hagra Broodpit enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {B}. + this.getRightHalfCard().addAbility(new BlackManaAbility()); } private HagraMauling(final HagraMauling card) { diff --git a/Mage.Sets/src/mage/cards/h/HauntingEchoes.java b/Mage.Sets/src/mage/cards/h/HauntingEchoes.java index c3e2e60bb46..6be5cde65a8 100644 --- a/Mage.Sets/src/mage/cards/h/HauntingEchoes.java +++ b/Mage.Sets/src/mage/cards/h/HauntingEchoes.java @@ -1,4 +1,3 @@ - package mage.cards.h; import mage.abilities.Ability; @@ -6,7 +5,6 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterCard; @@ -16,12 +14,12 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; import java.util.List; import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class HauntingEchoes extends CardImpl { @@ -63,9 +61,10 @@ class HauntingEchoesEffect extends OneShotEffect { if (!StaticFilters.FILTER_CARD_BASIC_LAND.match(card, game)) { card.moveToExile(null, "", source.getSourceId(), game); - FilterCard filterCard = new FilterCard("cards named " + card.getName()); - String nameToSearch = card.isSplitCard() ? ((SplitCard) card).getLeftHalfCard().getName() : card.getName(); + String nameToSearch = CardUtil.getCardNameForSameNameSearch(card); + FilterCard filterCard = new FilterCard("cards named " + nameToSearch); filterCard.add(new NamePredicate(nameToSearch)); + int count = targetPlayer.getLibrary().count(filterCard, game); TargetCardInLibrary target = new TargetCardInLibrary(count, count, filterCard); diff --git a/Mage.Sets/src/mage/cards/h/HeirloomBlade.java b/Mage.Sets/src/mage/cards/h/HeirloomBlade.java index 65edc738a56..7a87d4de458 100644 --- a/Mage.Sets/src/mage/cards/h/HeirloomBlade.java +++ b/Mage.Sets/src/mage/cards/h/HeirloomBlade.java @@ -82,7 +82,7 @@ class HeirloomBladeEffect extends OneShotEffect { Cards otherCards = new CardsImpl(); for (Card card : controller.getLibrary().getCards(game)) { revealed.add(card); - if (card != null && card.isCreature() && equipped.shareSubtypes(card, game)) { + if (card != null && card.isCreature() && equipped.shareCreatureTypes(card, game)) { controller.moveCardToHandWithInfo(card, source.getSourceId(), game, true); break; } else { diff --git a/Mage.Sets/src/mage/cards/h/HorizonStone.java b/Mage.Sets/src/mage/cards/h/HorizonStone.java new file mode 100644 index 00000000000..c2c032d3558 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HorizonStone.java @@ -0,0 +1,73 @@ +package mage.cards.h; + +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.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 HorizonStone extends CardImpl { + + public HorizonStone(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); + + // If you would lose unspent mana, that mana becomes colorless instead. + this.addAbility(new SimpleStaticAbility(new HorizonStoneEffect())); + } + + private HorizonStone(final HorizonStone card) { + super(card); + } + + @Override + public HorizonStone copy() { + return new HorizonStone(this); + } +} + +class HorizonStoneEffect extends ReplacementEffectImpl { + + HorizonStoneEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If you would lose unspent mana, that mana becomes colorless instead."; + } + + private HorizonStoneEffect(final HorizonStoneEffect effect) { + super(effect); + } + + @Override + public HorizonStoneEffect copy() { + return new HorizonStoneEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @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.EMPTY_MANA_POOL; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HourOfEternity.java b/Mage.Sets/src/mage/cards/h/HourOfEternity.java index 918d40620dc..dea482bda90 100644 --- a/Mage.Sets/src/mage/cards/h/HourOfEternity.java +++ b/Mage.Sets/src/mage/cards/h/HourOfEternity.java @@ -97,8 +97,8 @@ class HourOfEternityEffect extends OneShotEffect { token.getPower().modifyBaseValue(4); token.getToughness().modifyBaseValue(4); token.getColor(game).setColor(ObjectColor.BLACK); - token.getSubtype(game).clear(); - token.getSubtype(game).add(SubType.ZOMBIE); + token.removeAllCreatureTypes(game); + token.addSubType(game, SubType.ZOMBIE); token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); } } diff --git a/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java b/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java index 5fd36bb4ea7..27c0a4b5e82 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java +++ b/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java @@ -95,7 +95,7 @@ class IllusionaryTerrainEffect extends ContinuousEffectImpl { // the land mana ability is intrinsic, so add it here, not layer 6 if (land.getSubtype(game).contains(firstChoice)) { land.getSubtype(game).removeAll(SubType.getLandTypes()); - land.getSubtype(game).add(secondChoice); + land.addSubType(game, secondChoice); land.removeAllAbilities(source.getSourceId(), game); if (land.getSubtype(game).contains(SubType.FOREST)) { this.dependencyTypes.add(DependencyType.BecomeForest); diff --git a/Mage.Sets/src/mage/cards/i/ImpostorOfTheSixthPride.java b/Mage.Sets/src/mage/cards/i/ImpostorOfTheSixthPride.java index 94e82f36d8c..bfbb7d01bce 100644 --- a/Mage.Sets/src/mage/cards/i/ImpostorOfTheSixthPride.java +++ b/Mage.Sets/src/mage/cards/i/ImpostorOfTheSixthPride.java @@ -22,6 +22,7 @@ public final class ImpostorOfTheSixthPride extends CardImpl { this.toughness = new MageInt(1); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/i/InfernalTutor.java b/Mage.Sets/src/mage/cards/i/InfernalTutor.java index eb4b152f66a..d87437eb94f 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalTutor.java +++ b/Mage.Sets/src/mage/cards/i/InfernalTutor.java @@ -1,6 +1,5 @@ package mage.cards.i; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.condition.common.HellbentCondition; @@ -12,7 +11,6 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.CardsImpl; -import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterCard; @@ -23,9 +21,11 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class InfernalTutor extends CardImpl { @@ -89,7 +89,7 @@ class InfernalTutorEffect extends OneShotEffect { FilterCard filterCard; if (cardToReveal != null) { controller.revealCards("from hand :" + sourceObject.getName(), new CardsImpl(cardToReveal), game); - String nameToSearch = cardToReveal.isSplitCard() ? ((SplitCard) cardToReveal).getLeftHalfCard().getName() : cardToReveal.getName(); + String nameToSearch = CardUtil.getCardNameForSameNameSearch(cardToReveal); filterCard = new FilterCard("card named " + nameToSearch); filterCard.add(new NamePredicate(nameToSearch)); } else { diff --git a/Mage.Sets/src/mage/cards/i/IrregularCohort.java b/Mage.Sets/src/mage/cards/i/IrregularCohort.java index 4b7d95621f3..5d7ed2537e1 100644 --- a/Mage.Sets/src/mage/cards/i/IrregularCohort.java +++ b/Mage.Sets/src/mage/cards/i/IrregularCohort.java @@ -24,6 +24,7 @@ public final class IrregularCohort extends CardImpl { this.toughness = new MageInt(2); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // When Irregular Cohort enters the battlefield, create a 2/2 colorless Shapeshifter creature token with changeling. diff --git a/Mage.Sets/src/mage/cards/j/JestersScepter.java b/Mage.Sets/src/mage/cards/j/JestersScepter.java index 88c08a65953..e476ccee0ac 100644 --- a/Mage.Sets/src/mage/cards/j/JestersScepter.java +++ b/Mage.Sets/src/mage/cards/j/JestersScepter.java @@ -161,14 +161,15 @@ class JestersScepterCost extends CostImpl { Card card = game.getCard(target.getFirstTarget()); if (card != null) { if (controller.moveCardToGraveyardWithInfo(card, sourceId, game, Zone.EXILED)) { - // Split Card check if (card instanceof SplitCard) { game.getState().setValue(sourceId + "_nameOfExiledCardPayment", ((SplitCard) card).getLeftHalfCard().getName()); game.getState().setValue(sourceId + "_nameOfExiledCardPayment2", ((SplitCard) card).getRightHalfCard().getName()); - paid = true; - return paid; + } else if (card instanceof ModalDoubleFacesCard) { + game.getState().setValue(sourceId + "_nameOfExiledCardPayment", ((ModalDoubleFacesCard) card).getLeftHalfCard().getName()); + game.getState().setValue(sourceId + "_nameOfExiledCardPayment2", ((ModalDoubleFacesCard) card).getRightHalfCard().getName()); + } else { + game.getState().setValue(sourceId + "_nameOfExiledCardPayment", card.getName()); } - game.getState().setValue(sourceId + "_nameOfExiledCardPayment", card.getName()); paid = true; } } diff --git a/Mage.Sets/src/mage/cards/j/JeweledLotus.java b/Mage.Sets/src/mage/cards/j/JeweledLotus.java new file mode 100644 index 00000000000..435512972a3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JeweledLotus.java @@ -0,0 +1,48 @@ +package mage.cards.j; + +import mage.abilities.Ability; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +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.TargetController; +import mage.filter.FilterSpell; +import mage.filter.predicate.permanent.CommanderPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JeweledLotus extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("your commander"); + + static { + filter.add(CommanderPredicate.instance); + filter.add(TargetController.YOU.getOwnerPredicate()); + } + + public JeweledLotus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); + + // {T}, Sacrifice Jeweled Lotus: Add three mana of any one color. Spend this mana only to cast your commander. + Ability ability = new ConditionalAnyColorManaAbility( + new TapSourceCost(), 3, new ConditionalSpellManaBuilder(filter), true + ); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private JeweledLotus(final JeweledLotus card) { + super(card); + } + + @Override + public JeweledLotus copy() { + return new JeweledLotus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JwariDisruption.java b/Mage.Sets/src/mage/cards/j/JwariDisruption.java index 40fa89f1752..02ee1e75d70 100644 --- a/Mage.Sets/src/mage/cards/j/JwariDisruption.java +++ b/Mage.Sets/src/mage/cards/j/JwariDisruption.java @@ -1,28 +1,45 @@ package mage.cards.j; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.CounterUnlessPaysEffect; -import mage.cards.CardImpl; +import mage.abilities.mana.BlueManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.target.TargetSpell; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class JwariDisruption extends CardImpl { +public final class JwariDisruption extends ModalDoubleFacesCard { public JwariDisruption(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{1}{U}", + "Jwari Ruins", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.j.JwariDisruption.class; + // 1. + // Jwari Disruption + // Instant // Counter target spell unless its controller pays {1}. - this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(new GenericManaCost(1))); - this.getSpellAbility().addTarget(new TargetSpell()); + this.getLeftHalfCard().getSpellAbility().addEffect(new CounterUnlessPaysEffect(new GenericManaCost(1))); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetSpell()); + + // 2. + // Jwari Ruins + // Land + + // Jwari Ruins enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {U}. + this.getRightHalfCard().addAbility(new BlueManaAbility()); } private JwariDisruption(final JwariDisruption card) { diff --git a/Mage.Sets/src/mage/cards/j/JwariRuins.java b/Mage.Sets/src/mage/cards/j/JwariRuins.java deleted file mode 100644 index 9333be02603..00000000000 --- a/Mage.Sets/src/mage/cards/j/JwariRuins.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.j; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class JwariRuins extends CardImpl { - - public JwariRuins(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Jwari Ruins enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {U}. - this.addAbility(new BlueManaAbility()); - } - - private JwariRuins(final JwariRuins card) { - super(card); - } - - @Override - public JwariRuins copy() { - return new JwariRuins(this); - } -} diff --git a/Mage.Sets/src/mage/cards/k/KabiraPlateau.java b/Mage.Sets/src/mage/cards/k/KabiraPlateau.java deleted file mode 100644 index 04ed2cb920c..00000000000 --- a/Mage.Sets/src/mage/cards/k/KabiraPlateau.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.k; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.WhiteManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class KabiraPlateau extends CardImpl { - - public KabiraPlateau(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Kabira Plateau enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {W}. - this.addAbility(new WhiteManaAbility()); - } - - private KabiraPlateau(final KabiraPlateau card) { - super(card); - } - - @Override - public KabiraPlateau copy() { - return new KabiraPlateau(this); - } -} diff --git a/Mage.Sets/src/mage/cards/k/KabiraTakedown.java b/Mage.Sets/src/mage/cards/k/KabiraTakedown.java index 30c4a77d30b..6958b6c61d9 100644 --- a/Mage.Sets/src/mage/cards/k/KabiraTakedown.java +++ b/Mage.Sets/src/mage/cards/k/KabiraTakedown.java @@ -1,33 +1,53 @@ package mage.cards.k; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.DamageTargetEffect; -import mage.cards.CardImpl; +import mage.abilities.hint.ValueHint; +import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.StaticFilters; import mage.target.common.TargetCreatureOrPlaneswalker; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class KabiraTakedown extends CardImpl { +public final class KabiraTakedown extends ModalDoubleFacesCard { private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURES); public KabiraTakedown(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{1}{W}", + "Kabira Plateau", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.k.KabiraPlateau.class; + // 1. + // Kabira Takedown + // Instant // Kabira Takedown deals damage equal to the number of creatures you control to target creature or planeswalker. - this.getSpellAbility().addEffect(new DamageTargetEffect(xValue).setText("{this} deals damage equal to the number of creatures you control to target creature or planeswalker")); - this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + this.getLeftHalfCard().getSpellAbility().addEffect(new DamageTargetEffect(xValue) + .setText("{this} deals damage equal to the number of creatures you control to target creature or planeswalker")); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + this.getLeftHalfCard().getSpellAbility().addHint(new ValueHint("Creatures you control", xValue)); + + // 2. + // Kabira Plateau + // Land + + // Kabira Plateau enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {W}. + this.getRightHalfCard().addAbility(new WhiteManaAbility()); } private KabiraTakedown(final KabiraTakedown card) { diff --git a/Mage.Sets/src/mage/cards/k/KamahlHeartOfKrosa.java b/Mage.Sets/src/mage/cards/k/KamahlHeartOfKrosa.java new file mode 100644 index 00000000000..0cbaf39dca7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KamahlHeartOfKrosa.java @@ -0,0 +1,69 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +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.StaticFilters; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KamahlHeartOfKrosa extends CardImpl { + + public KamahlHeartOfKrosa(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // At the beginning of combat on your turn, creatures you control get +3/+3 and gain trample until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility(new BoostControlledEffect( + 3, 3, Duration.EndOfTurn + ).setText("creatures you control get +3/+3"), TargetController.YOU, false); + ability.addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURES + ).setText("and gain trample until end of turn")); + this.addAbility(ability); + + // {1}{G}: Until end of turn, target land you control becomes a 1/1 Elemental creature with vigilance, indestructible, and haste. It's still a land. + ability = new SimpleActivatedAbility(new BecomesCreatureTargetEffect(new CreatureToken( + 1, 1, "1/1 Elemental creature with vigilance, indestructible, and haste" + ).withSubType(SubType.ELEMENTAL) + .withAbility(VigilanceAbility.getInstance()) + .withAbility(IndestructibleAbility.getInstance()) + .withAbility(HasteAbility.getInstance()), + false, true, Duration.EndOfTurn + ), new ManaCostsImpl("{1}{G}")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND)); + this.addAbility(ability); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private KamahlHeartOfKrosa(final KamahlHeartOfKrosa card) { + super(card); + } + + @Override + public KamahlHeartOfKrosa copy() { + return new KamahlHeartOfKrosa(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KazanduMammoth.java b/Mage.Sets/src/mage/cards/k/KazanduMammoth.java index 7dc14543011..e3be7c0d13e 100644 --- a/Mage.Sets/src/mage/cards/k/KazanduMammoth.java +++ b/Mage.Sets/src/mage/cards/k/KazanduMammoth.java @@ -1,10 +1,12 @@ package mage.cards.k; import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.LandfallAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.cards.CardImpl; +import mage.abilities.mana.GreenManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; @@ -12,22 +14,33 @@ import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class KazanduMammoth extends CardImpl { +public final class KazanduMammoth extends ModalDoubleFacesCard { public KazanduMammoth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.ELEPHANT}, "{1}{G}{G}", + "Kazandu Valley", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.subtype.add(SubType.ELEPHANT); - this.power = new MageInt(3); - this.toughness = new MageInt(3); - - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.k.KazanduValley.class; + // 1. + // Kazandu Mammoth + // Creature — Elephant + this.getLeftHalfCard().setPT(new MageInt(3), new MageInt(3)); // Landfall — Whenever a land enters the battlefield under your control, Kazandu Mammoth gets +2/+2 until end of turn. - this.addAbility(new LandfallAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn))); + this.getLeftHalfCard().addAbility(new LandfallAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn))); + + // 2. + // Kazandu Valley + // Land + + // Kazandu Valley enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {G}. + this.getRightHalfCard().addAbility(new GreenManaAbility()); } private KazanduMammoth(final KazanduMammoth card) { diff --git a/Mage.Sets/src/mage/cards/k/KazanduValley.java b/Mage.Sets/src/mage/cards/k/KazanduValley.java deleted file mode 100644 index 5463227c345..00000000000 --- a/Mage.Sets/src/mage/cards/k/KazanduValley.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.k; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class KazanduValley extends CardImpl { - - public KazanduValley(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Kazandu Valley enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {G}. - this.addAbility(new GreenManaAbility()); - } - - private KazanduValley(final KazanduValley card) { - super(card); - } - - @Override - public KazanduValley copy() { - return new KazanduValley(this); - } -} diff --git a/Mage.Sets/src/mage/cards/k/KazuulsCliffs.java b/Mage.Sets/src/mage/cards/k/KazuulsCliffs.java deleted file mode 100644 index 69b98f10d59..00000000000 --- a/Mage.Sets/src/mage/cards/k/KazuulsCliffs.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.k; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.RedManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class KazuulsCliffs extends CardImpl { - - public KazuulsCliffs(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Kazuul's Cliffs enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {R}. - this.addAbility(new RedManaAbility()); - } - - private KazuulsCliffs(final KazuulsCliffs card) { - super(card); - } - - @Override - public KazuulsCliffs copy() { - return new KazuulsCliffs(this); - } -} diff --git a/Mage.Sets/src/mage/cards/k/KazuulsFury.java b/Mage.Sets/src/mage/cards/k/KazuulsFury.java index 6ef4d6b20cf..ef23ecda54a 100644 --- a/Mage.Sets/src/mage/cards/k/KazuulsFury.java +++ b/Mage.Sets/src/mage/cards/k/KazuulsFury.java @@ -1,11 +1,14 @@ package mage.cards.k; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.dynamicvalue.common.SacrificeCostCreaturesPower; import mage.abilities.effects.common.DamageTargetEffect; -import mage.cards.CardImpl; +import mage.abilities.mana.RedManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetControlledCreaturePermanent; @@ -14,23 +17,37 @@ import java.util.UUID; import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; /** - * @author TheElk801 + * @author JayDi85 */ -public final class KazuulsFury extends CardImpl { +public final class KazuulsFury extends ModalDoubleFacesCard { public KazuulsFury(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{2}{R}", + "Kazuul's Cliffs", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.k.KazuulsCliffs.class; + // 1. + // Kazuul's Fury + // Instant // As an additional cost to cast this spell, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); + this.getLeftHalfCard().getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); // Kazuul's Fury deals damage equal to the sacrificed creatures power to any target. - this.getSpellAbility().addEffect(new DamageTargetEffect(SacrificeCostCreaturesPower.instance) + this.getLeftHalfCard().getSpellAbility().addEffect(new DamageTargetEffect(SacrificeCostCreaturesPower.instance) .setText("{this} deals damage equal to the sacrificed creature's power to any target")); - this.getSpellAbility().addTarget(new TargetAnyTarget()); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetAnyTarget()); + + // 2. + // Kazuul's Cliffs + // Land + + // Kazuul's Cliffs enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {R}. + this.getRightHalfCard().addAbility(new RedManaAbility()); } private KazuulsFury(final KazuulsFury card) { diff --git a/Mage.Sets/src/mage/cards/k/KedissEmberclawFamiliar.java b/Mage.Sets/src/mage/cards/k/KedissEmberclawFamiliar.java new file mode 100644 index 00000000000..675bbc7350c --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KedissEmberclawFamiliar.java @@ -0,0 +1,99 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.PartnerAbility; +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.CommanderPredicate; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KedissEmberclawFamiliar extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("commander you control"); + + static { + filter.add(CommanderPredicate.instance); + } + + public KedissEmberclawFamiliar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.LIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever a commander you control deals combat damage to an opponent, it deals that much damage to each other opponent. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new KedissEmberclawFamiliarEffect(), filter, false, + SetTargetPointer.PLAYER, true, true + )); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private KedissEmberclawFamiliar(final KedissEmberclawFamiliar card) { + super(card); + } + + @Override + public KedissEmberclawFamiliar copy() { + return new KedissEmberclawFamiliar(this); + } +} + +class KedissEmberclawFamiliarEffect extends OneShotEffect { + + KedissEmberclawFamiliarEffect() { + super(Outcome.Benefit); + staticText = "it deals that much damage to each other opponent"; + } + + private KedissEmberclawFamiliarEffect(final KedissEmberclawFamiliarEffect effect) { + super(effect); + } + + @Override + public KedissEmberclawFamiliarEffect copy() { + return new KedissEmberclawFamiliarEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Object obj = getValue("sourceId"); + if (!(obj instanceof UUID)) { + return false; + } + UUID sourceId = (UUID) obj; + obj = getValue("damage"); + if (!(obj instanceof Integer)) { + return false; + } + int damage = (Integer) obj; + for (UUID playerId : game.getOpponents(source.getControllerId())) { + if (playerId.equals(getTargetPointer().getFirst(game, source))) { + continue; + } + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + player.damage(damage, sourceId, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KelethSunmaneFamiliar.java b/Mage.Sets/src/mage/cards/k/KelethSunmaneFamiliar.java new file mode 100644 index 00000000000..920490e003a --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KelethSunmaneFamiliar.java @@ -0,0 +1,57 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +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.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.CommanderPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KelethSunmaneFamiliar extends CardImpl { + + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent("commander you control"); + + static { + filter.add(CommanderPredicate.instance); + } + + public KelethSunmaneFamiliar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HORSE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever a commander you control attacks, put a +1/+1 counter on it. + this.addAbility(new AttacksCreatureYouControlTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it"), + false, filter, true + )); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private KelethSunmaneFamiliar(final KelethSunmaneFamiliar card) { + super(card); + } + + @Override + public KelethSunmaneFamiliar copy() { + return new KelethSunmaneFamiliar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KeskitTheFleshSculptor.java b/Mage.Sets/src/mage/cards/k/KeskitTheFleshSculptor.java new file mode 100644 index 00000000000..2b98b3b3dd8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KeskitTheFleshSculptor.java @@ -0,0 +1,70 @@ +package mage.cards.k; + +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.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +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.constants.Zone; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KeskitTheFleshSculptor extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledPermanent("three other artifacts and/or creatures"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + + public KeskitTheFleshSculptor(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.ARTIFICER); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {T}, Sacrifice three other artifacts and/or creatures: Look at the top three cards of your library. Put two of them into your hand and the other into your graveyard. + Ability ability = new SimpleActivatedAbility(new LookLibraryAndPickControllerEffect( + StaticValue.get(3), false, StaticValue.get(2), StaticFilters.FILTER_CARD, + Zone.GRAVEYARD, false, false + ), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(3, filter))); + this.addAbility(ability); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private KeskitTheFleshSculptor(final KeskitTheFleshSculptor card) { + super(card); + } + + @Override + public KeskitTheFleshSculptor copy() { + return new KeskitTheFleshSculptor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KhalniAmbush.java b/Mage.Sets/src/mage/cards/k/KhalniAmbush.java index 73eece0b0e5..fc1d1f7ac0a 100644 --- a/Mage.Sets/src/mage/cards/k/KhalniAmbush.java +++ b/Mage.Sets/src/mage/cards/k/KhalniAmbush.java @@ -1,9 +1,12 @@ package mage.cards.k; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.FightTargetsEffect; -import mage.cards.CardImpl; +import mage.abilities.mana.GreenManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -11,20 +14,34 @@ import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class KhalniAmbush extends CardImpl { +public final class KhalniAmbush extends ModalDoubleFacesCard { public KhalniAmbush(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{2}{G}", + "Khalni Territory", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.k.KhalniTerritory.class; + // 1. + // Khalni Ambush + // Instant // Target creature you control fights target creature you don't control. - this.getSpellAbility().addEffect(new FightTargetsEffect()); - this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.getLeftHalfCard().getSpellAbility().addEffect(new FightTargetsEffect()); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + + // 2. + // Khalni Territory + // Land + + // Khalni Territory enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {G}. + this.getRightHalfCard().addAbility(new GreenManaAbility()); } private KhalniAmbush(final KhalniAmbush card) { diff --git a/Mage.Sets/src/mage/cards/k/KhalniTerritory.java b/Mage.Sets/src/mage/cards/k/KhalniTerritory.java deleted file mode 100644 index d9c1e6f6e82..00000000000 --- a/Mage.Sets/src/mage/cards/k/KhalniTerritory.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.k; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class KhalniTerritory extends CardImpl { - - public KhalniTerritory(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Khalni Territory enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {G}. - this.addAbility(new GreenManaAbility()); - } - - private KhalniTerritory(final KhalniTerritory card) { - super(card); - } - - @Override - public KhalniTerritory copy() { - return new KhalniTerritory(this); - } -} diff --git a/Mage.Sets/src/mage/cards/k/KinsbaileCourier.java b/Mage.Sets/src/mage/cards/k/KinsbaileCourier.java new file mode 100644 index 00000000000..9d2e2c8c7ec --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KinsbaileCourier.java @@ -0,0 +1,50 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.EncoreAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KinsbaileCourier extends CardImpl { + + public KinsbaileCourier(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.KITHKIN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Kinsbaile Courier enters the battlefield, put a +1/+1 counter on target creature. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // Encore {2}{W} + this.addAbility(new EncoreAbility(new ManaCostsImpl<>("{2}{W}"))); + } + + private KinsbaileCourier(final KinsbaileCourier card) { + super(card); + } + + @Override + public KinsbaileCourier copy() { + return new KinsbaileCourier(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KitesailSkirmisher.java b/Mage.Sets/src/mage/cards/k/KitesailSkirmisher.java new file mode 100644 index 00000000000..22d7e11b0b0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KitesailSkirmisher.java @@ -0,0 +1,82 @@ +package mage.cards.k; + +import com.google.common.base.Objects; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.EncoreAbility; +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.filter.FilterPermanent; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KitesailSkirmisher extends CardImpl { + + private static final FilterPermanent filter + = new FilterAttackingCreature("another target creature attacking the same player or planeswalker"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(KitesailSkirmisherPredicate.instance); + } + + public KitesailSkirmisher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.PIRATE); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Kitesail Skirmisher attacks, another target creature attacking the same player or planeswalker gains flying until end of turn. + Ability ability = new AttacksTriggeredAbility(new GainAbilityTargetEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn + ), false); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // Encore {4}{U} + this.addAbility(new EncoreAbility(new ManaCostsImpl<>("{4}{U}"))); + } + + private KitesailSkirmisher(final KitesailSkirmisher card) { + super(card); + } + + @Override + public KitesailSkirmisher copy() { + return new KitesailSkirmisher(this); + } +} + +enum KitesailSkirmisherPredicate implements ObjectSourcePlayerPredicate> { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return Objects.equal( + game.getCombat().getDefenderId(input.getSourceId()), + game.getCombat().getDefenderId(input.getObject().getId()) + ); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/k/KnowledgeAndPower.java b/Mage.Sets/src/mage/cards/k/KnowledgeAndPower.java index 1a78e9ef3fb..31a88dff8af 100644 --- a/Mage.Sets/src/mage/cards/k/KnowledgeAndPower.java +++ b/Mage.Sets/src/mage/cards/k/KnowledgeAndPower.java @@ -58,7 +58,7 @@ class ScryTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.SCRY; + return event.getType() == EventType.SCRIED; } @Override diff --git a/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java b/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java new file mode 100644 index 00000000000..1cc62350452 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java @@ -0,0 +1,176 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.PartnerAbility; +import mage.abilities.keyword.ReachAbility; +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.common.FilterPermanentCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInHand; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class KodamaOfTheEastTree extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(AnotherPredicate.instance); + } + + public KodamaOfTheEastTree(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Whenever another permanent enters the battlefield under your control, if it wasn't put onto the battlefield with this ability, you may put a permanent card with equal or lesser converted mana cost from your hand onto the battlefield. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldAllTriggeredAbility(new KodamaOfTheEastTreeEffect(), filter), + KodamaOfTheEastTreeCondition.instance, "Whenever another permanent enters the battlefield " + + "under your control, if it wasn't put onto the battlefield with this ability, you may put " + + "a permanent card with equal or lesser converted mana cost from your hand onto the battlefield." + ), new KodamaOfTheEastTreeWatcher()); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private KodamaOfTheEastTree(final KodamaOfTheEastTree card) { + super(card); + } + + @Override + public KodamaOfTheEastTree copy() { + return new KodamaOfTheEastTree(this); + } +} + +enum KodamaOfTheEastTreeCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = null; + for (Effect effect : source.getEffects()) { + Object obj = effect.getValue("permanentEnteringBattlefield"); + if (obj instanceof Permanent) { + permanent = (Permanent) obj; + break; + } + } + KodamaOfTheEastTreeWatcher watcher = game.getState().getWatcher(KodamaOfTheEastTreeWatcher.class); + return watcher != null && !watcher.checkPermanent(permanent, source, game); + } +} + +class KodamaOfTheEastTreeEffect extends OneShotEffect { + + KodamaOfTheEastTreeEffect() { + super(Outcome.Benefit); + } + + private KodamaOfTheEastTreeEffect(final KodamaOfTheEastTreeEffect effect) { + super(effect); + } + + @Override + public KodamaOfTheEastTreeEffect copy() { + return new KodamaOfTheEastTreeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Object obj = this.getValue("permanentEnteringBattlefield"); + if (!(obj instanceof Permanent)) { + return false; + } + Permanent permanent = (Permanent) obj; + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + FilterCard filter = new FilterPermanentCard( + "a permanent card with converted mana cost " + permanent.getConvertedManaCost() + " or less" + ); + filter.add(new ConvertedManaCostPredicate( + ComparisonType.FEWER_THAN, permanent.getConvertedManaCost() + 1 + )); + TargetCardInHand target = new TargetCardInHand(filter); + if (!target.canChoose(source.getSourceId(), source.getControllerId(), game) + || !player.chooseUse(outcome, "Put a permanent card onto the battlefield?", source, game)) { + return false; + } + player.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent otherPermanent = game.getPermanent(card.getId()); + if (otherPermanent == null) { + return false; + } + KodamaOfTheEastTreeWatcher watcher = game.getState().getWatcher(KodamaOfTheEastTreeWatcher.class); + if (watcher != null) { + watcher.addPermanent(otherPermanent, source, game); + } + return true; + } +} + +class KodamaOfTheEastTreeWatcher extends Watcher { + + private final Map> morMap = new HashMap<>(); + + KodamaOfTheEastTreeWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + } + + void addPermanent(Permanent permanent, Ability source, Game game) { + morMap.computeIfAbsent(getKey(source, game), x -> new HashSet<>()) + .add(new MageObjectReference(permanent, game)); + } + + boolean checkPermanent(Permanent permanent, Ability source, Game game) { + return morMap + .computeIfAbsent(getKey(source, game), x -> new HashSet<>()) + .stream() + .anyMatch(mor -> mor.refersTo(permanent, game)); + } + + private static String getKey(Ability source, Game game) { + return "" + source.getSourceId() + game.getState().getZoneChangeCounter(source.getSourceId()); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KondasBanner.java b/Mage.Sets/src/mage/cards/k/KondasBanner.java index fbe583449aa..5c48062109a 100644 --- a/Mage.Sets/src/mage/cards/k/KondasBanner.java +++ b/Mage.Sets/src/mage/cards/k/KondasBanner.java @@ -109,7 +109,7 @@ class KondasBannerTypeBoostEffect extends BoostAllEffect { Permanent equipedCreature = game.getPermanent(equipment.getAttachedTo()); if (equipedCreature != null) { for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (perm.shareSubtypes(equipedCreature, game)) { + if (perm.shareCreatureTypes(equipedCreature, game)) { perm.addPower(power.calculate(game, source, this)); perm.addToughness(toughness.calculate(game, source, this)); diff --git a/Mage.Sets/src/mage/cards/k/KrarkTheThumbless.java b/Mage.Sets/src/mage/cards/k/KrarkTheThumbless.java new file mode 100644 index 00000000000..05521376400 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KrarkTheThumbless.java @@ -0,0 +1,87 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.PartnerAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KrarkTheThumbless extends CardImpl { + + public KrarkTheThumbless(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.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever you cast an instant or sorcery spell, flip a coin. If you lose the flip, return that spell to its owner's hand. If you win the flip, copy that spell, and you may choose new targets for the copy. + this.addAbility(new SpellCastControllerTriggeredAbility( + new KrarkTheThumblessEffect(), + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, + false, true + )); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private KrarkTheThumbless(final KrarkTheThumbless card) { + super(card); + } + + @Override + public KrarkTheThumbless copy() { + return new KrarkTheThumbless(this); + } +} + +class KrarkTheThumblessEffect extends OneShotEffect { + + KrarkTheThumblessEffect() { + super(Outcome.Benefit); + staticText = "flip a coin. If you lose the flip, return that spell to its owner's hand. " + + "If you win the flip, copy that spell, and you may choose new targets for the copy"; + } + + private KrarkTheThumblessEffect(final KrarkTheThumblessEffect effect) { + super(effect); + } + + @Override + public KrarkTheThumblessEffect copy() { + return new KrarkTheThumblessEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Spell spell = game.getSpellOrLKIStack(getTargetPointer().getFirst(game, source)); + if (player == null || spell == null) { + return false; + } + if (player.flipCoin(source, game, true)) { + spell.createCopyOnStack(game, source, player.getId(), true); + return true; + } + if (spell.isCopy()) { + game.getStack().remove(spell, game); + return true; + } + return game.getSpell(spell.getId()) == null && player.moveCards(spell, Zone.HAND, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LaboratoryDrudge.java b/Mage.Sets/src/mage/cards/l/LaboratoryDrudge.java new file mode 100644 index 00000000000..26e3a1a7727 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LaboratoryDrudge.java @@ -0,0 +1,91 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LaboratoryDrudge extends CardImpl { + + public LaboratoryDrudge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // At the beginning of each end step, draw a card if you've cast a spell from a graveyard or activated an ability of a card in a graveyard this turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), LaboratoryDrudgeCondition.instance, + "draw a card if you've cast a spell from a graveyard or activated an ability of a card in a graveyard this turn" + ), TargetController.ANY, false + ), new LaboratoryDrudgeWatcher()); + } + + private LaboratoryDrudge(final LaboratoryDrudge card) { + super(card); + } + + @Override + public LaboratoryDrudge copy() { + return new LaboratoryDrudge(this); + } +} + +enum LaboratoryDrudgeCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + LaboratoryDrudgeWatcher watcher = game.getState().getWatcher(LaboratoryDrudgeWatcher.class); + return watcher != null && watcher.checkPlayer(source.getControllerId()); + } +} + +class LaboratoryDrudgeWatcher extends Watcher { + + private final Set playerSet = new HashSet<>(); + + LaboratoryDrudgeWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getZone() != Zone.GRAVEYARD) { + return; + } + switch (event.getType()) { + case SPELL_CAST: + case ACTIVATED_ABILITY: + playerSet.add(event.getPlayerId()); + } + } + + @Override + public void reset() { + playerSet.clear(); + super.reset(); + } + + boolean checkPlayer(UUID playerId) { + return playerSet.contains(playerId); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LavaglidePathway.java b/Mage.Sets/src/mage/cards/l/LavaglidePathway.java deleted file mode 100644 index cbd2e2a2fb1..00000000000 --- a/Mage.Sets/src/mage/cards/l/LavaglidePathway.java +++ /dev/null @@ -1,33 +0,0 @@ -package mage.cards.l; - -import mage.abilities.mana.RedManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class LavaglidePathway extends CardImpl { - - public LavaglidePathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // {T}: Add {R}. - this.addAbility(new RedManaAbility()); - } - - private LavaglidePathway(final LavaglidePathway card) { - super(card); - } - - @Override - public LavaglidePathway copy() { - return new LavaglidePathway(this); - } -} diff --git a/Mage.Sets/src/mage/cards/l/LiarsPendulum.java b/Mage.Sets/src/mage/cards/l/LiarsPendulum.java index e30258077d1..078496adf96 100644 --- a/Mage.Sets/src/mage/cards/l/LiarsPendulum.java +++ b/Mage.Sets/src/mage/cards/l/LiarsPendulum.java @@ -8,7 +8,6 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.SplitCard; import mage.cards.repository.CardRepository; import mage.choices.Choice; import mage.choices.ChoiceImpl; @@ -85,14 +84,6 @@ class LiarsPendulumEffect extends OneShotEffect { boolean rightGuess = !opponentGuess; for (Card card : controller.getHand().getCards(game)) { - if (card.isSplitCard()) { - SplitCard splitCard = (SplitCard) card; - if (splitCard.getLeftHalfCard().getName().equals(cardName)) { - rightGuess = opponentGuess; - } else if (splitCard.getRightHalfCard().getName().equals(cardName)) { - rightGuess = opponentGuess; - } - } if (CardUtil.haveSameNames(card, cardName, game)) { rightGuess = opponentGuess; } diff --git a/Mage.Sets/src/mage/cards/l/LifeAndLimb.java b/Mage.Sets/src/mage/cards/l/LifeAndLimb.java index c48d4df3265..e1ea3d82cd8 100644 --- a/Mage.Sets/src/mage/cards/l/LifeAndLimb.java +++ b/Mage.Sets/src/mage/cards/l/LifeAndLimb.java @@ -70,18 +70,13 @@ class LifeAndLimbEffect extends ContinuousEffectImpl { switch (layer) { case TypeChangingEffects_4: permanent.addCardType(CardType.CREATURE); - if (!permanent.hasSubtype(SubType.SAPROLING, game)) { - permanent.getSubtype(game).add(SubType.SAPROLING); - } - - // land abilities are intrinsic, so add them here, not in layer 6 - if (!permanent.hasSubtype(SubType.FOREST, game)) { - permanent.getSubtype(game).add(SubType.FOREST); - if (!permanent.getAbilities(game).containsClass(GreenManaAbility.class)) { - permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - } permanent.addCardType(CardType.LAND); + permanent.addSubType(game, SubType.SAPROLING); + // land abilities are intrinsic, so add them here, not in layer 6 + permanent.addSubType(game, SubType.FOREST); + if (!permanent.getAbilities(game).containsClass(GreenManaAbility.class)) { + permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); + } break; case ColorChangingEffects_5: permanent.getColor(game).setColor(ObjectColor.GREEN); diff --git a/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java b/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java new file mode 100644 index 00000000000..f09e409f149 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java @@ -0,0 +1,146 @@ +package mage.cards.l; + +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.keyword.PartnerAbility; +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.FilterCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class LivioOathswornSentinel extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("another creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public LivioOathswornSentinel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {1}{W}: Choose another target creature. Its controller may exile it with an aegis counter on it. + Ability ability = new SimpleActivatedAbility( + new LivioOathswornSentinelExileEffect(), new ManaCostsImpl("{1}{W}") + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {2}{W}, {T}: Return all exiled cards with aegis counters on them to the battlefield under their owners' control. + ability = new SimpleActivatedAbility(new LivioOathswornSentinelReturnEffect(), new ManaCostsImpl("{2}{W}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private LivioOathswornSentinel(final LivioOathswornSentinel card) { + super(card); + } + + @Override + public LivioOathswornSentinel copy() { + return new LivioOathswornSentinel(this); + } +} + +class LivioOathswornSentinelExileEffect extends OneShotEffect { + + LivioOathswornSentinelExileEffect() { + super(Outcome.Exile); + staticText = "choose another target creature. Its controller may exile it with an aegis counter on it"; + } + + private LivioOathswornSentinelExileEffect(final LivioOathswornSentinelExileEffect effect) { + super(effect); + } + + @Override + public LivioOathswornSentinelExileEffect copy() { + return new LivioOathswornSentinelExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + Player player = game.getPlayer(permanent.getControllerId()); + if (player == null || !player.chooseUse( + outcome, "Exile " + permanent.getName() + " with an aegis counter on it?", source, game + )) { + return false; + } + Card card = game.getCard(permanent.getId()); + player.moveCards(permanent, Zone.EXILED, source, game); + if (card == null || game.getState().getZone(card.getId()) != Zone.EXILED) { + return false; + } + card.addCounters(CounterType.AEGIS.createInstance(), source, game); + return true; + } +} + +class LivioOathswornSentinelReturnEffect extends OneShotEffect { + + LivioOathswornSentinelReturnEffect() { + super(Outcome.Benefit); + staticText = "return all exiled cards with aegis counters on them to the battlefield under their owners' control"; + } + + private LivioOathswornSentinelReturnEffect(final LivioOathswornSentinelReturnEffect effect) { + super(effect); + } + + @Override + public LivioOathswornSentinelReturnEffect copy() { + return new LivioOathswornSentinelReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Set cards = game + .getExile() + .getAllCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> card.getCounters(game).containsKey(CounterType.AEGIS)) + .collect(Collectors.toSet()); + return player.moveCards( + cards, Zone.BATTLEFIELD, source, game, false, + false, true, null + ); + } +} diff --git a/Mage.Sets/src/mage/cards/l/Lobotomy.java b/Mage.Sets/src/mage/cards/l/Lobotomy.java index fd202a31717..48f20996013 100644 --- a/Mage.Sets/src/mage/cards/l/Lobotomy.java +++ b/Mage.Sets/src/mage/cards/l/Lobotomy.java @@ -1,7 +1,5 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -18,9 +16,11 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPlayer; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class Lobotomy extends CardImpl { @@ -83,8 +83,8 @@ class LobotomyEffect extends OneShotEffect { FilterCard filterNamedCards = new FilterCard(); String nameToSearch = "---";// so no card matches if (chosenCard != null) { - nameToSearch = chosenCard.isSplitCard() ? ((SplitCard) chosenCard).getLeftHalfCard().getName() : chosenCard.getName(); - filterNamedCards.setMessage("cards named " + chosenCard.getName()); + nameToSearch = CardUtil.getCardNameForSameNameSearch(chosenCard); + filterNamedCards.setMessage("cards named " + nameToSearch); } filterNamedCards.add(new NamePredicate(nameToSearch)); Cards cardsToExile = new CardsImpl(); diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheMoon.java b/Mage.Sets/src/mage/cards/m/MagusOfTheMoon.java index d68dba2bd48..cd52cc84c36 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheMoon.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheMoon.java @@ -78,7 +78,7 @@ public final class MagusOfTheMoon extends CardImpl { // So the ability removing has to be done before Layer 6 land.removeAllAbilities(source.getSourceId(), game); land.getSubtype(game).removeAll(SubType.getLandTypes()); - land.getSubtype(game).add(SubType.MOUNTAIN); + land.addSubType(game, SubType.MOUNTAIN); // Mountains have the red mana ability intrinsically so the ability must be added in this layer land.addAbility(new RedManaAbility(), source.getSourceId(), game); break; diff --git a/Mage.Sets/src/mage/cards/m/MakindiMesas.java b/Mage.Sets/src/mage/cards/m/MakindiMesas.java deleted file mode 100644 index 828672d19ee..00000000000 --- a/Mage.Sets/src/mage/cards/m/MakindiMesas.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.m; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.WhiteManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class MakindiMesas extends CardImpl { - - public MakindiMesas(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Makindi Mesas enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {W}. - this.addAbility(new WhiteManaAbility()); - } - - private MakindiMesas(final MakindiMesas card) { - super(card); - } - - @Override - public MakindiMesas copy() { - return new MakindiMesas(this); - } -} diff --git a/Mage.Sets/src/mage/cards/m/MakindiStampede.java b/Mage.Sets/src/mage/cards/m/MakindiStampede.java index 3d0a394eb95..dbc0b79d70d 100644 --- a/Mage.Sets/src/mage/cards/m/MakindiStampede.java +++ b/Mage.Sets/src/mage/cards/m/MakindiStampede.java @@ -1,26 +1,43 @@ package mage.cards.m; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.cards.CardImpl; +import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class MakindiStampede extends CardImpl { +public final class MakindiStampede extends ModalDoubleFacesCard { public MakindiStampede(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{3}{W}{W}", + "Makindi Mesas", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.m.MakindiMesas.class; + // 1. + // Makindi Stampede + // Sorcery // Creatures you control get +2/+2 until end of turn. - this.getSpellAbility().addEffect(new BoostControlledEffect(2, 2, Duration.EndOfTurn)); + this.getLeftHalfCard().getSpellAbility().addEffect(new BoostControlledEffect(2, 2, Duration.EndOfTurn)); + + // 2. + // Makindi Mesas + // Land + + // Makindi Mesas enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {W}. + this.getRightHalfCard().addAbility(new WhiteManaAbility()); } private MakindiStampede(final MakindiStampede card) { diff --git a/Mage.Sets/src/mage/cards/m/MalakirMire.java b/Mage.Sets/src/mage/cards/m/MalakirMire.java deleted file mode 100644 index 03e5ac46d31..00000000000 --- a/Mage.Sets/src/mage/cards/m/MalakirMire.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.m; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.BlackManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class MalakirMire extends CardImpl { - - public MalakirMire(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Malakir Mire enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {B}. - this.addAbility(new BlackManaAbility()); - } - - private MalakirMire(final MalakirMire card) { - super(card); - } - - @Override - public MalakirMire copy() { - return new MalakirMire(this); - } -} diff --git a/Mage.Sets/src/mage/cards/m/MalakirRebirth.java b/Mage.Sets/src/mage/cards/m/MalakirRebirth.java index 11b8634846a..f0479e09252 100644 --- a/Mage.Sets/src/mage/cards/m/MalakirRebirth.java +++ b/Mage.Sets/src/mage/cards/m/MalakirRebirth.java @@ -1,35 +1,52 @@ package mage.cards.m; -import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.cards.CardImpl; +import mage.abilities.mana.BlackManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; +import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class MalakirRebirth extends CardImpl { +public final class MalakirRebirth extends ModalDoubleFacesCard { public MalakirRebirth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{B}", + "Malakir Mire", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.m.MalakirMire.class; + // 1. + // Malakir Rebirth + // Instant // Choose target creature. You lose 2 life. Until end of turn, that creature gains "When this creature dies, return it to the battlefield tapped under its owner's control." - this.getSpellAbility().addEffect(new MalakirRebirthEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getLeftHalfCard().getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(2) + .setText("Choose target creature. You lose 2 life")); + this.getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityTargetEffect(new DiesSourceTriggeredAbility( + new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false + ), Duration.EndOfTurn).setText("Until end of turn, that creature gains \"When this creature dies, return it to the battlefield tapped under its owner's control.\"")); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // 2. + // Malakir Mire + // Land + + // Malakir Mire enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {B}. + this.getRightHalfCard().addAbility(new BlackManaAbility()); } private MalakirRebirth(final MalakirRebirth card) { @@ -40,31 +57,4 @@ public final class MalakirRebirth extends CardImpl { public MalakirRebirth copy() { return new MalakirRebirth(this); } -} - -class MalakirRebirthEffect extends OneShotEffect { - - MalakirRebirthEffect() { - super(Outcome.Benefit); - staticText = "Choose target creature. You lose 2 life. Until end of turn, " + - "that creature gains \"When this creature dies, return it to the battlefield tapped under its owner's control.\""; - } - - private MalakirRebirthEffect(final MalakirRebirthEffect effect) { - super(effect); - } - - @Override - public MalakirRebirthEffect copy() { - return new MalakirRebirthEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addEffect(new GainAbilityTargetEffect(new DiesSourceTriggeredAbility( - new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false - ), Duration.EndOfTurn), source); - Player player = game.getPlayer(source.getControllerId()); - return player != null && player.loseLife(2, game, false) > 0; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/ManaEchoes.java b/Mage.Sets/src/mage/cards/m/ManaEchoes.java index 0e266ad9251..d879b3fabf7 100644 --- a/Mage.Sets/src/mage/cards/m/ManaEchoes.java +++ b/Mage.Sets/src/mage/cards/m/ManaEchoes.java @@ -65,7 +65,7 @@ class ManaEchoesEffect extends OneShotEffect { if (controller != null && permanent != null) { int foundCreatures = 0; for (Permanent perm : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { - if (permanent.shareSubtypes(perm, game)) { + if (permanent.shareCreatureTypes(perm, game)) { foundCreatures++; } } diff --git a/Mage.Sets/src/mage/cards/m/MephidrossVampire.java b/Mage.Sets/src/mage/cards/m/MephidrossVampire.java index e782a5f8889..06b7ad63581 100644 --- a/Mage.Sets/src/mage/cards/m/MephidrossVampire.java +++ b/Mage.Sets/src/mage/cards/m/MephidrossVampire.java @@ -86,9 +86,7 @@ class MephidrossVampireEffect extends ContinuousEffectImpl { creature.addAbility(new DealsDamageToACreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, false, false), source.getSourceId(), game); break; case TypeChangingEffects_4: - if (!creature.hasSubtype(SubType.VAMPIRE, game)) { - creature.getSubtype(game).add(SubType.VAMPIRE); - } + creature.addSubType(game, SubType.VAMPIRE); break; } } diff --git a/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java b/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java index ee046ca105b..235d4ce49e4 100644 --- a/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java +++ b/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java @@ -74,20 +74,21 @@ class ChooseACreature extends OneShotEffect { if (sourceObject == null) { sourceObject = game.getObject(source.getSourceId()); } - if (controller != null - && sourceObject != null) { - Target target = new TargetCreaturePermanent(); - target.setNotTarget(true); - if (target.canChoose(source.getSourceId(), controller.getId(), game)) { - controller.choose(Outcome.Copy, target, source.getSourceId(), game); - Permanent chosenPermanent = game.getPermanent(target.getFirstTarget()); - if (chosenPermanent != null) { - game.getState().setValue(source.getSourceId().toString() + INFO_KEY, chosenPermanent.copy()); - } - } + if (controller == null + || sourceObject == null) { + return false; + } + Target target = new TargetCreaturePermanent(); + target.setNotTarget(true); + if (!target.canChoose(source.getSourceId(), controller.getId(), game)) { return true; } - return false; + controller.choose(Outcome.Copy, target, source.getSourceId(), game); + Permanent chosenPermanent = game.getPermanent(target.getFirstTarget()); + if (chosenPermanent != null) { + game.getState().setValue(source.getSourceId().toString() + INFO_KEY, chosenPermanent.copy()); + } + return true; } @Override @@ -108,46 +109,41 @@ class MetamorphicAlterationEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { Permanent enchantment = game.getPermanent(source.getSourceId()); Permanent copied = (Permanent) game.getState().getValue(source.getSourceId().toString() + ChooseACreature.INFO_KEY); - if (enchantment != null - && copied != null) { - Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); - if (permanent != null - && layer == Layer.CopyEffects_1) { - permanent.setName(copied.getName()); - permanent.getManaCost().clear(); - permanent.getManaCost().addAll(copied.getManaCost()); - permanent.setExpansionSetCode(copied.getExpansionSetCode()); - permanent.getSuperType().clear(); - for (SuperType t : copied.getSuperType()) { - permanent.addSuperType(t); - } - permanent.getCardType().clear(); - for (CardType cardType : copied.getCardType()) { - permanent.addCardType(cardType); - } - permanent.getSubtype(game).retainAll(SubType.getLandTypes()); - for (SubType subType : copied.getSubtype(game)) { - permanent.getSubtype(game).add(subType); - } - permanent.getColor(game).setColor(copied.getColor(game)); - permanent.removeAllAbilities(source.getSourceId(), game); - for (Ability ability : copied.getAbilities()) { - permanent.addAbility(ability, source.getSourceId(), game); - } - permanent.getPower().setValue(copied.getPower().getBaseValue()); - permanent.getToughness().setValue(copied.getToughness().getBaseValue()); - return true; - } + if (enchantment == null + || copied == null) { + return false; } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; + Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); + if (permanent == null) { + return false; + } + permanent.setName(copied.getName()); + permanent.getManaCost().clear(); + permanent.getManaCost().addAll(copied.getManaCost()); + permanent.setExpansionSetCode(copied.getExpansionSetCode()); + permanent.getSuperType().clear(); + for (SuperType t : copied.getSuperType()) { + permanent.addSuperType(t); + } + permanent.getCardType().clear(); + for (CardType cardType : copied.getCardType()) { + permanent.addCardType(cardType); + } + permanent.removeAllSubTypes(game); + permanent.setIsAllCreatureTypes(copied.isAllCreatureTypes()); + permanent.getSubtype(game).addAll(copied.getSubtype(game)); + permanent.setIsAllCreatureTypes(copied.isAllCreatureTypes()); + permanent.getColor(game).setColor(copied.getColor(game)); + permanent.removeAllAbilities(source.getSourceId(), game); + for (Ability ability : copied.getAbilities()) { + permanent.addAbility(ability, source.getSourceId(), game); + } + permanent.getPower().setValue(copied.getPower().getBaseValue()); + permanent.getToughness().setValue(copied.getToughness().getBaseValue()); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/m/MeteoricMace.java b/Mage.Sets/src/mage/cards/m/MeteoricMace.java new file mode 100644 index 00000000000..4ec48967f2e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MeteoricMace.java @@ -0,0 +1,50 @@ +package mage.cards.m; + +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.CascadeAbility; +import mage.abilities.keyword.EquipAbility; +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 MeteoricMace extends CardImpl { + + public MeteoricMace(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}{R}{R}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature gets +4/+0 and has trample. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(4, 0)); + ability.addEffect(new GainAbilityAttachedEffect( + TrampleAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and has trample")); + this.addAbility(ability); + + // Equip {4} + this.addAbility(new EquipAbility(4)); + + // Cascade + this.addAbility(new CascadeAbility()); + } + + private MeteoricMace(final MeteoricMace card) { + super(card); + } + + @Override + public MeteoricMace copy() { + return new MeteoricMace(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/Mindreaver.java b/Mage.Sets/src/mage/cards/m/Mindreaver.java index 83def79c22d..f2fda1722ed 100644 --- a/Mage.Sets/src/mage/cards/m/Mindreaver.java +++ b/Mage.Sets/src/mage/cards/m/Mindreaver.java @@ -1,9 +1,5 @@ - package mage.cards.m; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -16,30 +12,30 @@ import mage.abilities.keyword.HeroicAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.SplitCard; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.SpellAbilityType; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterSpell; import mage.filter.predicate.Predicate; import mage.game.ExileZone; import mage.game.Game; -import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetPlayer; import mage.target.TargetSpell; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Mindreaver extends CardImpl { public Mindreaver(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.HUMAN); this.subtype.add(SubType.WIZARD); @@ -121,16 +117,7 @@ class MindreaverNamePredicate implements Predicate { cardNames.add(card.getName()); } } - // If a player names a card, the player may name either half of a split card, but not both. - // A split card has the chosen name if one of its two names matches the chosen name. - if (input instanceof SplitCard) { - return cardNames.contains(((SplitCard) input).getLeftHalfCard().getName()) || cardNames.contains(((SplitCard) input).getRightHalfCard().getName()); - } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { - SplitCard card = (SplitCard) ((Spell) input).getCard(); - return cardNames.contains(card.getLeftHalfCard().getName()) || cardNames.contains(card.getRightHalfCard().getName()); - } else { - return cardNames.contains(input.getName()); - } + return cardNames.stream().anyMatch(needName -> CardUtil.haveSameNames(input, needName, game)); } @Override diff --git a/Mage.Sets/src/mage/cards/m/MirrorEntity.java b/Mage.Sets/src/mage/cards/m/MirrorEntity.java index 9174db87c9e..8ea0f5324b6 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorEntity.java +++ b/Mage.Sets/src/mage/cards/m/MirrorEntity.java @@ -1,52 +1,46 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.VariableManaCost; -import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.ManacostVariableValue; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.SetPowerToughnessAllEffect; import mage.abilities.keyword.ChangelingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.SubLayer; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.Iterator; +import java.util.UUID; /** - * * @author Plopman */ public final class MirrorEntity extends CardImpl { - static private FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Creatures you control"); - public MirrorEntity(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.SHAPESHIFTER); this.power = new MageInt(1); this.toughness = new MageInt(1); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); + // {X}: Until end of turn, creatures you control have base power and toughness X/X and gain all creature types. - DynamicValue variableMana = ManacostVariableValue.instance; - Effect effect = new SetPowerToughnessAllEffect(variableMana, variableMana, Duration.EndOfTurn, filter, true); - effect.setText("Until end of turn, creatures you control have base power and toughness X/X"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new VariableManaCost()); - effect = new GainAbilityAllEffect(ChangelingAbility.getInstance(), Duration.EndOfTurn, filter, false, Layer.TypeChangingEffects_4, SubLayer.NA); - effect.setText("and gain all creature types"); - ability.addEffect(effect); + Ability ability = new SimpleActivatedAbility(new SetPowerToughnessAllEffect( + ManacostVariableValue.instance, ManacostVariableValue.instance, + Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, true + ).setText("Until end of turn, creatures you control have base power and toughness X/X"), new VariableManaCost()); + ability.addEffect(new MirrorEntityEffect()); this.addAbility(ability); } @@ -59,3 +53,43 @@ public final class MirrorEntity extends CardImpl { return new MirrorEntity(this); } } + +class MirrorEntityEffect extends ContinuousEffectImpl { + + MirrorEntityEffect() { + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); + staticText = "and gain all creature types"; + } + + private MirrorEntityEffect(final MirrorEntityEffect effect) { + super(effect); + } + + @Override + public MirrorEntityEffect copy() { + return new MirrorEntityEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + for (Permanent perm : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURES, source.getControllerId(), source.getSourceId(), game + )) { + affectedObjectList.add(new MageObjectReference(perm, 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(); // no longer on the battlefield, remove reference to object + continue; + } + permanent.setIsAllCreatureTypes(true); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MistformUltimus.java b/Mage.Sets/src/mage/cards/m/MistformUltimus.java index 375a434a355..d4624294a7b 100644 --- a/Mage.Sets/src/mage/cards/m/MistformUltimus.java +++ b/Mage.Sets/src/mage/cards/m/MistformUltimus.java @@ -1,31 +1,35 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; -import mage.abilities.keyword.ChangelingAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.InfoEffect; 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 java.util.UUID; /** - * * @author vereena42 */ public final class MistformUltimus extends CardImpl { public MistformUltimus(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); - + this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ILLUSION); this.power = new MageInt(3); this.toughness = new MageInt(3); // Mistform Ultimus is every creature type. - this.addAbility(ChangelingAbility.getInstance()); + this.setIsAllCreatureTypes(true); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect( + "{this} is every creature type (even if this card isn't on the battlefield)." + ))); } public MistformUltimus(final MistformUltimus card) { diff --git a/Mage.Sets/src/mage/cards/m/MnemonicDeluge.java b/Mage.Sets/src/mage/cards/m/MnemonicDeluge.java new file mode 100644 index 00000000000..c66bd0d9dc4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MnemonicDeluge.java @@ -0,0 +1,91 @@ +package mage.cards.m; + +import mage.ApprovingObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSpellEffect; +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.TargetCardInGraveyard; +import org.apache.log4j.Logger; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MnemonicDeluge extends CardImpl { + + public MnemonicDeluge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{U}{U}{U}"); + + // Exile target instant or sorcery card from a graveyard. Copy that card three times. You may cast the copies without paying their mana costs. Exile Mnemonic Deluge. + this.getSpellAbility().addEffect(new MnemonicDelugeEffect()); + this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + this.getSpellAbility().addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY)); + } + + private MnemonicDeluge(final MnemonicDeluge card) { + super(card); + } + + @Override + public MnemonicDeluge copy() { + return new MnemonicDeluge(this); + } +} + +class MnemonicDelugeEffect extends OneShotEffect { + + MnemonicDelugeEffect() { + super(Outcome.Benefit); + staticText = "Exile target instant or sorcery card from a graveyard. " + + "Copy that card three times. You may cast the copies without paying their mana costs."; + } + + private MnemonicDelugeEffect(final MnemonicDelugeEffect effect) { + super(effect); + } + + @Override + public MnemonicDelugeEffect copy() { + return new MnemonicDelugeEffect(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); + Cards cards = new CardsImpl(); + for (int i = 0; i < 3; i++) { + Card copiedCard = game.copyCard(card, source, source.getControllerId()); + game.getExile().add(source.getSourceId(), "", copiedCard); + game.getState().setZone(copiedCard.getId(), Zone.EXILED); + cards.add(copiedCard); + } + for (Card copiedCard : cards.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(MnemonicDelugeEffect.class).error("Mnemonic Deluge: " + + "spell ability == null " + copiedCard.getName()); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoongloveChangeling.java b/Mage.Sets/src/mage/cards/m/MoongloveChangeling.java index ef32aee047a..f7115243c58 100644 --- a/Mage.Sets/src/mage/cards/m/MoongloveChangeling.java +++ b/Mage.Sets/src/mage/cards/m/MoongloveChangeling.java @@ -24,6 +24,7 @@ public final class MoongloveChangeling extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn), new ColoredManaCost(ColoredManaSymbol.B))); } diff --git a/Mage.Sets/src/mage/cards/m/MorophonTheBoundless.java b/Mage.Sets/src/mage/cards/m/MorophonTheBoundless.java index c8064c745c5..b18b28ea8ce 100644 --- a/Mage.Sets/src/mage/cards/m/MorophonTheBoundless.java +++ b/Mage.Sets/src/mage/cards/m/MorophonTheBoundless.java @@ -40,6 +40,7 @@ public final class MorophonTheBoundless extends CardImpl { this.toughness = new MageInt(6); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // As Morophon, the Boundless enters the battlefield, choose a creature type. diff --git a/Mage.Sets/src/mage/cards/m/MothdustChangeling.java b/Mage.Sets/src/mage/cards/m/MothdustChangeling.java index 6f276339b1e..c6f60f80ffb 100644 --- a/Mage.Sets/src/mage/cards/m/MothdustChangeling.java +++ b/Mage.Sets/src/mage/cards/m/MothdustChangeling.java @@ -36,6 +36,7 @@ public final class MothdustChangeling extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), new TapTargetCost(new TargetControlledPermanent(filter)))); } diff --git a/Mage.Sets/src/mage/cards/m/MurkwaterPathway.java b/Mage.Sets/src/mage/cards/m/MurkwaterPathway.java deleted file mode 100644 index f3c88906826..00000000000 --- a/Mage.Sets/src/mage/cards/m/MurkwaterPathway.java +++ /dev/null @@ -1,33 +0,0 @@ -package mage.cards.m; - -import mage.abilities.mana.BlackManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class MurkwaterPathway extends CardImpl { - - public MurkwaterPathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // {T}: Add {B}. - this.addAbility(new BlackManaAbility()); - } - - private MurkwaterPathway(final MurkwaterPathway card) { - super(card); - } - - @Override - public MurkwaterPathway copy() { - return new MurkwaterPathway(this); - } -} diff --git a/Mage.Sets/src/mage/cards/n/NamelessInversion.java b/Mage.Sets/src/mage/cards/n/NamelessInversion.java index 8ad30fe242f..7191a28e74d 100644 --- a/Mage.Sets/src/mage/cards/n/NamelessInversion.java +++ b/Mage.Sets/src/mage/cards/n/NamelessInversion.java @@ -25,6 +25,7 @@ public final class NamelessInversion extends CardImpl { // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Target creature gets +3/-3 and loses all creature types until end of turn. diff --git a/Mage.Sets/src/mage/cards/n/NaturalReclamation.java b/Mage.Sets/src/mage/cards/n/NaturalReclamation.java new file mode 100644 index 00000000000..1965c296df2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NaturalReclamation.java @@ -0,0 +1,37 @@ +package mage.cards.n; + +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.CascadeAbility; +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 NaturalReclamation extends CardImpl { + + public NaturalReclamation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{G}"); + + // Cascade + this.addAbility(new CascadeAbility()); + + // Destroy target artifact or enchantment. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + } + + private NaturalReclamation(final NaturalReclamation card) { + super(card); + } + + @Override + public NaturalReclamation copy() { + return new NaturalReclamation(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/Necromancy.java b/Mage.Sets/src/mage/cards/n/Necromancy.java index 50d87e1e836..d0522b0c225 100644 --- a/Mage.Sets/src/mage/cards/n/Necromancy.java +++ b/Mage.Sets/src/mage/cards/n/Necromancy.java @@ -1,7 +1,6 @@ package mage.cards.n; -import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -29,8 +28,9 @@ import mage.target.Target; import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Necromancy extends CardImpl { @@ -175,18 +175,12 @@ class NecromancyChangeAbilityEffect extends ContinuousEffectImpl implements Sour if (permanent != null) { switch (layer) { case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (!permanent.hasSubtype(SubType.AURA, game)) { - permanent.getSubtype(game).add(SubType.AURA); - } - } + permanent.addSubType(game, SubType.AURA); break; case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - permanent.addAbility(newAbility, source.getSourceId(), game); - permanent.getSpellAbility().getTargets().clear(); - permanent.getSpellAbility().getTargets().add(target); - } + permanent.addAbility(newAbility, source.getSourceId(), game); + permanent.getSpellAbility().getTargets().clear(); + permanent.getSpellAbility().getTargets().add(target); } return true; } diff --git a/Mage.Sets/src/mage/cards/n/NeedlevergePathway.java b/Mage.Sets/src/mage/cards/n/NeedlevergePathway.java index 8371bafe285..44497d275fc 100644 --- a/Mage.Sets/src/mage/cards/n/NeedlevergePathway.java +++ b/Mage.Sets/src/mage/cards/n/NeedlevergePathway.java @@ -1,25 +1,39 @@ package mage.cards.n; import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class NeedlevergePathway extends CardImpl { +public final class NeedlevergePathway extends ModalDoubleFacesCard { public NeedlevergePathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + super(ownerId, setInfo, + new CardType[]{CardType.LAND}, new SubType[]{}, "", + "Pillarverge Pathway", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.p.PillarvergePathway.class; + // 1. + // Needleverge Pathway + // Land // {T}: Add {R}. - this.addAbility(new RedManaAbility()); + this.getLeftHalfCard().addAbility(new RedManaAbility()); + + // 2. + // Pillarverge Pathway + // Land + + // {T}: Add {W}. + this.getRightHalfCard().addAbility(new WhiteManaAbility()); } private NeedlevergePathway(final NeedlevergePathway card) { diff --git a/Mage.Sets/src/mage/cards/n/NewBlood.java b/Mage.Sets/src/mage/cards/n/NewBlood.java index 4361a19ef0d..49cf5eea454 100644 --- a/Mage.Sets/src/mage/cards/n/NewBlood.java +++ b/Mage.Sets/src/mage/cards/n/NewBlood.java @@ -152,7 +152,7 @@ class ChangeCreatureTypeTargetEffect extends ContinuousEffectImpl { if (targetObject.hasSubtype(fromSubType, game)) { targetObject.getSubtype(game).remove(fromSubType); if (!targetObject.hasSubtype(toSubType, game)) { - targetObject.getSubtype(game).add(toSubType); + targetObject.addSubType(game, toSubType); } } break; diff --git a/Mage.Sets/src/mage/cards/n/NimDeathmantle.java b/Mage.Sets/src/mage/cards/n/NimDeathmantle.java index 6198990341e..3e0dac45738 100644 --- a/Mage.Sets/src/mage/cards/n/NimDeathmantle.java +++ b/Mage.Sets/src/mage/cards/n/NimDeathmantle.java @@ -39,17 +39,23 @@ public final class NimDeathmantle extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +2/+2, has intimidate, and is a black Zombie. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 2))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(IntimidateAbility.getInstance(), AttachmentType.EQUIPMENT))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SetCardColorAttachedEffect(ObjectColor.BLACK, Duration.WhileOnBattlefield, AttachmentType.EQUIPMENT))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SetCardSubtypeAttachedEffect(Duration.WhileOnBattlefield, AttachmentType.EQUIPMENT, SubType.ZOMBIE))); + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 2)); + ability.addEffect(new GainAbilityAttachedEffect( + IntimidateAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText(", has intimidate")); + ability.addEffect(new SetCardColorAttachedEffect( + ObjectColor.BLACK, Duration.WhileOnBattlefield, AttachmentType.EQUIPMENT + ).setText(", and is")); + ability.addEffect(new SetCardSubtypeAttachedEffect( + Duration.WhileOnBattlefield, AttachmentType.EQUIPMENT, SubType.ZOMBIE + ).setText("black Zombie").concatBy("a")); + this.addAbility(ability); // Whenever a nontoken creature is put into your graveyard from the battlefield, you may pay {4}. If you do, return that card to the battlefield and attach Nim Deathmantle to it. this.addAbility(new NimDeathmantleTriggeredAbility()); // Equip {4} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(4))); - } public NimDeathmantle(final NimDeathmantle card) { @@ -93,7 +99,7 @@ class NimDeathmantleTriggeredAbility extends TriggeredAbilityImpl { && !(permanent instanceof PermanentToken) && permanent.isCreature()) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game) +1)); + getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game) + 1)); return true; } return false; @@ -124,8 +130,8 @@ class NimDeathmantleEffect extends OneShotEffect { Permanent equipment = game.getPermanent(source.getSourceId()); if (controller != null && equipment != null) { if (controller.chooseUse(Outcome.Benefit, equipment.getName() + " - Pay " + cost.getText() + '?', source, game)) { - cost.clearPaid(); - if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { + cost.clearPaid(); + if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { UUID target = targetPointer.getFirst(game, source); if (target != null) { Card card = game.getCard(target); diff --git a/Mage.Sets/src/mage/cards/n/NumaJoragaChieftain.java b/Mage.Sets/src/mage/cards/n/NumaJoragaChieftain.java new file mode 100644 index 00000000000..92ee8d9e3be --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NumaJoragaChieftain.java @@ -0,0 +1,98 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.DistributeCountersEffect; +import mage.abilities.keyword.PartnerAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanentAmount; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NumaJoragaChieftain extends CardImpl { + + public NumaJoragaChieftain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // At the beginning of combat on your turn, you may pay {X}{X}. When you do, distribute X +1/+1 counters among any number of target Elves. + this.addAbility(new BeginningOfCombatTriggeredAbility( + new NumaJoragaChieftainEffect(), TargetController.YOU, false + )); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private NumaJoragaChieftain(final NumaJoragaChieftain card) { + super(card); + } + + @Override + public NumaJoragaChieftain copy() { + return new NumaJoragaChieftain(this); + } +} + +class NumaJoragaChieftainEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent(SubType.ELF, "Elves"); + + NumaJoragaChieftainEffect() { + super(Outcome.Benefit); + staticText = "you may pay {X}{X}. When you do, distribute X +1/+1 counters among any number of target Elves"; + } + + private NumaJoragaChieftainEffect(final NumaJoragaChieftainEffect effect) { + super(effect); + } + + @Override + public NumaJoragaChieftainEffect copy() { + return new NumaJoragaChieftainEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + ManaCosts cost = new ManaCostsImpl("{X}{X}"); + if (!player.chooseUse(Outcome.BoostCreature, "Pay " + cost.getText() + "?", source, game)) { + return false; + } + int costX = player.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source); + cost.add(new GenericManaCost(2 * costX)); + if (!cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { + return false; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DistributeCountersEffect(CounterType.P1P1, costX, false, ""), + false, "distribute " + costX + " +1/+1 counters among any number of target Elves" + ); + ability.addTarget(new TargetCreaturePermanentAmount(costX, filter)); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NyleasPresence.java b/Mage.Sets/src/mage/cards/n/NyleasPresence.java index 14cb39ed093..406fb52f5cd 100644 --- a/Mage.Sets/src/mage/cards/n/NyleasPresence.java +++ b/Mage.Sets/src/mage/cards/n/NyleasPresence.java @@ -1,6 +1,5 @@ package mage.cards.n; -import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -17,8 +16,6 @@ import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetLandPermanent; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; /** @@ -30,18 +27,18 @@ public final class NyleasPresence extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); this.subtype.add(SubType.AURA); - // Enchant land TargetPermanent auraTarget = new TargetLandPermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); + // When Nylea's Presence enters the battlefield, draw a card. this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); - // Enchanted land is every basic land type in addition to its other types. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NyleasPresenceLandTypeEffect())); + // Enchanted land is every basic land type in addition to its other types. + this.addAbility(new SimpleStaticAbility(new NyleasPresenceLandTypeEffect())); } public NyleasPresence(final NyleasPresence card) { @@ -56,22 +53,18 @@ public final class NyleasPresence extends CardImpl { class NyleasPresenceLandTypeEffect extends ContinuousEffectImpl { - protected List landTypes = new ArrayList<>(); - public NyleasPresenceLandTypeEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); - landTypes.addAll(SubType.getBasicLands()); + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); this.staticText = "Enchanted land is every basic land type in addition to its other types"; + dependencyTypes.add(DependencyType.BecomePlains); + dependencyTypes.add(DependencyType.BecomeIsland); + dependencyTypes.add(DependencyType.BecomeSwamp); + dependencyTypes.add(DependencyType.BecomeMountain); + dependencyTypes.add(DependencyType.BecomeForest); } public NyleasPresenceLandTypeEffect(final NyleasPresenceLandTypeEffect effect) { super(effect); - this.landTypes.addAll(effect.landTypes); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; } @Override @@ -80,52 +73,22 @@ class NyleasPresenceLandTypeEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Permanent land = game.getPermanent(enchantment.getAttachedTo()); - if (land != null) { - switch (layer) { - case AbilityAddingRemovingEffects_6: - Mana mana = new Mana(); - for (Ability ability : land.getAbilities()) { - if (ability instanceof BasicManaAbility) { - for (Mana netMana : ((BasicManaAbility) ability).getNetMana(game)) { - mana.add(netMana); - } - } - } - if (mana.getGreen() == 0 && landTypes.contains(SubType.FOREST)) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (mana.getRed() == 0 && landTypes.contains(SubType.MOUNTAIN)) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (mana.getBlue() == 0 && landTypes.contains(SubType.ISLAND)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (mana.getWhite() == 0 && landTypes.contains(SubType.PLAINS)) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (mana.getBlack() == 0 && landTypes.contains(SubType.SWAMP)) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - break; - case TypeChangingEffects_4: - for (SubType subtype : landTypes) { - if (!land.hasSubtype(subtype, game)) { - land.getSubtype(game).add(subtype); - } - } - break; - } - } + if (enchantment == null || enchantment.getAttachedTo() == null) { + return true; } + Permanent land = game.getPermanent(enchantment.getAttachedTo()); + if (land == null) { + return true; + } + land.getSubtype(game).removeAll(SubType.getBasicLands()); + land.getSubtype(game).addAll(SubType.getBasicLands()); + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); return true; } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; - } } diff --git a/Mage.Sets/src/mage/cards/n/NymrisOonasTrickster.java b/Mage.Sets/src/mage/cards/n/NymrisOonasTrickster.java new file mode 100644 index 00000000000..424adf5aea2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NymrisOonasTrickster.java @@ -0,0 +1,56 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.common.FirstSpellOpponentsTurnTriggeredAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +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.constants.SuperType; +import mage.constants.Zone; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NymrisOonasTrickster extends CardImpl { + + public NymrisOonasTrickster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.FAERIE); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(1); + this.toughness = new MageInt(6); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever you cast your first spell during each opponent's turn, look at the top two cards of your library. Put one of those cards into your hand and the other into your graveyard. + this.addAbility(new FirstSpellOpponentsTurnTriggeredAbility( + new LookLibraryAndPickControllerEffect( + StaticValue.get(2), false, StaticValue.get(1), + StaticFilters.FILTER_CARD, Zone.GRAVEYARD, false, false + ), false + )); + } + + private NymrisOonasTrickster(final NymrisOonasTrickster card) { + super(card); + } + + @Override + public NymrisOonasTrickster copy() { + return new NymrisOonasTrickster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/ObekaBruteChronologist.java b/Mage.Sets/src/mage/cards/o/ObekaBruteChronologist.java new file mode 100644 index 00000000000..11023d4cb29 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/ObekaBruteChronologist.java @@ -0,0 +1,70 @@ +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.effects.OneShotEffect; +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.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ObekaBruteChronologist extends CardImpl { + + public ObekaBruteChronologist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.OGRE); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // {T}: The player whose turn it is may end the turn. + this.addAbility(new SimpleActivatedAbility(new ObekaBruteChronologistEffect(), new TapSourceCost())); + } + + private ObekaBruteChronologist(final ObekaBruteChronologist card) { + super(card); + } + + @Override + public ObekaBruteChronologist copy() { + return new ObekaBruteChronologist(this); + } +} + +class ObekaBruteChronologistEffect extends OneShotEffect { + + ObekaBruteChronologistEffect() { + super(Outcome.Benefit); + staticText = "the player whose turn it is may end the turn"; + } + + private ObekaBruteChronologistEffect(final ObekaBruteChronologistEffect effect) { + super(effect); + } + + @Override + public ObekaBruteChronologistEffect copy() { + return new ObekaBruteChronologistEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(game.getActivePlayerId()); + return player != null + && player.chooseUse(outcome, "End the turn?", source, game) + && game.endTurn(source); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OnduInversion.java b/Mage.Sets/src/mage/cards/o/OnduInversion.java index 62c716c341d..c7701723898 100644 --- a/Mage.Sets/src/mage/cards/o/OnduInversion.java +++ b/Mage.Sets/src/mage/cards/o/OnduInversion.java @@ -1,26 +1,44 @@ package mage.cards.o; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.StaticFilters; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class OnduInversion extends CardImpl { +public final class OnduInversion extends ModalDoubleFacesCard { public OnduInversion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{W}{W}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{6}{W}{W}", + "Ondu Skyruins", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.o.OnduInversion.class; + // 1. + // Ondu Inversion + // Sorcery // Destroy all nonland permanents. - this.getSpellAbility().addEffect(new DestroyAllEffect(StaticFilters.FILTER_PERMANENTS_NON_LAND)); + this.getLeftHalfCard().getSpellAbility().addEffect(new DestroyAllEffect(StaticFilters.FILTER_PERMANENTS_NON_LAND)); + + // 2. + // Ondu Skyruins + // Land + + // Ondu Skyruins enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {W}. + this.getRightHalfCard().addAbility(new WhiteManaAbility()); } private OnduInversion(final OnduInversion card) { diff --git a/Mage.Sets/src/mage/cards/o/OnduSkyruins.java b/Mage.Sets/src/mage/cards/o/OnduSkyruins.java deleted file mode 100644 index 58f520c9934..00000000000 --- a/Mage.Sets/src/mage/cards/o/OnduSkyruins.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.o; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.WhiteManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class OnduSkyruins extends CardImpl { - - public OnduSkyruins(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Ondu Skyruins enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {W}. - this.addAbility(new WhiteManaAbility()); - } - - private OnduSkyruins(final OnduSkyruins card) { - super(card); - } - - @Override - public OnduSkyruins copy() { - return new OnduSkyruins(this); - } -} diff --git a/Mage.Sets/src/mage/cards/o/OneWithTheStars.java b/Mage.Sets/src/mage/cards/o/OneWithTheStars.java index 168757c0575..3388b4c1322 100644 --- a/Mage.Sets/src/mage/cards/o/OneWithTheStars.java +++ b/Mage.Sets/src/mage/cards/o/OneWithTheStars.java @@ -87,6 +87,8 @@ class OneWithTheStarsEffect extends ContinuousEffectImpl { if (permanent == null) { return false; } + permanent.setIsAllCreatureTypes(false); + permanent.getSubtype(game).retainAll(SubType.getEnchantmentTypes()); permanent.getCardType().clear(); permanent.addCardType(CardType.ENCHANTMENT); return true; diff --git a/Mage.Sets/src/mage/cards/o/OpalTitan.java b/Mage.Sets/src/mage/cards/o/OpalTitan.java index bbbbc452f79..58ad41edcf5 100644 --- a/Mage.Sets/src/mage/cards/o/OpalTitan.java +++ b/Mage.Sets/src/mage/cards/o/OpalTitan.java @@ -1,6 +1,5 @@ package mage.cards.o; -import java.util.UUID; import mage.MageObjectReference; import mage.ObjectColor; import mage.abilities.Ability; @@ -13,23 +12,16 @@ import mage.abilities.effects.common.continuous.SourceEffect; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.DependencyType; -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.Zone; +import mage.constants.*; import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class OpalTitan extends CardImpl { @@ -98,7 +90,8 @@ class OpalTitanBecomesCreatureEffect extends ContinuousEffectImpl implements Sou if (sublayer == SubLayer.NA) { permanent.getCardType().clear(); permanent.addCardType(CardType.CREATURE); - permanent.getSubtype(game).add(SubType.GIANT); + permanent.removeAllSubTypes(game); + permanent.addSubType(game, SubType.GIANT); } break; case AbilityAddingRemovingEffects_6: diff --git a/Mage.Sets/src/mage/cards/p/PanopticMirror.java b/Mage.Sets/src/mage/cards/p/PanopticMirror.java index c0f6193e1dc..ad2bc5cca5b 100644 --- a/Mage.Sets/src/mage/cards/p/PanopticMirror.java +++ b/Mage.Sets/src/mage/cards/p/PanopticMirror.java @@ -1,6 +1,5 @@ package mage.cards.p; -import java.util.UUID; import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -9,11 +8,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.*; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; @@ -24,8 +19,9 @@ import mage.target.TargetCard; import mage.target.common.TargetCardInHand; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author Plopman */ public final class PanopticMirror extends CardImpl { @@ -83,7 +79,7 @@ class PanopticMirrorExileEffect extends OneShotEffect { } TargetCardInHand target = new TargetCardInHand(filter); - if (player.choose(outcome.PlayForFree, target, source.getSourceId(), game)) { + if (player.choose(Outcome.PlayForFree, target, source.getSourceId(), game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { card.moveToExile(CardUtil.getCardExileZoneId(game, source), "Panoptic Mirror", source.getSourceId(), game); @@ -129,9 +125,12 @@ class PanopticMirrorCastEffect extends OneShotEffect { for (UUID uuid : PanopticMirror.getImprinted()) { Card card = game.getCard(uuid); if (card != null) { - if (card.isSplitCard()) { + if (card instanceof SplitCard) { cards.add(((SplitCard) card).getLeftHalfCard()); cards.add(((SplitCard) card).getRightHalfCard()); + } else if (card instanceof ModalDoubleFacesCard) { + cards.add(((ModalDoubleFacesCard) card).getLeftHalfCard()); + cards.add(((ModalDoubleFacesCard) card).getRightHalfCard()); } else { cards.add(card); } @@ -147,7 +146,7 @@ class PanopticMirrorCastEffect extends OneShotEffect { } if (cardToCopy != null) { Card copy = game.copyCard(cardToCopy, source, source.getControllerId()); - if (controller.chooseUse(outcome.PlayForFree, "Cast the copied card without paying mana cost?", source, game)) { + if (controller.chooseUse(Outcome.PlayForFree, "Cast the copied card without paying mana cost?", source, game)) { game.getState().setValue("PlayFromNotOwnHandZone" + copy.getId(), Boolean.TRUE); controller.cast(controller.chooseAbilityForCast(copy, game, true), game, true, new ApprovingObject(source, game)); diff --git a/Mage.Sets/src/mage/cards/p/ParagonOfTheAmesha.java b/Mage.Sets/src/mage/cards/p/ParagonOfTheAmesha.java index 29e40dd9337..6fe38126354 100644 --- a/Mage.Sets/src/mage/cards/p/ParagonOfTheAmesha.java +++ b/Mage.Sets/src/mage/cards/p/ParagonOfTheAmesha.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -19,14 +17,15 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class ParagonOfTheAmesha extends CardImpl { public ParagonOfTheAmesha(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.KNIGHT); @@ -37,18 +36,17 @@ public final class ParagonOfTheAmesha extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // {W}{U}{B}{R}{G}: Until end of turn, Paragon of the Amesha becomes an Angel, gets +3/+3, and gains flying and lifelink. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ParagonOfTheAmeshaEffect(), new ManaCostsImpl("{W}{U}{B}{R}{G}")); + Ability ability = new SimpleActivatedAbility(new ParagonOfTheAmeshaEffect(), new ManaCostsImpl("{W}{U}{B}{R}{G}")); Effect effect = new BoostSourceEffect(3, 3, Duration.EndOfTurn); - effect.setText("gets +3/+3,"); + effect.setText(", gets +3/+3"); ability.addEffect(effect); effect = new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); - effect.setText("and gains flying"); + effect.setText(", and gains flying"); ability.addEffect(effect); effect = new GainAbilitySourceEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn); effect.setText("and lifelink"); ability.addEffect(effect); this.addAbility(ability); - } public ParagonOfTheAmesha(final ParagonOfTheAmesha card) { @@ -62,12 +60,12 @@ public final class ParagonOfTheAmesha extends CardImpl { private static class ParagonOfTheAmeshaEffect extends ContinuousEffectImpl { - public ParagonOfTheAmeshaEffect() { - super(Duration.EndOfTurn, Outcome.BecomeCreature); - setText(); + private ParagonOfTheAmeshaEffect() { + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature); + staticText = "Until end of turn, {this} becomes an Angel"; } - public ParagonOfTheAmeshaEffect(final ParagonOfTheAmeshaEffect effect) { + private ParagonOfTheAmeshaEffect(final ParagonOfTheAmeshaEffect effect) { super(effect); } @@ -77,34 +75,14 @@ public final class ParagonOfTheAmesha extends CardImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent == null) { return false; } - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getSubtype(game).clear(); - permanent.getSubtype(game).add(SubType.ANGEL); - } - break; - } + permanent.removeAllCreatureTypes(game); + permanent.addSubType(game, SubType.ANGEL); return true; } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - private void setText() { - staticText = "Until end of turn, {this} becomes an Angel, "; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } } } diff --git a/Mage.Sets/src/mage/cards/p/ParallaxTide.java b/Mage.Sets/src/mage/cards/p/ParallaxTide.java index fe86166b114..a1dfb245f51 100644 --- a/Mage.Sets/src/mage/cards/p/ParallaxTide.java +++ b/Mage.Sets/src/mage/cards/p/ParallaxTide.java @@ -24,7 +24,6 @@ public final class ParallaxTide extends CardImpl { public ParallaxTide(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); - // Fading 5 this.addAbility(new FadingAbility(5, this)); @@ -34,7 +33,7 @@ public final class ParallaxTide extends CardImpl { this.addAbility(ability); // When Parallax Tide leaves the battlefield, each player returns to the battlefield all cards they own exiled with Parallax Tide. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), splitCard)); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false)); } public ParallaxTide(final ParallaxTide card) { diff --git a/Mage.Sets/src/mage/cards/p/PathOfAncestry.java b/Mage.Sets/src/mage/cards/p/PathOfAncestry.java index 8b4085a101e..ee0be6c4478 100644 --- a/Mage.Sets/src/mage/cards/p/PathOfAncestry.java +++ b/Mage.Sets/src/mage/cards/p/PathOfAncestry.java @@ -5,23 +5,18 @@ import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.keyword.ScryEffect; -import mage.abilities.keyword.ChangelingAbility; import mage.abilities.mana.CommanderColorIdentityManaAbility; 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.SubTypeSet; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; /** @@ -92,8 +87,7 @@ class PathOfAncestryTriggeredAbility extends DelayedTriggeredAbility { return false; } Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell == null || (spell.getSubtype(game).isEmpty() - && !spell.hasAbility(ChangelingAbility.getInstance(), game))) { + if (spell == null) { return false; } Player player = game.getPlayer(getControllerId()); @@ -101,7 +95,6 @@ class PathOfAncestryTriggeredAbility extends DelayedTriggeredAbility { return false; } boolean isAllA = false; - Set subTypeSet = new HashSet<>(); for (UUID commanderId : game.getCommandersIds(player)) { Card commander = game.getPermanent(commanderId); if (commander == null) { @@ -110,19 +103,11 @@ class PathOfAncestryTriggeredAbility extends DelayedTriggeredAbility { if (commander == null) { continue; } - if (commander.isAllCreatureTypes() - || commander.hasAbility(ChangelingAbility.getInstance(), game)) { - isAllA = true; - break; + if (spell.getCard().shareCreatureTypes(commander, game)) { + return true; } - subTypeSet.addAll(commander.getSubtype(game)); } - subTypeSet.removeIf(subType -> subType.getSubTypeSet() != SubTypeSet.CreatureType); - if (subTypeSet.isEmpty() && !isAllA) { - return false; - } - return spell.hasAbility(ChangelingAbility.getInstance(), game) - || spell.getSubtype(game).stream().anyMatch(subTypeSet::contains); + return false; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PelakkaCaverns.java b/Mage.Sets/src/mage/cards/p/PelakkaCaverns.java deleted file mode 100644 index eab63cde1e6..00000000000 --- a/Mage.Sets/src/mage/cards/p/PelakkaCaverns.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.p; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.BlackManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class PelakkaCaverns extends CardImpl { - - public PelakkaCaverns(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Pelakka Caverns enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {B}. - this.addAbility(new BlackManaAbility()); - } - - private PelakkaCaverns(final PelakkaCaverns card) { - super(card); - } - - @Override - public PelakkaCaverns copy() { - return new PelakkaCaverns(this); - } -} diff --git a/Mage.Sets/src/mage/cards/p/PelakkaPredation.java b/Mage.Sets/src/mage/cards/p/PelakkaPredation.java index a3558d2b69c..9474fb8bc3f 100644 --- a/Mage.Sets/src/mage/cards/p/PelakkaPredation.java +++ b/Mage.Sets/src/mage/cards/p/PelakkaPredation.java @@ -1,10 +1,14 @@ package mage.cards.p; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; +import mage.abilities.mana.BlackManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +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.ConvertedManaCostPredicate; @@ -13,9 +17,9 @@ import mage.target.common.TargetOpponent; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class PelakkaPredation extends CardImpl { +public final class PelakkaPredation extends ModalDoubleFacesCard { private static final FilterCard filter = new FilterCard("a card from it with converted mana cost 3 or greater"); @@ -24,14 +28,28 @@ public final class PelakkaPredation extends CardImpl { } public PelakkaPredation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{2}{B}", + "Pelakka Caverns", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.p.PelakkaCaverns.class; + // 1. + // 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.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter, TargetController.ANY)); - this.getSpellAbility().addTarget(new TargetOpponent()); + this.getLeftHalfCard().getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter, TargetController.ANY)); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetOpponent()); + + // 2. + // Pelakka Caverns + // Land + + // Pelakka Caverns enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {B}. + this.getRightHalfCard().addAbility(new BlackManaAbility()); } private PelakkaPredation(final PelakkaPredation card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalImage.java b/Mage.Sets/src/mage/cards/p/PhantasmalImage.java index 9c0b2db68f8..de0d842757c 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalImage.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalImage.java @@ -30,9 +30,7 @@ public final class PhantasmalImage extends CardImpl { private static final ApplyToPermanent phantasmalImageApplier = new ApplyToPermanent() { @Override public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { - if (!permanent.hasSubtype(SubType.ILLUSION, game)) { - permanent.getSubtype(game).add(SubType.ILLUSION); - } + permanent.addSubType(game, SubType.ILLUSION); // Add directly because the created permanent is only used to copy from, so there is no need to add the ability to e.g. TriggeredAbilities permanent.getAbilities().add(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect())); //permanent.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), game); @@ -41,9 +39,7 @@ public final class PhantasmalImage extends CardImpl { @Override public boolean apply(Game game, MageObject mageObject, Ability source, UUID copyToObjectId) { - if (!mageObject.hasSubtype(SubType.ILLUSION, game)) { - mageObject.getSubtype(game).add(SubType.ILLUSION); - } + mageObject.addSubType(game, SubType.ILLUSION); // Add directly because the created permanent is only used to copy from, so there is no need to add the ability to e.g. TriggeredAbilities mageObject.getAbilities().add(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect())); //permanent.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), game); diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java b/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java index 76b23c0cae3..17bfef4703c 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java @@ -1,6 +1,5 @@ package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; @@ -8,28 +7,18 @@ import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ChooseBasicLandTypeEffect; import mage.abilities.keyword.EnchantAbility; -import mage.abilities.mana.BlackManaAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.abilities.mana.RedManaAbility; -import mage.abilities.mana.WhiteManaAbility; +import mage.abilities.mana.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetLandPermanent; +import java.util.UUID; + /** - * * @author Quercitron */ public final class PhantasmalTerrain extends CardImpl { @@ -60,14 +49,14 @@ public final class PhantasmalTerrain extends CardImpl { return new PhantasmalTerrain(this); } - class PhantasmalTerrainContinuousEffect extends ContinuousEffectImpl { + private static class PhantasmalTerrainContinuousEffect extends ContinuousEffectImpl { - public PhantasmalTerrainContinuousEffect() { - super(Duration.WhileOnBattlefield, Outcome.Neutral); - this.staticText = "enchanted land is the chosen type"; + private PhantasmalTerrainContinuousEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); + staticText = "Enchanted land is the chosen type"; } - public PhantasmalTerrainContinuousEffect(final PhantasmalTerrainContinuousEffect effect) { + private PhantasmalTerrainContinuousEffect(final PhantasmalTerrainContinuousEffect effect) { super(effect); } @@ -77,72 +66,60 @@ public final class PhantasmalTerrain extends CardImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - SubType choice = SubType.byDescription((String) game.getState() - .getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); - if (choice != null) { - if (choice.equals(SubType.MOUNTAIN)) { - dependencyTypes.add(DependencyType.BecomeMountain); - } - if (choice.equals(SubType.FOREST)) { + public void init(Ability source, Game game) { + super.init(source, game); + SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + switch (choice) { + case FOREST: dependencyTypes.add(DependencyType.BecomeForest); - } - if (choice.equals(SubType.SWAMP)) { - dependencyTypes.add(DependencyType.BecomeSwamp); - } - if (choice.equals(SubType.ISLAND)) { - dependencyTypes.add(DependencyType.BecomeIsland); - } - if (choice.equals(SubType.PLAINS)) { + break; + case PLAINS: dependencyTypes.add(DependencyType.BecomePlains); - } + break; + case MOUNTAIN: + dependencyTypes.add(DependencyType.BecomeMountain); + break; + case ISLAND: + dependencyTypes.add(DependencyType.BecomeIsland); + break; + case SWAMP: + dependencyTypes.add(DependencyType.BecomeSwamp); + break; } - if (enchantment != null - && enchantment.getAttachedTo() != null - && choice != null) { - Permanent land = game.getPermanent(enchantment.getAttachedTo()); - if (land != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - land.getSubtype(game).clear(); - land.getSubtype(game).add(choice); - land.removeAllAbilities(source.getSourceId(), game); - - // land ability is intrinsic, so apply at this layer, not layer 6 - if (choice.equals(SubType.FOREST)) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.PLAINS)) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.MOUNTAIN)) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.ISLAND)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.SWAMP)) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - } - break; - } - 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; + Permanent enchantment = game.getPermanent(source.getSourceId()); + SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + if (enchantment == null || enchantment.getAttachedTo() == null || choice == null) { + return false; + } + Permanent land = game.getPermanent(enchantment.getAttachedTo()); + if (land == null) { + return false; + } + land.getSubtype(game).removeAll(SubType.getLandTypes()); + land.addSubType(game, choice); + land.removeAllAbilities(source.getSourceId(), game); + switch (choice) { + case FOREST: + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + break; + case PLAINS: + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + break; + case MOUNTAIN: + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + break; + case ISLAND: + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + break; + case SWAMP: + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + break; + } + return true; } } } diff --git a/Mage.Sets/src/mage/cards/p/PillarvergePathway.java b/Mage.Sets/src/mage/cards/p/PillarvergePathway.java deleted file mode 100644 index fb003dc9f41..00000000000 --- a/Mage.Sets/src/mage/cards/p/PillarvergePathway.java +++ /dev/null @@ -1,33 +0,0 @@ -package mage.cards.p; - -import mage.abilities.mana.WhiteManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class PillarvergePathway extends CardImpl { - - public PillarvergePathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // {T}: Add {W}. - this.addAbility(new WhiteManaAbility()); - } - - private PillarvergePathway(final PillarvergePathway card) { - super(card); - } - - @Override - public PillarvergePathway copy() { - return new PillarvergePathway(this); - } -} diff --git a/Mage.Sets/src/mage/cards/p/PolymorphistsJest.java b/Mage.Sets/src/mage/cards/p/PolymorphistsJest.java index dd721287a36..2f56d290467 100644 --- a/Mage.Sets/src/mage/cards/p/PolymorphistsJest.java +++ b/Mage.Sets/src/mage/cards/p/PolymorphistsJest.java @@ -1,39 +1,32 @@ package mage.cards.p; -import java.util.Iterator; -import java.util.UUID; import mage.MageObjectReference; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; 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.SubType; -import mage.filter.common.FilterCreaturePermanent; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPlayer; +import java.util.Iterator; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PolymorphistsJest extends CardImpl { public PolymorphistsJest(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}"); // Until end of turn, each creature target player controls loses all abilities and becomes a blue Frog with base power and toughness 1/1. this.getSpellAbility().addEffect(new PolymorphistsJestEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - } public PolymorphistsJest(final PolymorphistsJest card) { @@ -48,8 +41,6 @@ public final class PolymorphistsJest extends CardImpl { class PolymorphistsJestEffect extends ContinuousEffectImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - public PolymorphistsJestEffect() { super(Duration.EndOfTurn, Outcome.BecomeCreature); staticText = "Until end of turn, each creature target player controls loses all abilities and becomes a blue Frog with base power and toughness 1/1"; @@ -68,46 +59,40 @@ class PolymorphistsJestEffect extends ContinuousEffectImpl { public void init(Ability source, Game game) { super.init(source, game); if (this.affectedObjectsSet) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, getTargetPointer().getFirst(game, source), game)) { - affectedObjectList.add(new MageObjectReference(permanent, game)); - } + game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + getTargetPointer().getFirst(game, source), source.getSourceId(), game + ).stream() + .map(permanent -> new MageObjectReference(permanent, game)) + .forEach(affectedObjectList::add); } } @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { + for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { Permanent permanent = it.next().getPermanent(game); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getSubtype(game).clear(); - permanent.getSubtype(game).add(SubType.FROG); - } - - break; - case ColorChangingEffects_5: - if (sublayer == SubLayer.NA) { - permanent.getColor(game).setBlack(false); - permanent.getColor(game).setGreen(false); - permanent.getColor(game).setBlue(false); - permanent.getColor(game).setWhite(false); - permanent.getColor(game).setBlack(false); - permanent.getColor(game).setColor(ObjectColor.BLUE); - } - break; - case AbilityAddingRemovingEffects_6: - permanent.removeAllAbilities(source.getSourceId(), game); - break; - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - permanent.getPower().setValue(1); - permanent.getToughness().setValue(1); - } - } - } else { + if (permanent == null) { it.remove(); + continue; + } + switch (layer) { + case TypeChangingEffects_4: + permanent.removeAllCreatureTypes(game); + permanent.addSubType(game, SubType.FROG); + break; + case ColorChangingEffects_5: + permanent.getColor(game).setColor(ObjectColor.BLUE); + break; + case AbilityAddingRemovingEffects_6: + permanent.removeAllAbilities(source.getSourceId(), game); + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getPower().setValue(1); + permanent.getToughness().setValue(1); + } } } return true; @@ -120,7 +105,10 @@ class PolymorphistsJestEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.PTChangingEffects_7 || layer == Layer.ColorChangingEffects_5 || layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + return layer == Layer.PTChangingEffects_7 + || layer == Layer.ColorChangingEffects_5 + || layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.TypeChangingEffects_4; } } diff --git a/Mage.Sets/src/mage/cards/p/PravaOfTheSteelLegion.java b/Mage.Sets/src/mage/cards/p/PravaOfTheSteelLegion.java new file mode 100644 index 00000000000..f94a5c5c06f --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PravaOfTheSteelLegion.java @@ -0,0 +1,66 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.PartnerAbility; +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.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.SoldierToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PravaOfTheSteelLegion extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(TokenPredicate.instance); + } + + public PravaOfTheSteelLegion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // As long as it's your turn, creature tokens you control get +1/+4. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(new BoostControlledEffect( + 1, 4, Duration.WhileOnBattlefield, filter + ), MyTurnCondition.instance, "as long as it's your turn, creature tokens you control get +1/+4"))); + + // {3}{W}: Create a 1/1 white Soldier creature token. + this.addAbility(new SimpleActivatedAbility( + new CreateTokenEffect(new SoldierToken()), new ManaCostsImpl("{3}{W}") + )); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private PravaOfTheSteelLegion(final PravaOfTheSteelLegion card) { + super(card); + } + + @Override + public PravaOfTheSteelLegion copy() { + return new PravaOfTheSteelLegion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProfaneTransfusion.java b/Mage.Sets/src/mage/cards/p/ProfaneTransfusion.java new file mode 100644 index 00000000000..d90d3e5ec8c --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProfaneTransfusion.java @@ -0,0 +1,94 @@ +package mage.cards.p; + +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.PhyrexianRebirthHorrorToken; +import mage.game.permanent.token.Token; +import mage.players.Player; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProfaneTransfusion extends CardImpl { + + public ProfaneTransfusion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{B}{B}{B}"); + + // Two target players exchange life totals. You create an X/X colorless Horror artifact creature token, where X is the difference between those players' life totals. + this.getSpellAbility().addEffect(new ProfaneTransfusionEffect()); + this.getSpellAbility().addTarget(new TargetPlayer(2)); + } + + private ProfaneTransfusion(final ProfaneTransfusion card) { + super(card); + } + + @Override + public ProfaneTransfusion copy() { + return new ProfaneTransfusion(this); + } +} + +class ProfaneTransfusionEffect extends OneShotEffect { + + ProfaneTransfusionEffect() { + super(Outcome.Benefit); + staticText = "two target players exchange life totals. " + + "You create an X/X colorless Horror artifact creature token, " + + "where X is the difference between those players' life totals"; + } + + private ProfaneTransfusionEffect(final ProfaneTransfusionEffect effect) { + super(effect); + } + + @Override + public ProfaneTransfusionEffect copy() { + return new ProfaneTransfusionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (source.getTargets().get(0).getTargets().size() < 2) { + return false; + } + Player player1 = game.getPlayer(source.getTargets().get(0).getTargets().get(0)); + Player player2 = game.getPlayer(source.getTargets().get(0).getTargets().get(1)); + if (player1 == null || player2 == null) { + return false; + } + int lifePlayer1 = player1.getLife(); + int lifePlayer2 = player2.getLife(); + int lifeDifference = Math.abs(lifePlayer1 - lifePlayer2); + + Token token = new PhyrexianRebirthHorrorToken(); + token.setPower(lifeDifference); + token.setToughness(lifeDifference); + + if (lifeDifference == 0 + || !player1.isLifeTotalCanChange() + || !player2.isLifeTotalCanChange()) { + return token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + } + + if (lifePlayer1 < lifePlayer2 && (!player1.isCanGainLife() || !player2.isCanLoseLife())) { + return token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + } + + if (lifePlayer1 > lifePlayer2 && (!player1.isCanLoseLife() || !player2.isCanGainLife())) { + return token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + } + + player1.setLife(lifePlayer2, game, source); + player2.setLife(lifePlayer1, game, source); + return token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RadiantSerraArchangel.java b/Mage.Sets/src/mage/cards/r/RadiantSerraArchangel.java new file mode 100644 index 00000000000..c3e4f685023 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RadiantSerraArchangel.java @@ -0,0 +1,68 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.common.continuous.GainProtectionFromColorSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.PartnerAbility; +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.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RadiantSerraArchangel extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledCreaturePermanent("another untapped creature you control with flying"); + + static { + filter.add(Predicates.not(TappedPredicate.instance)); + filter.add(AnotherPredicate.instance); + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public RadiantSerraArchangel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ANGEL); + this.power = new MageInt(6); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Tap another untapped creature you control with flying: Radiant, Serra Archangel gains protection from the color of your choice until end of turn. + this.addAbility(new SimpleActivatedAbility( + new GainProtectionFromColorSourceEffect(Duration.EndOfTurn), + new TapTargetCost(new TargetControlledPermanent(filter)) + )); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private RadiantSerraArchangel(final RadiantSerraArchangel card) { + super(card); + } + + @Override + public RadiantSerraArchangel copy() { + return new RadiantSerraArchangel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/Realmwright.java b/Mage.Sets/src/mage/cards/r/Realmwright.java index a7bb10453cb..f3b15d94261 100644 --- a/Mage.Sets/src/mage/cards/r/Realmwright.java +++ b/Mage.Sets/src/mage/cards/r/Realmwright.java @@ -1,35 +1,22 @@ - package mage.cards.r; -import java.util.List; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.ChooseBasicLandTypeEffect; -import mage.abilities.mana.BlackManaAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.abilities.mana.RedManaAbility; -import mage.abilities.mana.WhiteManaAbility; +import mage.abilities.mana.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; -import mage.filter.common.FilterControlledLandPermanent; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class Realmwright extends CardImpl { @@ -46,7 +33,7 @@ public final class Realmwright extends CardImpl { this.addAbility(new AsEntersBattlefieldAbility(new ChooseBasicLandTypeEffect(Outcome.Neutral))); // Lands you control are the chosen type in addition to their other types. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RealmwrightEffect2())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RealmwrightEffect())); } public Realmwright(final Realmwright card) { @@ -59,111 +46,81 @@ public final class Realmwright extends CardImpl { } } -class RealmwrightEffect2 extends ContinuousEffectImpl { +class RealmwrightEffect extends ContinuousEffectImpl { - public RealmwrightEffect2() { - super(Duration.WhileOnBattlefield, Outcome.Neutral); + public RealmwrightEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); staticText = "Lands you control are the chosen type in addition to their other types"; } - public RealmwrightEffect2(final RealmwrightEffect2 effect) { + public RealmwrightEffect(final RealmwrightEffect effect) { super(effect); } @Override - public RealmwrightEffect2 copy() { - return new RealmwrightEffect2(this); + public RealmwrightEffect copy() { + return new RealmwrightEffect(this); } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Player you = game.getPlayer(source.getControllerId()); - List lands = game.getBattlefield().getAllActivePermanents(new FilterControlledLandPermanent(), source.getControllerId(), game); + public void init(Ability source, Game game) { + super.init(source, game); SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); - if (you != null && choice != null) { - for (Permanent land : lands) { - if (land != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA && !land.hasSubtype(choice, game)) { - land.getSubtype(game).add(choice); - } - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - boolean addAbility = true; - if (choice.equals(SubType.FOREST)) { - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof GreenManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - } - if (choice.equals(SubType.PLAINS)) { - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof WhiteManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - } - if (choice.equals(SubType.MOUNTAIN)) { - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof RedManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - } - if (choice.equals(SubType.ISLAND)) { - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof BlueManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - } - if (choice.equals(SubType.SWAMP)) { - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof BlackManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - } - } - break; - } - } - } - return true; + switch (choice) { + case PLAINS: + dependencyTypes.add(DependencyType.BecomePlains); + break; + case ISLAND: + dependencyTypes.add(DependencyType.BecomeIsland); + break; + case SWAMP: + dependencyTypes.add(DependencyType.BecomeSwamp); + break; + case MOUNTAIN: + dependencyTypes.add(DependencyType.BecomeMountain); + break; + case FOREST: + dependencyTypes.add(DependencyType.BecomeForest); + break; } - return false; } @Override public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + if (choice == null) { + return false; + } + Ability ability; + switch (choice) { + case PLAINS: + ability = new WhiteManaAbility(); + break; + case ISLAND: + ability = new BlueManaAbility(); + break; + case SWAMP: + ability = new BlackManaAbility(); + break; + case MOUNTAIN: + ability = new RedManaAbility(); + break; + case FOREST: + ability = new GreenManaAbility(); + break; + default: + ability = null; + } + for (Permanent land : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + source.getControllerId(), source.getSourceId(), game + )) { + if (land == null || land.hasSubtype(choice, game)) { + continue; + } + land.addSubType(game, choice); + land.addAbility(ability, source.getSourceId(), game); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/r/ReapIntellect.java b/Mage.Sets/src/mage/cards/r/ReapIntellect.java index ed1a2671fb3..b9f13f7281c 100644 --- a/Mage.Sets/src/mage/cards/r/ReapIntellect.java +++ b/Mage.Sets/src/mage/cards/r/ReapIntellect.java @@ -12,16 +12,17 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; import mage.players.Player; +import mage.target.TargetCard; import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; +import mage.util.CardUtil; + import java.util.ArrayList; import java.util.List; import java.util.UUID; -import mage.target.TargetCard; /** - * * @author jeffwadsworth */ public final class ReapIntellect extends CardImpl { @@ -100,12 +101,11 @@ class ReapIntellectEffect extends OneShotEffect { List names = new ArrayList<>(); FilterCard filterNamedCards = new FilterCard(); for (Card card : exiledCards.getCards(game)) { + String nameToSearch = CardUtil.getCardNameForSameNameSearch(card); if (exiledCards.size() == 1) { - filterNamedCards.add(new NamePredicate(card.isSplitCard() - ? ((SplitCard) card).getLeftHalfCard().getName() : card.getName())); + filterNamedCards.add(new NamePredicate(nameToSearch)); } else { - names.add(new NamePredicate(card.isSplitCard() - ? ((SplitCard) card).getLeftHalfCard().getName() : card.getName())); + names.add(new NamePredicate(nameToSearch)); } } if (exiledCards.size() > 1) { diff --git a/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java b/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java index 6a3f7ce162a..fb8fd6ae0ce 100644 --- a/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java +++ b/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java @@ -4,21 +4,18 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; -import mage.abilities.keyword.ChangelingAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.SubTypeSet; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; -import java.util.HashSet; -import java.util.Set; +import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; /** * @author TheElk801 @@ -73,12 +70,11 @@ class ReturnFromExtinctionTarget extends TargetCardInYourGraveyard { if (targetOne == null || targetTwo == null) { return false; } - return targetOne.shareSubtypes(targetTwo, game); + return targetOne.shareCreatureTypes(targetTwo, game); } @Override public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { - Set subTypes = new HashSet<>(); MageObject targetSource = game.getObject(sourceId); Player player = game.getPlayer(sourceControllerId); if (player == null) { @@ -87,22 +83,21 @@ class ReturnFromExtinctionTarget extends TargetCardInYourGraveyard { if (targetSource == null) { return false; } - for (Card card : player.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) { - if (card.isAllCreatureTypes() || card.hasAbility(ChangelingAbility.getInstance(), game)) { - if (!subTypes.isEmpty()) { - return true; - } else { - subTypes.addAll(SubType.getCreatureTypes()); + List cards = player.getGraveyard().getCards( + filter, sourceId, sourceControllerId, game + ).stream().collect(Collectors.toList()); + if (cards.size() < 2) { + return false; + } + for (int i = 0; i < cards.size(); i++) { + for (int j = 0; j < cards.size(); j++) { + if (i <= j) { + continue; } - continue; - } - for (SubType subType : card.getSubtype(game)) { - if (subType.getSubTypeSet() == SubTypeSet.CreatureType && subTypes.contains(subType)) { + if (cards.get(i).shareCreatureTypes(cards.get(j), game)) { return true; } } - subTypes.addAll(card.getSubtype(game)); - subTypes.removeIf((SubType st) -> (st.getSubTypeSet() != SubTypeSet.CreatureType)); } return false; } diff --git a/Mage.Sets/src/mage/cards/r/RiverglidePathway.java b/Mage.Sets/src/mage/cards/r/RiverglidePathway.java index 00e7141b736..39451b2c14d 100644 --- a/Mage.Sets/src/mage/cards/r/RiverglidePathway.java +++ b/Mage.Sets/src/mage/cards/r/RiverglidePathway.java @@ -3,23 +3,36 @@ package mage.cards.r; import mage.abilities.mana.RedManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class RiverglidePathway extends CardImpl { +public final class RiverglidePathway extends ModalDoubleFacesCard { public RiverglidePathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + super(ownerId, setInfo, + new CardType[]{CardType.LAND}, new SubType[]{}, "", + "Lavaglide Pathway", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.l.LavaglidePathway.class; + // 1. + // Riverglide Pathway + // Land // {T}: Add {U}. - this.addAbility(new RedManaAbility()); + this.getLeftHalfCard().addAbility(new RedManaAbility()); + + // 2. + // Lavaglide Pathway + // Land + + // {T}: Add {R}. + this.getRightHalfCard().addAbility(new RedManaAbility()); } private RiverglidePathway(final RiverglidePathway card) { diff --git a/Mage.Sets/src/mage/cards/r/RograkhSonOfRohgahh.java b/Mage.Sets/src/mage/cards/r/RograkhSonOfRohgahh.java new file mode 100644 index 00000000000..bef6a014d81 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RograkhSonOfRohgahh.java @@ -0,0 +1,52 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.PartnerAbility; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RograkhSonOfRohgahh extends CardImpl { + + public RograkhSonOfRohgahh(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{0}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.KOBOLD); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(0); + this.toughness = new MageInt(1); + this.color.setRed(true); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Menace + this.addAbility(new MenaceAbility()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private RograkhSonOfRohgahh(final RograkhSonOfRohgahh card) { + super(card); + } + + @Override + public RograkhSonOfRohgahh copy() { + return new RograkhSonOfRohgahh(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RuinsOfOranRief.java b/Mage.Sets/src/mage/cards/r/RuinsOfOranRief.java index 47d2869fdc0..b9cb0426b87 100644 --- a/Mage.Sets/src/mage/cards/r/RuinsOfOranRief.java +++ b/Mage.Sets/src/mage/cards/r/RuinsOfOranRief.java @@ -28,7 +28,7 @@ public final class RuinsOfOranRief extends CardImpl { static { filter.add(ColorlessPredicate.instance); - filter.add(new EnteredThisTurnPredicate()); + filter.add(EnteredThisTurnPredicate.instance); } public RuinsOfOranRief(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RunedStalactite.java b/Mage.Sets/src/mage/cards/r/RunedStalactite.java index 34a54ffa55a..e76431ecfbf 100644 --- a/Mage.Sets/src/mage/cards/r/RunedStalactite.java +++ b/Mage.Sets/src/mage/cards/r/RunedStalactite.java @@ -1,38 +1,33 @@ - package mage.cards.r; -import java.util.UUID; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.abilities.keyword.ChangelingAbility; +import mage.abilities.effects.common.continuous.GainAllCreatureTypesAttachedEffect; 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.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class RunedStalactite extends CardImpl { public RunedStalactite(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/+1 and is every creature type. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1,1))); - Effect effect = new GainAbilityAttachedEffect(ChangelingAbility.getInstance(), AttachmentType.EQUIPMENT, Duration.WhileOnBattlefield); - effect.setText("Equipped creature is every creature type (Changeling)"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1)); + ability.addEffect(new GainAllCreatureTypesAttachedEffect()); + this.addAbility(ability); + // Equip {2} this.addAbility(new EquipAbility(Outcome.BoostCreature, new ManaCostsImpl("{2}"))); } diff --git a/Mage.Sets/src/mage/cards/s/SarkhanTheDragonspeaker.java b/Mage.Sets/src/mage/cards/s/SarkhanTheDragonspeaker.java index 49b3957b0a2..95ef7f0563d 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanTheDragonspeaker.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanTheDragonspeaker.java @@ -91,8 +91,8 @@ class SarkhanTheDragonspeakerEffect extends ContinuousEffectImpl { if (sublayer == SubLayer.NA) { permanent.getCardType().clear(); permanent.addCardType(CardType.CREATURE); - permanent.getSubtype(game).clear(); - permanent.getSubtype(game).add(SubType.DRAGON); + permanent.removeAllSubTypes(game); + permanent.addSubType(game, SubType.DRAGON); permanent.getSuperType().clear(); permanent.addSuperType(SuperType.LEGENDARY); } diff --git a/Mage.Sets/src/mage/cards/s/SarkhanTheMasterless.java b/Mage.Sets/src/mage/cards/s/SarkhanTheMasterless.java index d8b4c335edc..844679b4676 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanTheMasterless.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanTheMasterless.java @@ -127,8 +127,8 @@ class SarkhanTheMasterlessBecomeDragonEffect extends ContinuousEffectImpl { if (sublayer == SubLayer.NA) { permanent.getCardType().clear(); permanent.addCardType(CardType.CREATURE); - permanent.getSubtype(game).clear(); - permanent.getSubtype(game).add(SubType.DRAGON); + permanent.removeAllSubTypes(game); + permanent.addSubType(game, SubType.DRAGON); } break; case ColorChangingEffects_5: diff --git a/Mage.Sets/src/mage/cards/s/ScytheSpecter.java b/Mage.Sets/src/mage/cards/s/ScytheSpecter.java index 0726c646937..baf78776926 100644 --- a/Mage.Sets/src/mage/cards/s/ScytheSpecter.java +++ b/Mage.Sets/src/mage/cards/s/ScytheSpecter.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.MageInt; @@ -11,8 +10,8 @@ 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.filter.FilterCard; import mage.game.Game; import mage.players.Player; @@ -24,7 +23,6 @@ import java.util.Map; import java.util.UUID; /** - * * @author jeffwadsworth */ public final class ScytheSpecter extends CardImpl { @@ -80,13 +78,7 @@ class ScytheSpecterEffect extends OneShotEffect { opponent.chooseTarget(Outcome.Discard, target, source, game); Card targetCard = game.getCard(target.getFirstTarget()); if (targetCard != null) { - if (targetCard.isSplitCard()) { //check Split Cards - if (targetCard.getSecondCardFace().getConvertedManaCost() < targetCard.getConvertedManaCost()) { - currentCMC = targetCard.getConvertedManaCost(); - } - } else { - currentCMC = targetCard.getConvertedManaCost(); - } + currentCMC = targetCard.getConvertedManaCost(); if (highestCMC <= currentCMC) { highestCMC = currentCMC; } diff --git a/Mage.Sets/src/mage/cards/s/SeaGateReborn.java b/Mage.Sets/src/mage/cards/s/SeaGateReborn.java deleted file mode 100644 index 8f16afde3c3..00000000000 --- a/Mage.Sets/src/mage/cards/s/SeaGateReborn.java +++ /dev/null @@ -1,42 +0,0 @@ -package mage.cards.s; - -import mage.abilities.common.AsEntersBattlefieldAbility; -import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.effects.common.TapSourceUnlessPaysEffect; -import mage.abilities.mana.BlueManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class SeaGateReborn extends CardImpl { - - public SeaGateReborn(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // As Sea Gate, Reborn enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. - this.addAbility(new AsEntersBattlefieldAbility( - new TapSourceUnlessPaysEffect(new PayLifeCost(3)), - "you may pay 3 life. If you don't, it enters the battlefield tapped" - )); - - // {T}: Add {U}. - this.addAbility(new BlueManaAbility()); - } - - private SeaGateReborn(final SeaGateReborn card) { - super(card); - } - - @Override - public SeaGateReborn copy() { - return new SeaGateReborn(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SeaGateRestoration.java b/Mage.Sets/src/mage/cards/s/SeaGateRestoration.java index 4c64ab6b471..82418ca74c6 100644 --- a/Mage.Sets/src/mage/cards/s/SeaGateRestoration.java +++ b/Mage.Sets/src/mage/cards/s/SeaGateRestoration.java @@ -1,38 +1,60 @@ package mage.cards.s; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.dynamicvalue.AdditiveDynamicValue; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; -import mage.cards.CardImpl; +import mage.abilities.mana.BlueManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class SeaGateRestoration extends CardImpl { +public final class SeaGateRestoration extends ModalDoubleFacesCard { private static final DynamicValue xValue = new AdditiveDynamicValue(CardsInControllerHandCount.instance, StaticValue.get(1)); public SeaGateRestoration(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}{U}{U}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{4}{U}{U}{U}", + "Sea Gate, Reborn", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.s.SeaGateReborn.class; + // 1. + // Sea Gate Restoration + // Sorcery // Draw cards equal to the number of cards in your hand plus one. You have no maximum hand size for the rest of the game. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(xValue) + this.getLeftHalfCard().getSpellAbility().addEffect(new DrawCardSourceControllerEffect(xValue) .setText("Draw cards equal to the number of cards in your hand plus one.")); - this.getSpellAbility().addEffect(new MaximumHandSizeControllerEffect( + this.getLeftHalfCard().getSpellAbility().addEffect(new MaximumHandSizeControllerEffect( Integer.MAX_VALUE, Duration.EndOfGame, MaximumHandSizeControllerEffect.HandSizeModification.SET )); + + // 2. + // Sea Gate, Reborn + // Land + + // As Sea Gate, Reborn enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. + this.getRightHalfCard().addAbility(new AsEntersBattlefieldAbility( + new TapSourceUnlessPaysEffect(new PayLifeCost(3)), + "you may pay 3 life. If you don't, it enters the battlefield tapped" + )); + + // {T}: Add {U}. + this.getRightHalfCard().addAbility(new BlueManaAbility()); } private SeaGateRestoration(final SeaGateRestoration card) { diff --git a/Mage.Sets/src/mage/cards/s/SealockMonster.java b/Mage.Sets/src/mage/cards/s/SealockMonster.java index 38576608221..773a8e07d80 100644 --- a/Mage.Sets/src/mage/cards/s/SealockMonster.java +++ b/Mage.Sets/src/mage/cards/s/SealockMonster.java @@ -1,48 +1,49 @@ - package mage.cards.s; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BecomesMonstrousSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.combat.CantAttackUnlessDefenderControllsPermanent; +import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect; import mage.abilities.keyword.MonstrosityAbility; -import mage.abilities.mana.BlueManaAbility; 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.filter.common.FilterLandPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.Target; import mage.target.common.TargetLandPermanent; import java.util.UUID; /** - * * @author LevelX2 */ public final class SealockMonster extends CardImpl { + private static final FilterPermanent filter = new FilterLandPermanent(SubType.ISLAND, "an Island"); + public SealockMonster(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.subtype.add(SubType.OCTOPUS); this.power = new MageInt(5); this.toughness = new MageInt(5); // Sealock Monster can't attack unless defending player controls an Island. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackUnlessDefenderControllsPermanent(new FilterLandPermanent(SubType.ISLAND,"an Island")))); - // {5}{U}{U}: Monstrosity 3. - this.addAbility(new MonstrosityAbility("{5}{U}{U}",3)); - // When Sealock Monster becomes monstrous, target land becomes an island in addition to its other types. - Ability ability = new BecomesMonstrousSourceTriggeredAbility(new SealockMonsterBecomesIslandTargetEffect()); - Target target = new TargetLandPermanent(); - ability.addTarget(target); - this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(new CantAttackUnlessDefenderControllsPermanent(filter))); + // {5}{U}{U}: Monstrosity 3. + this.addAbility(new MonstrosityAbility("{5}{U}{U}", 3)); + + // 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) + ); + ability.addTarget(new TargetLandPermanent()); + this.addAbility(ability); } public SealockMonster(final SealockMonster card) { @@ -54,56 +55,3 @@ public final class SealockMonster extends CardImpl { return new SealockMonster(this); } } - -class SealockMonsterBecomesIslandTargetEffect extends ContinuousEffectImpl { - - private static Ability islandAbility = new BlueManaAbility(); - - public SealockMonsterBecomesIslandTargetEffect() { - super(Duration.EndOfGame, Outcome.Detriment); - this.staticText = "target land becomes an island in addition to its other types"; - - } - - public SealockMonsterBecomesIslandTargetEffect(final SealockMonsterBecomesIslandTargetEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public SealockMonsterBecomesIslandTargetEffect copy() { - return new SealockMonsterBecomesIslandTargetEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (UUID targetPermanent : targetPointer.getTargets(game, source)) { - Permanent land = game.getPermanent(targetPermanent); - if (land != null) { - switch (layer) { - case AbilityAddingRemovingEffects_6: - if (!land.getAbilities().containsRule(islandAbility)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - break; - case TypeChangingEffects_4: - if (!land.hasSubtype(SubType.ISLAND, game)) { - land.getSubtype(game).add(SubType.ISLAND); - } - break; - } - } - } - return true; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/SecretSalvage.java b/Mage.Sets/src/mage/cards/s/SecretSalvage.java index 50495f98a52..b80ed6dc01f 100644 --- a/Mage.Sets/src/mage/cards/s/SecretSalvage.java +++ b/Mage.Sets/src/mage/cards/s/SecretSalvage.java @@ -1,16 +1,9 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.cards.SplitCard; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -21,9 +14,11 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class SecretSalvage extends CardImpl { @@ -72,9 +67,11 @@ class SecretSalvageEffect extends OneShotEffect { Card targetCard = game.getCard(getTargetPointer().getFirst(game, source)); if (targetCard != null) { controller.moveCards(targetCard, Zone.EXILED, source, game); + + String nameToSearch = CardUtil.getCardNameForSameNameSearch(targetCard); FilterCard nameFilter = new FilterCard(); - String nameToSearch = targetCard.isSplitCard() ? ((SplitCard) targetCard).getLeftHalfCard().getName() : targetCard.getName(); nameFilter.add(new NamePredicate(nameToSearch)); + TargetCardInLibrary target = new TargetCardInLibrary(0, Integer.MAX_VALUE, nameFilter); if (controller.searchLibrary(target, source, game)) { if (!target.getTargets().isEmpty()) { diff --git a/Mage.Sets/src/mage/cards/s/SejiriGlacier.java b/Mage.Sets/src/mage/cards/s/SejiriGlacier.java deleted file mode 100644 index 79e75fd0d75..00000000000 --- a/Mage.Sets/src/mage/cards/s/SejiriGlacier.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.s; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.WhiteManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class SejiriGlacier extends CardImpl { - - public SejiriGlacier(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Sejiri Glacier enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {W}. - this.addAbility(new WhiteManaAbility()); - } - - private SejiriGlacier(final SejiriGlacier card) { - super(card); - } - - @Override - public SejiriGlacier copy() { - return new SejiriGlacier(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SejiriShelter.java b/Mage.Sets/src/mage/cards/s/SejiriShelter.java index 5dffd6e2d5c..57dba988279 100644 --- a/Mage.Sets/src/mage/cards/s/SejiriShelter.java +++ b/Mage.Sets/src/mage/cards/s/SejiriShelter.java @@ -1,28 +1,46 @@ package mage.cards.s; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.continuous.GainProtectionFromColorTargetEffect; +import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SubType; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class SejiriShelter extends CardImpl { +public final class SejiriShelter extends ModalDoubleFacesCard { public SejiriShelter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{1}{W}", + "Sejiri Glacier", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.s.SejiriGlacier.class; + // 1. + // Sejiri Shelter + // Instant // Target creature you control gains protection from the color of your choice until end of turn. - this.getSpellAbility().addEffect(new GainProtectionFromColorTargetEffect(Duration.EndOfTurn)); - this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getLeftHalfCard().getSpellAbility().addEffect(new GainProtectionFromColorTargetEffect(Duration.EndOfTurn)); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + + // 2. + // Sejiri Glacier + // Land + + // Sejiri Glacier enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {W}. + this.getRightHalfCard().addAbility(new WhiteManaAbility()); } private SejiriShelter(final SejiriShelter card) { diff --git a/Mage.Sets/src/mage/cards/s/Shapesharer.java b/Mage.Sets/src/mage/cards/s/Shapesharer.java index fbca5f79ed1..5c0c7674c33 100644 --- a/Mage.Sets/src/mage/cards/s/Shapesharer.java +++ b/Mage.Sets/src/mage/cards/s/Shapesharer.java @@ -35,6 +35,7 @@ public final class Shapesharer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // {2}{U}: Target Shapeshifter becomes a copy of target creature until your next turn. diff --git a/Mage.Sets/src/mage/cards/s/SharedAnimosity.java b/Mage.Sets/src/mage/cards/s/SharedAnimosity.java index 01624774eed..f15580f1e82 100644 --- a/Mage.Sets/src/mage/cards/s/SharedAnimosity.java +++ b/Mage.Sets/src/mage/cards/s/SharedAnimosity.java @@ -1,12 +1,10 @@ - package mage.cards.s; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.keyword.ChangelingAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -18,21 +16,20 @@ import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; /** - * * @author Plopman */ public final class SharedAnimosity extends CardImpl { public SharedAnimosity(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); // Whenever a creature you control attacks, it gets +1/+0 until end of turn for each other attacking creature that shares a creature type with it. - this.addAbility(new AttacksCreatureYouControlTriggeredAbility(new SharedAnimosityEffect(), false, true)); + this.addAbility(new AttacksCreatureYouControlTriggeredAbility( + new SharedAnimosityEffect(), false, true) + ); } public SharedAnimosity(final SharedAnimosity card) { @@ -51,7 +48,7 @@ class SharedAnimosityEffect extends ContinuousEffectImpl { public SharedAnimosityEffect() { super(Duration.EndOfTurn, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); - + } public SharedAnimosityEffect(final SharedAnimosityEffect effect) { @@ -69,29 +66,11 @@ class SharedAnimosityEffect extends ContinuousEffectImpl { super.init(source, game); Permanent permanent = game.getPermanent(this.targetPointer.getFirst(game, source)); if (permanent != null) { - FilterCreaturePermanent filter = new FilterCreaturePermanent(); filter.add(Predicates.not(new PermanentIdPredicate(this.targetPointer.getFirst(game, source)))); filter.add(AttackingPredicate.instance); - boolean allCreatureTypes = false; - if (permanent.isAllCreatureTypes()) { - allCreatureTypes = true; - } else { - for(Ability ability : permanent.getAbilities()){ - if(ability instanceof ChangelingAbility){ - allCreatureTypes = true; - } - } - } - if(!allCreatureTypes){ - List> predicateList = new ArrayList<>(); - for(SubType subtype : permanent.getSubtype(game)){ - predicateList.add(subtype.getPredicate()); - } - filter.add(Predicates.or(predicateList)); - } - - power = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game).size(); + filter.add(new SharedAnimosityPredicate(permanent)); + power = game.getBattlefield().count(filter, source.getControllerId(), source.getSourceId(), game); } } @@ -110,3 +89,17 @@ class SharedAnimosityEffect extends ContinuousEffectImpl { return "it gets +1/+0 until end of turn for each other attacking creature that shares a creature type with it"; } } + +class SharedAnimosityPredicate implements Predicate { + + private final Permanent permanent; + + SharedAnimosityPredicate(Permanent permanent) { + this.permanent = permanent; + } + + @Override + public boolean apply(Card input, Game game) { + return permanent != null && input != null && permanent.shareCreatureTypes(input, game); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShatterskullSmashing.java b/Mage.Sets/src/mage/cards/s/ShatterskullSmashing.java index b668a4b1f1c..e5dfa404cbb 100644 --- a/Mage.Sets/src/mage/cards/s/ShatterskullSmashing.java +++ b/Mage.Sets/src/mage/cards/s/ShatterskullSmashing.java @@ -1,15 +1,21 @@ package mage.cards.s; import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.condition.Condition; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MultipliedValue; import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.common.DamageMultiEffect; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; +import mage.abilities.mana.RedManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.game.Game; import mage.target.TargetAmount; import mage.target.common.TargetCreatureOrPlaneswalkerAmount; @@ -20,21 +26,41 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class ShatterskullSmashing extends CardImpl { +public final class ShatterskullSmashing extends ModalDoubleFacesCard { private static final DynamicValue xValue = new MultipliedValue(ManacostVariableValue.instance, 2); public ShatterskullSmashing(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{R}{R}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{X}{R}{R}", + "Shatterskull, the Hammer Pass", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); + + // 1. + // Shatterskull Smashing + // Sorcery // Shatterskull Smashing deals X damage divided as you choose among up to two target creatures and/or planeswalkers. If X is 6 or more, Shatterskull Smashing deals twice X damage divided as you choose among them instead. - this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + this.getLeftHalfCard().getSpellAbility().addEffect(new ConditionalOneShotEffect( new DamageMultiEffect(xValue), new DamageMultiEffect(ManacostVariableValue.instance), ShatterskullSmashingCondition.instance, "{this} deals X damage divided as you choose " + "among up to two target creatures and/or planeswalkers. If X is 6 or more, " + "{this} deals twice X damage divided as you choose among them instead." )); - this.getSpellAbility().setTargetAdjuster(ShatterskullSmashingAdjuster.instance); + this.getLeftHalfCard().getSpellAbility().setTargetAdjuster(ShatterskullSmashingAdjuster.instance); + + // 2. + // Shatterskull, the Hammer Pass + // Land + + // As Shatterskull, the Hammer Pass enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. + this.getRightHalfCard().addAbility(new AsEntersBattlefieldAbility( + new TapSourceUnlessPaysEffect(new PayLifeCost(3)), + "you may pay 3 life. If you don't, it enters the battlefield tapped" + )); + + // {T}: Add {R}. + this.getRightHalfCard().addAbility(new RedManaAbility()); } private ShatterskullSmashing(final ShatterskullSmashing card) { diff --git a/Mage.Sets/src/mage/cards/s/ShatterskullTheHammerPass.java b/Mage.Sets/src/mage/cards/s/ShatterskullTheHammerPass.java deleted file mode 100644 index cc1b6d1e10f..00000000000 --- a/Mage.Sets/src/mage/cards/s/ShatterskullTheHammerPass.java +++ /dev/null @@ -1,42 +0,0 @@ -package mage.cards.s; - -import mage.abilities.common.AsEntersBattlefieldAbility; -import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.effects.common.TapSourceUnlessPaysEffect; -import mage.abilities.mana.RedManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class ShatterskullTheHammerPass extends CardImpl { - - public ShatterskullTheHammerPass(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // As Shatterskull, the Hammer Pass enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. - this.addAbility(new AsEntersBattlefieldAbility( - new TapSourceUnlessPaysEffect(new PayLifeCost(3)), - "you may pay 3 life. If you don't, it enters the battlefield tapped" - )); - - // {T}: Add {R}. - this.addAbility(new RedManaAbility()); - } - - private ShatterskullTheHammerPass(final ShatterskullTheHammerPass card) { - super(card); - } - - @Override - public ShatterskullTheHammerPass copy() { - return new ShatterskullTheHammerPass(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java b/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java index edc7d925e23..a276ced613d 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java +++ b/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java @@ -27,6 +27,7 @@ public final class ShieldsOfVelisVel extends CardImpl { this.subtype.add(SubType.SHAPESHIFTER); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); //Creatures target player controls get +0/+1 and gain all creature types until end of turn. @@ -78,7 +79,7 @@ class ShieldsOfVelisVelGainEffect extends ContinuousEffectImpl { for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { Permanent permanent = it.next().getPermanent(game); if (permanent != null) { - permanent.addAbility(ChangelingAbility.getInstance(), source.getSourceId(), game); + permanent.setIsAllCreatureTypes(true); } else { it.remove(); } diff --git a/Mage.Sets/src/mage/cards/s/ShimianSpecter.java b/Mage.Sets/src/mage/cards/s/ShimianSpecter.java index c5a82bc53a3..d0bb35fc5f6 100644 --- a/Mage.Sets/src/mage/cards/s/ShimianSpecter.java +++ b/Mage.Sets/src/mage/cards/s/ShimianSpecter.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -10,10 +9,9 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.SplitCard; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterNonlandCard; @@ -23,9 +21,11 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class ShimianSpecter extends CardImpl { @@ -103,9 +103,7 @@ class ShimianSpecterEffect extends OneShotEffect { FilterCard filterNamedCards = new FilterCard(); String nameToSearch = "---";// so no card matches if (chosenCard != null) { - nameToSearch = chosenCard.isSplitCard() - ? ((SplitCard) chosenCard).getLeftHalfCard().getName() - : chosenCard.getName(); + nameToSearch = CardUtil.getCardNameForSameNameSearch(chosenCard); } filterNamedCards.add(new NamePredicate(nameToSearch)); diff --git a/Mage.Sets/src/mage/cards/s/SilundiIsle.java b/Mage.Sets/src/mage/cards/s/SilundiIsle.java deleted file mode 100644 index 9968045033f..00000000000 --- a/Mage.Sets/src/mage/cards/s/SilundiIsle.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.s; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class SilundiIsle extends CardImpl { - - public SilundiIsle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Silundi Isle enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {U}. - this.addAbility(new BlueManaAbility()); - } - - private SilundiIsle(final SilundiIsle card) { - super(card); - } - - @Override - public SilundiIsle copy() { - return new SilundiIsle(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SilundiVision.java b/Mage.Sets/src/mage/cards/s/SilundiVision.java index e4d74288469..bcf1340b10a 100644 --- a/Mage.Sets/src/mage/cards/s/SilundiVision.java +++ b/Mage.Sets/src/mage/cards/s/SilundiVision.java @@ -1,10 +1,14 @@ package mage.cards.s; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.mana.BlueManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; @@ -13,16 +17,20 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class SilundiVision extends CardImpl { +public final class SilundiVision extends ModalDoubleFacesCard { public SilundiVision(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{2}{U}", + "Silundi Isle", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.s.SilundiIsle.class; + // 1. + // Silundi Vision + // Instant // Look at the top six cards of your library. You may reveal an instant or sorcery 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( + this.getLeftHalfCard().getSpellAbility().addEffect( new LookLibraryAndPickControllerEffect( StaticValue.get(6), false, StaticValue.get(1), StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, Zone.LIBRARY, @@ -32,6 +40,16 @@ public final class SilundiVision extends CardImpl { "You may reveal an instant or sorcery card from among them " + "and put it into your hand. Put the rest on the bottom of your library in a random order.") ); + + // 2. + // Silundi Isle + // Land + + // Silundi Isle enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {U}. + this.getRightHalfCard().addAbility(new BlueManaAbility()); } private SilundiVision(final SilundiVision card) { diff --git a/Mage.Sets/src/mage/cards/s/SkeletalChangeling.java b/Mage.Sets/src/mage/cards/s/SkeletalChangeling.java index f58165c5820..faf91cad887 100644 --- a/Mage.Sets/src/mage/cards/s/SkeletalChangeling.java +++ b/Mage.Sets/src/mage/cards/s/SkeletalChangeling.java @@ -26,6 +26,7 @@ public final class SkeletalChangeling extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // {1}{B}: Regenerate Skeletal Changeling. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{1}{B}"))); diff --git a/Mage.Sets/src/mage/cards/s/SkyclaveBasilica.java b/Mage.Sets/src/mage/cards/s/SkyclaveBasilica.java deleted file mode 100644 index 3b165e92f21..00000000000 --- a/Mage.Sets/src/mage/cards/s/SkyclaveBasilica.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.s; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.WhiteManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class SkyclaveBasilica extends CardImpl { - - public SkyclaveBasilica(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Skyclave Basilica enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {W}. - this.addAbility(new WhiteManaAbility()); - } - - private SkyclaveBasilica(final SkyclaveBasilica card) { - super(card); - } - - @Override - public SkyclaveBasilica copy() { - return new SkyclaveBasilica(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SkyclaveCleric.java b/Mage.Sets/src/mage/cards/s/SkyclaveCleric.java index ca7eb695349..d58df53c6bb 100644 --- a/Mage.Sets/src/mage/cards/s/SkyclaveCleric.java +++ b/Mage.Sets/src/mage/cards/s/SkyclaveCleric.java @@ -1,33 +1,46 @@ package mage.cards.s; import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class SkyclaveCleric extends CardImpl { +public final class SkyclaveCleric extends ModalDoubleFacesCard { public SkyclaveCleric(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.KOR, SubType.CLERIC}, "{1}{W}", + "Skyclave Basilica", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.subtype.add(SubType.KOR); - this.subtype.add(SubType.CLERIC); - this.power = new MageInt(1); - this.toughness = new MageInt(3); - - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.s.SkyclaveBasilica.class; + // 1. + // Skyclave Cleric + // Creature — Kor Cleric + this.getLeftHalfCard().setPT(new MageInt(1), new MageInt(3)); // When Skyclave Cleric enters the battlefield, you gain 2 life. - this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(2))); + this.getLeftHalfCard().addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(2))); + + // 2. + // Skyclave Basilica + // Land + + // Skyclave Basilica enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {W}. + this.getRightHalfCard().addAbility(new WhiteManaAbility()); } private SkyclaveCleric(final SkyclaveCleric card) { diff --git a/Mage.Sets/src/mage/cards/s/SlashTheRanks.java b/Mage.Sets/src/mage/cards/s/SlashTheRanks.java new file mode 100644 index 00000000000..879c182a304 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SlashTheRanks.java @@ -0,0 +1,41 @@ +package mage.cards.s; + +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.CommanderPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SlashTheRanks extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreatureOrPlaneswalkerPermanent("creatures and planeswalkers except for commanders"); + + static { + filter.add(Predicates.not(CommanderPredicate.instance)); + } + + public SlashTheRanks(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}"); + + // Destroy all creatures and planeswalkers except for commanders. + this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + } + + private SlashTheRanks(final SlashTheRanks card) { + super(card); + } + + @Override + public SlashTheRanks copy() { + return new SlashTheRanks(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SlumberingTora.java b/Mage.Sets/src/mage/cards/s/SlumberingTora.java index c91c47e1ccc..e56691992c2 100644 --- a/Mage.Sets/src/mage/cards/s/SlumberingTora.java +++ b/Mage.Sets/src/mage/cards/s/SlumberingTora.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; @@ -17,23 +16,24 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SlumberingTora extends CardImpl { private static final FilterCard filter = new FilterCard("Spirit or Arcane card"); - + static { - filter.add(Predicates.or(SubType.SPIRIT.getPredicate(),SubType.ARCANE.getPredicate())); - } + filter.add(Predicates.or(SubType.SPIRIT.getPredicate(), SubType.ARCANE.getPredicate())); + } public SlumberingTora(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {2}, Discard a Spirit or Arcane card: Slumbering Tora becomes an X/X Cat artifact creature until end of turn, // where X is the discarded card's converted mana cost. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SlumberingToraEffect(), new ManaCostsImpl("{2}")); + Ability ability = new SimpleActivatedAbility(new SlumberingToraEffect(), new ManaCostsImpl("{2}")); ability.addCost(new DiscardTargetCost(new TargetCardInHand(filter))); this.addAbility(ability); } @@ -46,15 +46,18 @@ public final class SlumberingTora extends CardImpl { public SlumberingTora copy() { return new SlumberingTora(this); } - + private static class SlumberingToraEffect extends ContinuousEffectImpl { - public SlumberingToraEffect() { + private int convManaCosts = 0; + + private SlumberingToraEffect() { super(Duration.EndOfTurn, Outcome.BecomeCreature); - setText(); + staticText = "{this} becomes an X/X Cat artifact creature until end of turn, " + + "where X is the discarded card's converted mana cost"; } - public SlumberingToraEffect(final SlumberingToraEffect effect) { + private SlumberingToraEffect(final SlumberingToraEffect effect) { super(effect); } @@ -63,33 +66,36 @@ public final class SlumberingTora extends CardImpl { return new SlumberingToraEffect(this); } + @Override + public void init(Ability source, Game game) { + super.init(source, game); + for (Cost cost : source.getCosts()) { + if (cost instanceof DiscardTargetCost && !((DiscardTargetCost) cost).getCards().isEmpty()) { + convManaCosts = ((DiscardTargetCost) cost).getCards().get(0).getConvertedManaCost(); + return; + } + } + } + @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.addCardType(CardType.CREATURE); - permanent.getSubtype(game).add(SubType.CAT); - } - break; - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - int convManaCosts = 0; - for (Cost cost: source.getCosts()) { - if (cost instanceof DiscardTargetCost && !((DiscardTargetCost) cost).getCards().isEmpty()) { - convManaCosts = ((DiscardTargetCost)cost).getCards().get(0).getConvertedManaCost(); - break; - } - } - permanent.getPower().setValue(convManaCosts); - permanent.getToughness().setValue(convManaCosts); - } - } - return true; + if (permanent == null) { + return false; } - return false; + switch (layer) { + case TypeChangingEffects_4: + permanent.addCardType(CardType.ARTIFACT); + permanent.addCardType(CardType.CREATURE); + permanent.addSubType(game, SubType.CAT); + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getPower().setValue(convManaCosts); + permanent.getToughness().setValue(convManaCosts); + } + } + return true; } @Override @@ -97,14 +103,9 @@ public final class SlumberingTora extends CardImpl { return false; } - private void setText() { - staticText = "{this} becomes an X/X Cat artifact creature until end of turn, where X is the discarded card's converted mana cost"; - } - @Override public boolean hasLayer(Layer layer) { return layer == Layer.PTChangingEffects_7 || layer == Layer.TypeChangingEffects_4; } } - } diff --git a/Mage.Sets/src/mage/cards/s/SongMadRuins.java b/Mage.Sets/src/mage/cards/s/SongMadRuins.java deleted file mode 100644 index 6f85341ff51..00000000000 --- a/Mage.Sets/src/mage/cards/s/SongMadRuins.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.s; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.RedManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class SongMadRuins extends CardImpl { - - public SongMadRuins(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Song-Mad Ruins enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {R}. - this.addAbility(new RedManaAbility()); - } - - private SongMadRuins(final SongMadRuins card) { - super(card); - } - - @Override - public SongMadRuins copy() { - return new SongMadRuins(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SongMadTreachery.java b/Mage.Sets/src/mage/cards/s/SongMadTreachery.java index a5e006f57c6..4091bccdf65 100644 --- a/Mage.Sets/src/mage/cards/s/SongMadTreachery.java +++ b/Mage.Sets/src/mage/cards/s/SongMadTreachery.java @@ -1,35 +1,52 @@ package mage.cards.s; +import mage.abilities.common.EntersBattlefieldTappedAbility; 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.abilities.mana.RedManaAbility; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class SongMadTreachery extends CardImpl { +public final class SongMadTreachery extends ModalDoubleFacesCard { public SongMadTreachery(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{3}{R}{R}", + "Song-Mad Ruins", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.s.SongMadRuins.class; + // 1. + // Song-Mad Treachery + // Sorcery // Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. - this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); - this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature")); - this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + this.getLeftHalfCard().getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); + this.getLeftHalfCard().getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature")); + this.getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityTargetEffect( HasteAbility.getInstance(), Duration.EndOfTurn ).setText("It gains haste until end of turn.")); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // 2. + // Song-Mad Ruins + // Land + + // Song-Mad Ruins enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {R}. + this.getRightHalfCard().addAbility(new RedManaAbility()); } private SongMadTreachery(final SongMadTreachery card) { diff --git a/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java b/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java index 8c9b34ca570..4d29690f3fe 100644 --- a/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java +++ b/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; @@ -9,20 +8,14 @@ import mage.abilities.keyword.EnchantAbility; import mage.abilities.mana.GreenManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SongOfTheDryads extends CardImpl { @@ -40,7 +33,6 @@ public final class SongOfTheDryads extends CardImpl { // Enchanted permanent is a colorless Forest land. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesColorlessForestLandEffect())); - } public SongOfTheDryads(final SongOfTheDryads card) { @@ -55,13 +47,13 @@ public final class SongOfTheDryads extends CardImpl { class BecomesColorlessForestLandEffect extends ContinuousEffectImpl { - public BecomesColorlessForestLandEffect() { + BecomesColorlessForestLandEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); this.staticText = "Enchanted permanent is a colorless Forest land"; dependencyTypes.add(DependencyType.BecomeForest); } - public BecomesColorlessForestLandEffect(final BecomesColorlessForestLandEffect effect) { + private BecomesColorlessForestLandEffect(final BecomesColorlessForestLandEffect effect) { super(effect); } @@ -78,38 +70,36 @@ class BecomesColorlessForestLandEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); - if (permanent != null) { - switch (layer) { - case ColorChangingEffects_5: - permanent.getColor(game).setWhite(false); - permanent.getColor(game).setGreen(false); - permanent.getColor(game).setBlack(false); - permanent.getColor(game).setBlue(false); - permanent.getColor(game).setRed(false); - break; - case AbilityAddingRemovingEffects_6: - permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); - break; - case TypeChangingEffects_4: - permanent.removeAllAbilities(source.getSourceId(), game); - permanent.getCardType().clear(); - permanent.addCardType(CardType.LAND); - permanent.getSubtype(game).clear(); - permanent.getSubtype(game).add(SubType.FOREST); - break; - } - return true; - } + if (enchantment == null || enchantment.getAttachedTo() == null) { + return false; } - return false; + Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); + if (permanent == null) { + return false; + } + switch (layer) { + case ColorChangingEffects_5: + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setRed(false); + break; + case TypeChangingEffects_4: + permanent.getCardType().clear(); + permanent.addCardType(CardType.LAND); + permanent.removeAllSubTypes(game); + permanent.addSubType(game, SubType.FOREST); + permanent.removeAllAbilities(source.getSourceId(), game); + permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); + break; + } + return true; } @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 - || layer == Layer.ColorChangingEffects_5 + return layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4; } } diff --git a/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java b/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java index 8d6261aaa67..526d6c4f767 100644 --- a/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java +++ b/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java @@ -95,7 +95,7 @@ enum SorinVengefulBloodlordAdjuster implements TargetAdjuster { class SorinVengefulBloodlordEffect extends ContinuousEffectImpl { SorinVengefulBloodlordEffect() { super(Duration.Custom, Outcome.Neutral); - staticText = "That creature is a vampire in addition to its other types"; + staticText = "That creature is a Vampire in addition to its other types"; } private SorinVengefulBloodlordEffect(final SorinVengefulBloodlordEffect effect) { @@ -119,11 +119,7 @@ class SorinVengefulBloodlordEffect extends ContinuousEffectImpl { } } if (creature != null) { - if (sublayer == SubLayer.NA) { - if (!creature.hasSubtype(SubType.VAMPIRE, game)) { - creature.getSubtype(game).add(SubType.VAMPIRE); - } - } + creature.addSubType(game, SubType.VAMPIRE); return true; } else { this.used = true; diff --git a/Mage.Sets/src/mage/cards/s/SoulSculptor.java b/Mage.Sets/src/mage/cards/s/SoulSculptor.java index 710fe7cc5cc..aba2a78b39b 100644 --- a/Mage.Sets/src/mage/cards/s/SoulSculptor.java +++ b/Mage.Sets/src/mage/cards/s/SoulSculptor.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; @@ -10,29 +9,22 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import static mage.constants.Layer.TypeChangingEffects_4; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class SoulSculptor extends CardImpl { - final String rule = "Target creature becomes an enchantment and loses all abilities until a player casts a creature spell."; + private static final String rule = "Target creature becomes an enchantment and loses all abilities until a player casts a creature spell."; public SoulSculptor(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); @@ -42,7 +34,7 @@ public final class SoulSculptor extends CardImpl { this.toughness = new MageInt(1); // {1}{W}, {tap}: Target creature becomes an enchantment and loses all abilities until a player casts a creature spell. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new SoulSculptorEffect(), SoulSculptorCondition.instance, rule), new ManaCostsImpl("{1}{W}")); + Ability ability = new SimpleActivatedAbility(new ConditionalContinuousEffect(new SoulSculptorEffect(), SoulSculptorCondition.instance, rule), new ManaCostsImpl("{1}{W}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); @@ -90,27 +82,23 @@ class SoulSculptorEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Permanent permanent = affectedObjectList.get(0).getPermanent(game); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getCardType().clear(); - permanent.getSubtype(game).clear(); - if (!permanent.getCardType().contains(CardType.ENCHANTMENT)) { - permanent.getCardType().add(CardType.ENCHANTMENT); - } - } - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - permanent.getAbilities().clear(); - } - break; - } - return true; + if (permanent == null) { + this.discard(); + return false; } - this.discard(); - return false; + switch (layer) { + case TypeChangingEffects_4: + permanent.getCardType().clear(); + permanent.getSubtype(game).retainAll(SubType.getEnchantmentTypes()); + permanent.getCardType().add(CardType.ENCHANTMENT); + break; + case AbilityAddingRemovingEffects_6: + if (sublayer == SubLayer.NA) { + permanent.getAbilities().clear(); + } + break; + } + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SoulfireGrandMaster.java b/Mage.Sets/src/mage/cards/s/SoulfireGrandMaster.java index 5fd5de0fe70..228cc2d4b99 100644 --- a/Mage.Sets/src/mage/cards/s/SoulfireGrandMaster.java +++ b/Mage.Sets/src/mage/cards/s/SoulfireGrandMaster.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -11,11 +10,9 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.GainAbilitySpellsEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.keyword.LifelinkAbility; -import mage.cards.AdventureCardSpell; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.SplitCardHalf; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.FilterObject; @@ -26,6 +23,8 @@ import mage.game.events.ZoneChangeEvent; import mage.game.stack.Spell; import mage.players.Player; +import java.util.UUID; + /** * @author LevelX2 */ @@ -142,15 +141,7 @@ class SoulfireGrandMasterCastFromHandReplacementEffect extends ReplacementEffect && event.getTargetId().equals(spellId)) { if (game.getStack().getFirst() instanceof Spell) { Card cardOfSpell = ((Spell) game.getStack().getFirst()).getCard(); - if (cardOfSpell instanceof SplitCardHalf) { - return ((SplitCardHalf) cardOfSpell).getParentCard().getId().equals(spellId); - } else if (cardOfSpell instanceof AdventureCardSpell) { - return (((AdventureCardSpell) cardOfSpell).getParentCard().getId().equals(spellId)); - } else { - if (cardOfSpell.getId().equals(spellId)) { - return true; - } - } + return cardOfSpell.getMainCard().getId().equals(spellId); } } } diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java b/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java index 07a18848269..ee2c3e2426b 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java +++ b/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java @@ -16,6 +16,7 @@ import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import java.util.HashSet; import java.util.Map; @@ -108,10 +109,12 @@ class TargetTwoNonLandCardsWithSameNameInHand extends TargetCardInHand { } } else { for (UUID cardToCheck : cardsToCheck) { - FilterCard nameFilter = new FilterCard(); Card card = game.getCard(cardToCheck); if (card != null) { - nameFilter.add(new NamePredicate(card.isSplitCard() ? ((SplitCard) card).getLeftHalfCard().getName() : card.getName())); + String nameToSearch = CardUtil.getCardNameForSameNameSearch(card); + FilterCard nameFilter = new FilterCard(); + nameFilter.add(new NamePredicate(nameToSearch)); + if (cardsToCheck.count(nameFilter, game) > 1) { newPossibleTargets.add(cardToCheck); } @@ -133,8 +136,10 @@ class TargetTwoNonLandCardsWithSameNameInHand extends TargetCardInHand { } int possibleCards = 0; for (Card card : cardsToCheck.getCards(game)) { + String nameToSearch = CardUtil.getCardNameForSameNameSearch(card); FilterCard nameFilter = new FilterCard(); - nameFilter.add(new NamePredicate(card.isSplitCard() ? ((SplitCard) card).getLeftHalfCard().getName() : card.getName())); + nameFilter.add(new NamePredicate(nameToSearch)); + if (cardsToCheck.count(nameFilter, game) > 1) { ++possibleCards; } @@ -149,10 +154,12 @@ class TargetTwoNonLandCardsWithSameNameInHand extends TargetCardInHand { if (card != null) { if (targets.size() == 1) { Card card2 = game.getCard(targets.entrySet().iterator().next().getKey()); - return card2 != null && card2.getName().equals(card.getName()); + return CardUtil.haveSameNames(card2, card); } else { + String nameToSearch = CardUtil.getCardNameForSameNameSearch(card); FilterCard nameFilter = new FilterCard(); - nameFilter.add(new NamePredicate(card.isSplitCard() ? ((SplitCard) card).getLeftHalfCard().getName() : card.getName())); + nameFilter.add(new NamePredicate(nameToSearch)); + Player player = game.getPlayer(card.getOwnerId()); return player != null && player.getHand().getCards(nameFilter, game).size() > 1; } diff --git a/Mage.Sets/src/mage/cards/s/SpikefieldCave.java b/Mage.Sets/src/mage/cards/s/SpikefieldCave.java deleted file mode 100644 index 23629612b78..00000000000 --- a/Mage.Sets/src/mage/cards/s/SpikefieldCave.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.s; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.RedManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class SpikefieldCave extends CardImpl { - - public SpikefieldCave(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Spikefield Cave enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {R}. - this.addAbility(new RedManaAbility()); - } - - private SpikefieldCave(final SpikefieldCave card) { - super(card); - } - - @Override - public SpikefieldCave copy() { - return new SpikefieldCave(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SpikefieldHazard.java b/Mage.Sets/src/mage/cards/s/SpikefieldHazard.java index c32777fb09b..878e6126fd1 100644 --- a/Mage.Sets/src/mage/cards/s/SpikefieldHazard.java +++ b/Mage.Sets/src/mage/cards/s/SpikefieldHazard.java @@ -1,30 +1,48 @@ package mage.cards.s; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.ExileTargetIfDiesEffect; +import mage.abilities.mana.RedManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.target.common.TargetAnyTarget; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class SpikefieldHazard extends CardImpl { +public final class SpikefieldHazard extends ModalDoubleFacesCard { public SpikefieldHazard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{R}", + "Spikefield Cave", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.s.SpikefieldCave.class; + // 1. + // Spikefield Hazard + // Instant // Spikefield Hazard deals 1 damage to any target. If a permanent dealt damage this way would die this turn, exile it instead. - this.getSpellAbility().addEffect(new DamageTargetEffect(1)); - this.getSpellAbility().addEffect(new ExileTargetIfDiesEffect() + this.getLeftHalfCard().getSpellAbility().addEffect(new DamageTargetEffect(1)); + this.getLeftHalfCard().getSpellAbility().addEffect(new ExileTargetIfDiesEffect() .setText("If a permanent dealt damage this way would die this turn, exile it instead.")); - this.getSpellAbility().addTarget(new TargetAnyTarget()); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetAnyTarget()); + + // 2. + // Spikefield Cave + // Land + + // Spikefield Cave enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {R}. + this.getRightHalfCard().addAbility(new RedManaAbility()); } private SpikefieldHazard(final SpikefieldHazard card) { diff --git a/Mage.Sets/src/mage/cards/s/Standardize.java b/Mage.Sets/src/mage/cards/s/Standardize.java index b11eea4b55e..ffed5686509 100644 --- a/Mage.Sets/src/mage/cards/s/Standardize.java +++ b/Mage.Sets/src/mage/cards/s/Standardize.java @@ -1,10 +1,7 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BecomesSubtypeAllEffect; @@ -16,8 +13,12 @@ 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.players.Player; +import mage.util.SubTypeList; + +import java.util.UUID; /** * @author EvilGeek @@ -69,8 +70,10 @@ class StandardizeEffect extends OneShotEffect { chosenType = typeChoice.getChoice(); if (chosenType != null && !chosenType.isEmpty()) { // ADD TYPE TO TARGET - ContinuousEffect effect = new BecomesSubtypeAllEffect(Duration.EndOfTurn, SubType.byDescription(chosenType)); - game.addEffect(effect, source); + game.addEffect(new BecomesSubtypeAllEffect( + Duration.EndOfTurn, new SubTypeList(SubType.byDescription(chosenType)), + StaticFilters.FILTER_PERMANENT_CREATURE, true + ), source); return true; } diff --git a/Mage.Sets/src/mage/cards/s/StaunchThroneguard.java b/Mage.Sets/src/mage/cards/s/StaunchThroneguard.java new file mode 100644 index 00000000000..0685a4e1006 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StaunchThroneguard.java @@ -0,0 +1,41 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.BecomesMonarchSourceEffect; +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 StaunchThroneguard extends CardImpl { + + public StaunchThroneguard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // When Staunch Throneguard enters the battlefield, you become the monarch. + this.addAbility(new EntersBattlefieldTriggeredAbility(new BecomesMonarchSourceEffect())); + } + + private StaunchThroneguard(final StaunchThroneguard card) { + super(card); + } + + @Override + public StaunchThroneguard copy() { + return new StaunchThroneguard(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StoneforgeMasterwork.java b/Mage.Sets/src/mage/cards/s/StoneforgeMasterwork.java index cf49576bbdb..42a0e66a2b8 100644 --- a/Mage.Sets/src/mage/cards/s/StoneforgeMasterwork.java +++ b/Mage.Sets/src/mage/cards/s/StoneforgeMasterwork.java @@ -59,7 +59,7 @@ class StoneforgeMasterworkDynamicValue implements DynamicValue { if (equipped != null) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, sourceAbility.getControllerId(), game)) { if (!permanent.getId().equals(equipped.getId())) { - if (equipped.shareSubtypes(permanent, game)) { + if (equipped.shareCreatureTypes(permanent, game)) { xValue++; } } diff --git a/Mage.Sets/src/mage/cards/s/StoneworkPackbeast.java b/Mage.Sets/src/mage/cards/s/StoneworkPackbeast.java index 360c5461bf3..1327ee5efe8 100644 --- a/Mage.Sets/src/mage/cards/s/StoneworkPackbeast.java +++ b/Mage.Sets/src/mage/cards/s/StoneworkPackbeast.java @@ -4,6 +4,7 @@ import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.HasSubtypesSourceEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -22,16 +23,12 @@ public final class StoneworkPackbeast extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); this.subtype.add(SubType.BEAST); - this.subtype.add(SubType.CLERIC); - this.subtype.add(SubType.ROGUE); - this.subtype.add(SubType.WARRIOR); - this.subtype.add(SubType.WIZARD); this.power = new MageInt(2); this.toughness = new MageInt(1); // Stonework Packbeast is also a Cleric, Rogue, Warrior, and Wizard. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new InfoEffect("{this} is also a Cleric, Rogue, Warrior, and Wizard") + Zone.ALL, new HasSubtypesSourceEffect(SubType.CLERIC, SubType.ROGUE, SubType.WARRIOR, SubType.WIZARD) )); // {2}: Add one mana of any color. diff --git a/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java b/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java index 0c46713eafe..80a77f0a8f9 100644 --- a/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java +++ b/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -11,28 +10,23 @@ import mage.abilities.keyword.IslandwalkAbility; import mage.abilities.mana.BlueManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public final class StormtideLeviathan extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures without flying or islandwalk"); + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("Creatures without flying or islandwalk"); static { filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); @@ -48,12 +42,12 @@ public final class StormtideLeviathan extends CardImpl { // Islandwalk (This creature can't be blocked as long as defending player controls an Island.) this.addAbility(new IslandwalkAbility()); - // All lands are Islands in addition to their other types. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new StormtideLeviathanEffect())); - // Creatures without flying or islandwalk can't attack. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new CantAttackAnyPlayerAllEffect(Duration.WhileOnBattlefield, filter))); + // All lands are Islands in addition to their other types. + this.addAbility(new SimpleStaticAbility(new StormtideLeviathanEffect())); + + // Creatures without flying or islandwalk can't attack. + this.addAbility(new SimpleStaticAbility(new CantAttackAnyPlayerAllEffect(Duration.WhileOnBattlefield, filter))); } public StormtideLeviathan(final StormtideLeviathan card) { @@ -67,13 +61,13 @@ public final class StormtideLeviathan extends CardImpl { class StormtideLeviathanEffect extends ContinuousEffectImpl { - public StormtideLeviathanEffect() { - super(Duration.WhileOnBattlefield, Outcome.Neutral); + private StormtideLeviathanEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); staticText = "All lands are Islands in addition to their other types"; this.dependencyTypes.add(DependencyType.BecomeIsland); } - public StormtideLeviathanEffect(final StormtideLeviathanEffect effect) { + private StormtideLeviathanEffect(final StormtideLeviathanEffect effect) { super(effect); } @@ -83,32 +77,17 @@ public final class StormtideLeviathan extends CardImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { for (Permanent land : game.getBattlefield().getActivePermanents( - new FilterLandPermanent(), source.getControllerId(), game)) { - switch (layer) { - case TypeChangingEffects_4: - // land abilities are intrinsic, so add them here, not in layer 6 - if (!land.hasSubtype(SubType.ISLAND, game)) { - land.getSubtype(game).add(SubType.ISLAND); - if (!land.getAbilities(game).containsClass(BlueManaAbility.class)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - } - break; + StaticFilters.FILTER_LAND, source.getControllerId(), game + )) { + // land abilities are intrinsic, so add them here, not in 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 boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } } } diff --git a/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java b/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java index dbecb75abc9..47c622f7b7d 100644 --- a/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java +++ b/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java @@ -5,7 +5,6 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SuperType; @@ -15,12 +14,13 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; import mage.players.Player; +import mage.target.TargetCard; import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; import java.util.List; import java.util.UUID; -import mage.target.TargetCard; /** * @author North @@ -82,8 +82,7 @@ class SurgicalExtractionEffect extends OneShotEffect { if (chosenCard != null && controller != null) { Player owner = game.getPlayer(chosenCard.getOwnerId()); if (owner != null) { - String nameToSearch = chosenCard.isSplitCard() - ? ((SplitCard) chosenCard).getLeftHalfCard().getName() : chosenCard.getName(); + String nameToSearch = CardUtil.getCardNameForSameNameSearch(chosenCard); FilterCard filterNamedCard = new FilterCard("card named " + nameToSearch); filterNamedCard.add(new NamePredicate(nameToSearch)); diff --git a/Mage.Sets/src/mage/cards/s/SweetGumRecluse.java b/Mage.Sets/src/mage/cards/s/SweetGumRecluse.java new file mode 100644 index 00000000000..b043e1c9e96 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SweetGumRecluse.java @@ -0,0 +1,66 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.CascadeAbility; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.ReachAbility; +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.filter.predicate.permanent.EnteredThisTurnPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SweetGumRecluse extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creatures that entered the battlefield this turn"); + + static { + filter.add(EnteredThisTurnPredicate.instance); + } + + public SweetGumRecluse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.SPIDER); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Cascade + this.addAbility(new CascadeAbility()); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // When Sweet-Gum Recluse enters the battlefield, put three +1/+1 counters on each of any number of target creatures that entered the battlefield this turn. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance(3)) + ); + ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter, false)); + this.addAbility(ability); + } + + private SweetGumRecluse(final SweetGumRecluse card) { + super(card); + } + + @Override + public SweetGumRecluse copy() { + return new SweetGumRecluse(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TajuruParagon.java b/Mage.Sets/src/mage/cards/t/TajuruParagon.java index 3f0ca8bbad0..ce3403808d1 100644 --- a/Mage.Sets/src/mage/cards/t/TajuruParagon.java +++ b/Mage.Sets/src/mage/cards/t/TajuruParagon.java @@ -7,11 +7,13 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.KickedCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.InfoEffect; -import mage.abilities.keyword.ChangelingAbility; +import mage.abilities.effects.common.continuous.HasSubtypesSourceEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.*; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicate; import mage.game.Game; @@ -31,16 +33,12 @@ public final class TajuruParagon extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.ELF); - this.subtype.add(SubType.CLERIC); - this.subtype.add(SubType.ROGUE); - this.subtype.add(SubType.WARRIOR); - this.subtype.add(SubType.WIZARD); this.power = new MageInt(3); this.toughness = new MageInt(2); // Tajuru Paragon is also a Cleric, Rogue, Warrior, and Wizard. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new InfoEffect("{this} is also a Cleric, Rogue, Warrior, and Wizard") + Zone.ALL, new HasSubtypesSourceEffect(SubType.CLERIC, SubType.ROGUE, SubType.WARRIOR, SubType.WIZARD) )); // Kicker {3} @@ -115,27 +113,6 @@ class TajuruParagonPredicate implements Predicate { @Override public boolean apply(Card input, Game game) { - boolean isAllA = permanent.isAllCreatureTypes() - || permanent.hasAbility(ChangelingAbility.getInstance(), game); - boolean isAnyA = isAllA || permanent.getSubtype(game) - .stream() - .map(SubType::getSubTypeSet) - .anyMatch(SubTypeSet.CreatureType::equals); - boolean isAllB = input.isAllCreatureTypes() - || input.hasAbility(ChangelingAbility.getInstance(), game); - boolean isAnyB = isAllB || input - .getSubtype(game) - .stream() - .map(SubType::getSubTypeSet) - .anyMatch(SubTypeSet.CreatureType::equals); - if (isAllA) { - return isAllB || isAnyB; - } - return isAnyA - && (isAllB || permanent - .getSubtype(game) - .stream() - .filter(subType -> subType.getSubTypeSet() == SubTypeSet.CreatureType) - .anyMatch(subType -> input.hasSubtype(subType, game))); + return permanent != null && input != null && permanent.shareCreatureTypes(input, game); } } diff --git a/Mage.Sets/src/mage/cards/t/TamiyoCollectorOfTales.java b/Mage.Sets/src/mage/cards/t/TamiyoCollectorOfTales.java index ce2b23fde24..40a6d01f92b 100644 --- a/Mage.Sets/src/mage/cards/t/TamiyoCollectorOfTales.java +++ b/Mage.Sets/src/mage/cards/t/TamiyoCollectorOfTales.java @@ -17,6 +17,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; import java.util.UUID; @@ -131,13 +132,7 @@ class TamiyoCollectorOfTalesEffect extends OneShotEffect { Cards cards2 = new CardsImpl(); player.revealCards(source, cards, game); for (Card card : cards.getCards(game)) { - if (card.isSplitCard()) { - if (((SplitCard) card).getLeftHalfCard().getName().equals(choice.getChoice()) - || ((SplitCard) card).getRightHalfCard().getName().equals(choice.getChoice())) { - cards2.add(card); - } - } - if (card.getName().equals(choice.getChoice())) { + if (CardUtil.haveSameNames(card, choice.getChoice(), game)) { cards2.add(card); } } diff --git a/Mage.Sets/src/mage/cards/t/TangledFlorahedron.java b/Mage.Sets/src/mage/cards/t/TangledFlorahedron.java index fdc483ec19a..c08ce38b7fd 100644 --- a/Mage.Sets/src/mage/cards/t/TangledFlorahedron.java +++ b/Mage.Sets/src/mage/cards/t/TangledFlorahedron.java @@ -1,31 +1,44 @@ package mage.cards.t; import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.mana.GreenManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class TangledFlorahedron extends CardImpl { +public final class TangledFlorahedron extends ModalDoubleFacesCard { public TangledFlorahedron(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.ELEMENTAL}, "{1}{G}", + "Tangled Vale", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.subtype.add(SubType.ELEMENTAL); - this.power = new MageInt(1); - this.toughness = new MageInt(1); - - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.t.TangledVale.class; + // 1. + // Tangled Florahedron + // Creature — Elemental + this.getLeftHalfCard().setPT(new MageInt(1), new MageInt(1)); // {T}: Add {G}. - this.addAbility(new GreenManaAbility()); + this.getLeftHalfCard().addAbility(new GreenManaAbility()); + + // 2. + // Tangled Vale + // Land + + // Tangled Vale enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {G}. + this.getRightHalfCard().addAbility(new GreenManaAbility()); } private TangledFlorahedron(final TangledFlorahedron card) { diff --git a/Mage.Sets/src/mage/cards/t/TangledVale.java b/Mage.Sets/src/mage/cards/t/TangledVale.java deleted file mode 100644 index 52584376621..00000000000 --- a/Mage.Sets/src/mage/cards/t/TangledVale.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.t; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class TangledVale extends CardImpl { - - public TangledVale(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Tangled Vale enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {G}. - this.addAbility(new GreenManaAbility()); - } - - private TangledVale(final TangledVale card) { - super(card); - } - - @Override - public TangledVale copy() { - return new TangledVale(this); - } -} diff --git a/Mage.Sets/src/mage/cards/t/TaureanMauler.java b/Mage.Sets/src/mage/cards/t/TaureanMauler.java index 5dc202dfb03..086ebda27dc 100644 --- a/Mage.Sets/src/mage/cards/t/TaureanMauler.java +++ b/Mage.Sets/src/mage/cards/t/TaureanMauler.java @@ -26,6 +26,7 @@ public final class TaureanMauler extends CardImpl { this.toughness = new MageInt(2); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Whenever an opponent casts a spell, you may put a +1/+1 counter on Taurean Mauler. diff --git a/Mage.Sets/src/mage/cards/t/Terraformer.java b/Mage.Sets/src/mage/cards/t/Terraformer.java index f65370e2fb9..9066a84f0d0 100644 --- a/Mage.Sets/src/mage/cards/t/Terraformer.java +++ b/Mage.Sets/src/mage/cards/t/Terraformer.java @@ -1,8 +1,6 @@ package mage.cards.t; -import java.util.Iterator; -import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; @@ -10,30 +8,21 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.mana.BlackManaAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.abilities.mana.RedManaAbility; -import mage.abilities.mana.WhiteManaAbility; +import mage.abilities.mana.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.choices.ChoiceBasicLandType; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; -import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.common.FilterControlledPermanent; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.Iterator; +import java.util.UUID; + /** - * * @author emerald000 */ public final class Terraformer extends CardImpl { @@ -46,7 +35,7 @@ public final class Terraformer extends CardImpl { this.toughness = new MageInt(2); // {1}: Choose a basic land type. Each land you control becomes that type until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TerraformerEffect(), new GenericManaCost(1))); + this.addAbility(new SimpleActivatedAbility(new TerraformerEffect(), new GenericManaCost(1))); } public Terraformer(final Terraformer card) { @@ -66,7 +55,7 @@ class TerraformerEffect extends OneShotEffect { this.staticText = "Choose a basic land type. Each land you control becomes that type until end of turn"; } - TerraformerEffect(final TerraformerEffect effect) { + private TerraformerEffect(final TerraformerEffect effect) { super(effect); } @@ -92,13 +81,11 @@ class TerraformerEffect extends OneShotEffect { class TerraformerContinuousEffect extends ContinuousEffectImpl { - private static final FilterControlledPermanent filter = new FilterControlledLandPermanent(); - TerraformerContinuousEffect() { - super(Duration.EndOfTurn, Outcome.Neutral); + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); } - TerraformerContinuousEffect(final TerraformerContinuousEffect effect) { + private TerraformerContinuousEffect(final TerraformerContinuousEffect effect) { super(effect); } @@ -110,64 +97,68 @@ class TerraformerContinuousEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { super.init(source, game); - if (this.affectedObjectsSet) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { - affectedObjectList.add(new MageObjectReference(permanent, game)); - } - } - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "_Terraformer")); - if (choice != null) { - for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { - Permanent land = it.next().getPermanent(game); - if (land != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - land.getSubtype(game).clear(); - land.getSubtype(game).add(choice); - } - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - land.getAbilities().clear(); - if (choice.equals(SubType.FOREST)) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.PLAINS)) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.MOUNTAIN)) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.ISLAND)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.SWAMP)) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - } - break; - } - } else { - it.remove(); - } - } - return true; + switch (choice) { + case FOREST: + dependencyTypes.add(DependencyType.BecomeForest); + break; + case PLAINS: + dependencyTypes.add(DependencyType.BecomePlains); + break; + case MOUNTAIN: + dependencyTypes.add(DependencyType.BecomeMountain); + break; + case ISLAND: + dependencyTypes.add(DependencyType.BecomeIsland); + break; + case SWAMP: + dependencyTypes.add(DependencyType.BecomeSwamp); + break; + } + if (this.affectedObjectsSet) { + game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + source.getControllerId(), source.getSourceId(), game + ).stream() + .map(permanent -> new MageObjectReference(permanent, game)) + .forEach(affectedObjectList::add); } - return false; } @Override public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "_ElsewhereFlask")); + if (choice == null) { + return false; + } + for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { + Permanent land = it.next().getPermanent(game); + if (land == null) { + it.remove(); + continue; + } + land.getSubtype(game).removeAll(SubType.getLandTypes()); + land.addSubType(game, choice); + land.removeAllAbilities(source.getSourceId(), game); + switch (choice) { + case FOREST: + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + break; + case PLAINS: + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + break; + case MOUNTAIN: + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + break; + case ISLAND: + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + break; + case SWAMP: + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + break; + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java b/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java index a0980c9bec9..ebe6ac12fdd 100644 --- a/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java +++ b/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java @@ -1,41 +1,38 @@ - package mage.cards.t; -import java.util.UUID; -import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.VariableManaCost; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.common.continuous.SourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.TokenImpl; -import mage.game.permanent.token.Token; + +import java.util.UUID; /** - * - * @author cbt33 + * @author TheElk801 */ public final class TestamentOfFaith extends CardImpl { public TestamentOfFaith(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); // {X}: Testament of Faith becomes an X/X Wall creature with defender in addition to its other types until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TestamentOfFaithBecomesCreatureSourceEffect(new TestamentOfFaithToken(), "enchantment", Duration.EndOfTurn), new VariableManaCost())); + Ability ability = new SimpleActivatedAbility(new SetPowerToughnessSourceEffect( + ManacostVariableValue.instance, Duration.EndOfTurn, SubLayer.SetPT_7b + ).setText("{this} becomes an X/X"), new VariableManaCost()); + ability.addEffect(new TestamentOfFaithEffect()); + ability.addEffect(new GainAbilitySourceEffect( + DefenderAbility.getInstance(), Duration.EndOfTurn + ).setText("with defender in addition to its other types until end of turn")); + this.addAbility(ability); } public TestamentOfFaith(final TestamentOfFaith card) { @@ -48,121 +45,30 @@ public final class TestamentOfFaith extends CardImpl { } } -class TestamentOfFaithBecomesCreatureSourceEffect extends ContinuousEffectImpl implements SourceEffect { +class TestamentOfFaithEffect extends ContinuousEffectImpl { - protected Token token; - protected String type; - protected int zoneChangeCounter; - - public TestamentOfFaithBecomesCreatureSourceEffect(Token token, String type, Duration duration) { - super(duration, Outcome.BecomeCreature); - this.token = token; - this.type = type; - setText(); + TestamentOfFaithEffect() { + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature); + staticText = "Wall creature"; } - public TestamentOfFaithBecomesCreatureSourceEffect(final TestamentOfFaithBecomesCreatureSourceEffect effect) { + private TestamentOfFaithEffect(final TestamentOfFaithEffect effect) { super(effect); - this.token = effect.token.copy(); - this.type = effect.type; } @Override - public TestamentOfFaithBecomesCreatureSourceEffect copy() { - return new TestamentOfFaithBecomesCreatureSourceEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.getAffectedObjects().add(new MageObjectReference(source.getSourceId(), game)); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent permanent = affectedObjectList.get(0).getPermanent(game); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (!token.getCardType().isEmpty()) { - for (CardType t : token.getCardType()) { - if (!permanent.getCardType().contains(t)) { - permanent.addCardType(t); - } - } - } - if (type != null && type.isEmpty() || type == null) { - permanent.getSubtype(game).clear(); - } - if (!token.getSubtype(game).isEmpty()) { - permanent.getSubtype(game).addAll(token.getSubtype(game)); - } - } - break; - case ColorChangingEffects_5: - if (sublayer == SubLayer.NA) { - if (token.getColor(game).hasColor()) { - permanent.getColor(game).setColor(token.getColor(game)); - } - } - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - if (!token.getAbilities().isEmpty()) { - for (Ability ability: token.getAbilities()) { - permanent.addAbility(ability, source.getSourceId(), game); - } - } - } - break; - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - MageInt power = new MageInt(source.getManaCosts().getVariableCosts().get(0).getAmount()); - MageInt toughness = new MageInt(source.getManaCosts().getVariableCosts().get(0).getAmount()); - permanent.getPower().setValue(power.getValue()); - permanent.getToughness().setValue(toughness.getValue()); - } - } - return true; - } else { - if (duration == Duration.Custom) { - this.discard(); - } - } - return false; + public TestamentOfFaithEffect copy() { + return new TestamentOfFaithEffect(this); } @Override public boolean apply(Game game, Ability source) { - return false; + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return false; + } + permanent.addCardType(CardType.CREATURE); + permanent.addSubType(game, SubType.WALL); + return true; } - - private void setText() { - staticText = "{this} becomes an X/X Wall creature with defender in addition to its other types until end of turn"; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.PTChangingEffects_7 || layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4; - } - } - - -class TestamentOfFaithToken extends TokenImpl { - TestamentOfFaithToken() { - super("Wall", "X/X Wall creature with defender"); - cardType.add(CardType.CREATURE); - this.subtype.add(SubType.WALL); - color.setWhite(true); - this.addAbility(DefenderAbility.getInstance()); - } - public TestamentOfFaithToken(final TestamentOfFaithToken token) { - super(token); - } - - public TestamentOfFaithToken copy() { - return new TestamentOfFaithToken(this); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java b/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java index c6e10dfb32a..f78b0a9b3d6 100644 --- a/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java +++ b/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java @@ -136,7 +136,7 @@ class TeveshSzatDoomOfFoolsCommanderEffect extends OneShotEffect { TeveshSzatDoomOfFoolsCommanderEffect() { super(Outcome.Benefit); staticText = "Gain control of all commanders. Put all commanders " + - "from the commander zone onto the battlefield under your control."; + "from the command zone onto the battlefield under your control."; } private TeveshSzatDoomOfFoolsCommanderEffect(final TeveshSzatDoomOfFoolsCommanderEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java b/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java index c47ecd602ac..503a85bc489 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java @@ -141,7 +141,7 @@ class TezzeretCruelMachinistCardTypeEffect extends AddCardTypeTargetEffect { case TypeChangingEffects_4: target.getSuperType().clear(); target.getCardType().clear(); - target.getSubtype(game).clear(); + target.removeAllSubTypes(game); target.addCardType(CardType.ARTIFACT); target.addCardType(CardType.CREATURE); break; diff --git a/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java b/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java index bf93fc6fb61..4784689d15f 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java @@ -134,7 +134,6 @@ class TezzeretTheSeekerEffect3 extends ContinuousEffectImpl { if (!permanent.isCreature()) { permanent.addCardType(CardType.CREATURE); } - permanent.getSubtype(game).clear(); } break; case PTChangingEffects_7: diff --git a/Mage.Sets/src/mage/cards/t/ThalisseReverentMedium.java b/Mage.Sets/src/mage/cards/t/ThalisseReverentMedium.java new file mode 100644 index 00000000000..8b984e57349 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThalisseReverentMedium.java @@ -0,0 +1,108 @@ +package mage.cards.t; + +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.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.SpiritWhiteToken; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThalisseReverentMedium extends CardImpl { + + private static final Hint hint = new ValueHint( + "The number of tokens you created this turn", + ThalisseReverentMediumValue.instance + ); + + public ThalisseReverentMedium(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.CLERIC); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // At the beginning of each end step, create X 1/1 white Spirit creature tokens with flying, where X is the number of tokens you created this turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect( + new SpiritWhiteToken(), ThalisseReverentMediumValue.instance + ), TargetController.ANY, false).addHint(hint), new ThalisseReverentMediumWatcher()); + } + + private ThalisseReverentMedium(final ThalisseReverentMedium card) { + super(card); + } + + @Override + public ThalisseReverentMedium copy() { + return new ThalisseReverentMedium(this); + } +} + +enum ThalisseReverentMediumValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + ThalisseReverentMediumWatcher watcher = game.getState().getWatcher(ThalisseReverentMediumWatcher.class); + return watcher == null ? 0 : watcher.getTokenCount(sourceAbility.getControllerId()); + } + + @Override + public ThalisseReverentMediumValue copy() { + return instance; + } + + @Override + public String getMessage() { + return "the number of tokens you created this turn"; + } + + @Override + public String toString() { + return "X"; + } +} + +class ThalisseReverentMediumWatcher extends Watcher { + + private final Map tokenMap = new HashMap<>(); + + ThalisseReverentMediumWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.CREATED_TOKEN) { + return; + } + tokenMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + } + + @Override + public void reset() { + tokenMap.clear(); + super.reset(); + } + + int getTokenCount(UUID playerId) { + return tokenMap.getOrDefault(playerId, 0); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheAntiquitiesWar.java b/Mage.Sets/src/mage/cards/t/TheAntiquitiesWar.java index 4f125fbec79..bc13743dea1 100644 --- a/Mage.Sets/src/mage/cards/t/TheAntiquitiesWar.java +++ b/Mage.Sets/src/mage/cards/t/TheAntiquitiesWar.java @@ -98,7 +98,6 @@ class TheAntiquitiesWarEffect extends ContinuousEffectImpl { if (!permanent.isCreature()) { permanent.addCardType(CardType.CREATURE); } - permanent.getSubtype(game).clear(); } break; case PTChangingEffects_7: diff --git a/Mage.Sets/src/mage/cards/t/TimbercrownPathway.java b/Mage.Sets/src/mage/cards/t/TimbercrownPathway.java deleted file mode 100644 index d26749c8d0d..00000000000 --- a/Mage.Sets/src/mage/cards/t/TimbercrownPathway.java +++ /dev/null @@ -1,33 +0,0 @@ -package mage.cards.t; - -import mage.abilities.mana.GreenManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class TimbercrownPathway extends CardImpl { - - public TimbercrownPathway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // {T}: Add {G}. - this.addAbility(new GreenManaAbility()); - } - - private TimbercrownPathway(final TimbercrownPathway card) { - super(card); - } - - @Override - public TimbercrownPathway copy() { - return new TimbercrownPathway(this); - } -} diff --git a/Mage.Sets/src/mage/cards/t/TurntimberSerpentineWood.java b/Mage.Sets/src/mage/cards/t/TurntimberSerpentineWood.java deleted file mode 100644 index 8c926b1e063..00000000000 --- a/Mage.Sets/src/mage/cards/t/TurntimberSerpentineWood.java +++ /dev/null @@ -1,42 +0,0 @@ -package mage.cards.t; - -import mage.abilities.common.AsEntersBattlefieldAbility; -import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.effects.common.TapSourceUnlessPaysEffect; -import mage.abilities.mana.GreenManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class TurntimberSerpentineWood extends CardImpl { - - public TurntimberSerpentineWood(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // As Turntimber, Serpentine Wood enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. - this.addAbility(new AsEntersBattlefieldAbility( - new TapSourceUnlessPaysEffect(new PayLifeCost(3)), - "you may pay 3 life. If you don't, it enters the battlefield tapped" - )); - - // {T}: Add {G}. - this.addAbility(new GreenManaAbility()); - } - - private TurntimberSerpentineWood(final TurntimberSerpentineWood card) { - super(card); - } - - @Override - public TurntimberSerpentineWood copy() { - return new TurntimberSerpentineWood(this); - } -} diff --git a/Mage.Sets/src/mage/cards/t/TurntimberSymbiosis.java b/Mage.Sets/src/mage/cards/t/TurntimberSymbiosis.java index c87b32a7a22..fcd045618f6 100644 --- a/Mage.Sets/src/mage/cards/t/TurntimberSymbiosis.java +++ b/Mage.Sets/src/mage/cards/t/TurntimberSymbiosis.java @@ -1,10 +1,15 @@ package mage.cards.t; import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; +import mage.abilities.mana.GreenManaAbility; import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.StaticFilters; @@ -19,16 +24,35 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class TurntimberSymbiosis extends CardImpl { +public final class TurntimberSymbiosis extends ModalDoubleFacesCard { public TurntimberSymbiosis(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}{G}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{4}{G}{G}{G}", + "Turntimber, Serpentine Wood", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.t.TurntimberSerpentineWood.class; + // 1. + // Turntimber Symbiosis + // Sorcery - // Look at the top seven cards of your library. You may put a creature card from among them onto the battlefield. If that card has converted mana cost 3 or less, it enters with three additional +1/+1 counters on it. Put the rest on the bottom of your library in a random order. - this.getSpellAbility().addEffect(new TurntimberSymbiosisEffect()); + // Look at the top seven cards of your library. You may put a creature card from among them onto the battlefield. + // If that card has converted mana cost 3 or less, it enters with three additional +1/+1 counters on it. + // Put the rest on the bottom of your library in a random order. + this.getLeftHalfCard().getSpellAbility().addEffect(new TurntimberSymbiosisEffect()); + + // 2. + // Turntimber, Serpentine Wood + // Land + + // As Turntimber, Serpentine Wood enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. + this.getRightHalfCard().addAbility(new AsEntersBattlefieldAbility( + new TapSourceUnlessPaysEffect(new PayLifeCost(3)), + "you may pay 3 life. If you don't, it enters the battlefield tapped" + )); + + // {T}: Add {G}. + this.getRightHalfCard().addAbility(new GreenManaAbility()); } private TurntimberSymbiosis(final TurntimberSymbiosis card) { @@ -48,7 +72,7 @@ class TurntimberSymbiosisEffect extends OneShotEffect { staticText = "Look at the top seven cards of your library. You may put a creature card " + "from among them onto the battlefield. If that card has converted mana cost 3 or less, " + "it enters with three additional +1/+1 counters on it. " + - "Put the rest on the bottom of your library in a random order."; + "Put the rest on the bottom of your library in a random order"; } private TurntimberSymbiosisEffect(final TurntimberSymbiosisEffect effect) { @@ -66,6 +90,7 @@ class TurntimberSymbiosisEffect extends OneShotEffect { if (player == null) { return false; } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 7)); TargetCard target = new TargetCardInLibrary( 0, 1, StaticFilters.FILTER_CARD_CREATURE @@ -76,6 +101,7 @@ class TurntimberSymbiosisEffect extends OneShotEffect { player.putCardsOnBottomOfLibrary(cards, game, source, false); return true; } + boolean small = card.getConvertedManaCost() <= 3; player.moveCards(card, Zone.BATTLEFIELD, source, game); Permanent permanent = game.getPermanent(card.getId()); diff --git a/Mage.Sets/src/mage/cards/t/TurtleshellChangeling.java b/Mage.Sets/src/mage/cards/t/TurtleshellChangeling.java index a2780fbfe58..4571e67a6c5 100644 --- a/Mage.Sets/src/mage/cards/t/TurtleshellChangeling.java +++ b/Mage.Sets/src/mage/cards/t/TurtleshellChangeling.java @@ -28,6 +28,7 @@ public final class TurtleshellChangeling extends CardImpl { this.toughness = new MageInt(4); //Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); //{1}{U}: Switch {this}'s power and toughness until end of turn. diff --git a/Mage.Sets/src/mage/cards/u/UmaraSkyfalls.java b/Mage.Sets/src/mage/cards/u/UmaraSkyfalls.java deleted file mode 100644 index fefda8b11e4..00000000000 --- a/Mage.Sets/src/mage/cards/u/UmaraSkyfalls.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.u; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class UmaraSkyfalls extends CardImpl { - - public UmaraSkyfalls(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Umara Skyfalls enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {U}. - this.addAbility(new BlueManaAbility()); - } - - private UmaraSkyfalls(final UmaraSkyfalls card) { - super(card); - } - - @Override - public UmaraSkyfalls copy() { - return new UmaraSkyfalls(this); - } -} diff --git a/Mage.Sets/src/mage/cards/u/UmaraWizard.java b/Mage.Sets/src/mage/cards/u/UmaraWizard.java index 373c5f1762e..44806a3727d 100644 --- a/Mage.Sets/src/mage/cards/u/UmaraWizard.java +++ b/Mage.Sets/src/mage/cards/u/UmaraWizard.java @@ -1,11 +1,14 @@ package mage.cards.u; import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.mana.BlueManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; @@ -14,26 +17,36 @@ import mage.filter.StaticFilters; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class UmaraWizard extends CardImpl { +public final class UmaraWizard extends ModalDoubleFacesCard { public UmaraWizard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.MERFOLK, SubType.WIZARD}, "{4}{U}", + "Umara Skyfalls", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.subtype.add(SubType.MERFOLK); - this.subtype.add(SubType.WIZARD); - this.power = new MageInt(4); - this.toughness = new MageInt(3); - - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.u.UmaraSkyfalls.class; + // 1. + // Umara Wizard + // Creature — Merfolk Wizard + this.getLeftHalfCard().setPT(new MageInt(4), new MageInt(3)); // Whenever you cast an instant, sorcery, or Wizard spell, Umara Wizard gains flying until end of turn. - this.addAbility(new SpellCastControllerTriggeredAbility( + this.getLeftHalfCard().addAbility(new SpellCastControllerTriggeredAbility( new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_SPELL_INSTANT_SORCERY_WIZARD, false )); + + // 2. + // Umara Skyfalls + // Land + + // Umara Skyfalls enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {U}. + this.getRightHalfCard().addAbility(new BlueManaAbility()); } private UmaraWizard(final UmaraWizard card) { diff --git a/Mage.Sets/src/mage/cards/u/UniversalAutomaton.java b/Mage.Sets/src/mage/cards/u/UniversalAutomaton.java index 74f505881a1..735b2f1e825 100644 --- a/Mage.Sets/src/mage/cards/u/UniversalAutomaton.java +++ b/Mage.Sets/src/mage/cards/u/UniversalAutomaton.java @@ -22,6 +22,7 @@ public final class UniversalAutomaton extends CardImpl { this.toughness = new MageInt(1); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/u/UnsettledMariner.java b/Mage.Sets/src/mage/cards/u/UnsettledMariner.java index dcc22cf8710..d050604379c 100644 --- a/Mage.Sets/src/mage/cards/u/UnsettledMariner.java +++ b/Mage.Sets/src/mage/cards/u/UnsettledMariner.java @@ -31,6 +31,7 @@ public final class UnsettledMariner extends CardImpl { this.toughness = new MageInt(2); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Whenever you or a 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/u/UrborgTombOfYawgmoth.java b/Mage.Sets/src/mage/cards/u/UrborgTombOfYawgmoth.java index 5e4f2e852be..986af634752 100644 --- a/Mage.Sets/src/mage/cards/u/UrborgTombOfYawgmoth.java +++ b/Mage.Sets/src/mage/cards/u/UrborgTombOfYawgmoth.java @@ -81,7 +81,7 @@ public final class UrborgTombOfYawgmoth extends CardImpl { // So the ability removing has to be done before Layer 6 // Lands have their mana ability intrinsically, so that is added in layer 4 if (!land.getSubtype(game).contains(SubType.SWAMP)) { - land.getSubtype(game).add(SubType.SWAMP); + land.addSubType(game, SubType.SWAMP); } if (!land.getAbilities().containsRule(new BlackManaAbility())) { land.addAbility(new BlackManaAbility(), source.getSourceId(), game); diff --git a/Mage.Sets/src/mage/cards/u/UrsineChampion.java b/Mage.Sets/src/mage/cards/u/UrsineChampion.java index 32847388d8e..ef47de2301d 100644 --- a/Mage.Sets/src/mage/cards/u/UrsineChampion.java +++ b/Mage.Sets/src/mage/cards/u/UrsineChampion.java @@ -1,26 +1,20 @@ package mage.cards.u; -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.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.constants.SubType; 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.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class UrsineChampion extends CardImpl { @@ -53,14 +47,14 @@ public final class UrsineChampion extends CardImpl { return new UrsineChampion(this); } - private class UrsineChampionEffect extends ContinuousEffectImpl { + private static class UrsineChampionEffect extends ContinuousEffectImpl { - public UrsineChampionEffect() { - super(Duration.EndOfTurn, Outcome.BecomeCreature); - setText(); + private UrsineChampionEffect() { + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature); + staticText = "and becomes a Bear Berserker until end of turn"; } - public UrsineChampionEffect(final UrsineChampionEffect effect) { + private UrsineChampionEffect(final UrsineChampionEffect effect) { super(effect); } @@ -70,35 +64,14 @@ public final class UrsineChampion extends CardImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent == null) { return false; } - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getSubtype(game).clear(); - permanent.getSubtype(game).add(SubType.BEAR); - permanent.getSubtype(game).add(SubType.BERSERKER); - } - break; - } + permanent.removeAllCreatureTypes(game); + permanent.addSubType(game, SubType.BEAR, SubType.BERSERKER); return true; } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - private void setText() { - staticText = "and becomes a Bear Berserker until end of turn"; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } } } diff --git a/Mage.Sets/src/mage/cards/u/UrzasHotTub.java b/Mage.Sets/src/mage/cards/u/UrzasHotTub.java index 6a816e8d97b..2e20988c315 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasHotTub.java +++ b/Mage.Sets/src/mage/cards/u/UrzasHotTub.java @@ -1,7 +1,5 @@ - package mage.cards.u; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -10,10 +8,7 @@ import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.SplitCard; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SpellAbilityType; @@ -25,14 +20,15 @@ import mage.game.stack.Spell; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author L_J */ public class UrzasHotTub extends CardImpl { public UrzasHotTub(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // {2}, Discard a card: Search your library for a card that shares a complete word in its name with the discarded card, reveal it, put it into your hand, then shuffle your library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new UrzasHotTubEffect(), new ManaCostsImpl("{2}")); @@ -95,21 +91,23 @@ class UrzasHotTubPredicate implements Predicate { public boolean apply(MageObject input, Game game) { String name = input.getName(); if (input instanceof SplitCard) { - return sharesWordWithName(((SplitCard)input).getLeftHalfCard().getName()) || sharesWordWithName(((SplitCard)input).getRightHalfCard().getName()); - } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED){ - SplitCard card = (SplitCard) ((Spell)input).getCard(); + return sharesWordWithName(((SplitCard) input).getLeftHalfCard().getName()) || sharesWordWithName(((SplitCard) input).getRightHalfCard().getName()); + } else if (input instanceof ModalDoubleFacesCard) { + return sharesWordWithName(((ModalDoubleFacesCard) input).getLeftHalfCard().getName()) || sharesWordWithName(((ModalDoubleFacesCard) input).getRightHalfCard().getName()); + } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { + SplitCard card = (SplitCard) ((Spell) input).getCard(); return sharesWordWithName(card.getLeftHalfCard().getName()) || sharesWordWithName(card.getRightHalfCard().getName()); } else { if (name.contains(" // ")) { String leftName = name.substring(0, name.indexOf(" // ")); - String rightName = name.substring(name.indexOf(" // ") + 4, name.length()); + String rightName = name.substring(name.indexOf(" // ") + 4); return sharesWordWithName(leftName) || sharesWordWithName(rightName); } else { return sharesWordWithName(name); } } } - + private boolean sharesWordWithName(String str) { if (referenceName == null || referenceName == "") { return false; diff --git a/Mage.Sets/src/mage/cards/v/ValakutAwakening.java b/Mage.Sets/src/mage/cards/v/ValakutAwakening.java index cf591f4a6aa..214c83fe4c1 100644 --- a/Mage.Sets/src/mage/cards/v/ValakutAwakening.java +++ b/Mage.Sets/src/mage/cards/v/ValakutAwakening.java @@ -1,13 +1,13 @@ package mage.cards.v; import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.abilities.mana.RedManaAbility; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; @@ -19,16 +19,30 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class ValakutAwakening extends CardImpl { +public final class ValakutAwakening extends ModalDoubleFacesCard { public ValakutAwakening(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{2}{R}", + "Valakut Stoneforge", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.v.ValakutStoneforge.class; + // 1. + // Valakut Awakening + // Instant // Put any number of cards from your hand on the bottom of your library, then draw that many cards plus one. - this.getSpellAbility().addEffect(new ValakutAwakeningEffect()); + this.getLeftHalfCard().getSpellAbility().addEffect(new ValakutAwakeningEffect()); + + // 2. + // Valakut Stoneforge + // Land + + // Valakut Stoneforge enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {R}. + this.getRightHalfCard().addAbility(new RedManaAbility()); } private ValakutAwakening(final ValakutAwakening card) { @@ -69,9 +83,6 @@ class ValakutAwakeningEffect extends OneShotEffect { ); player.choose(outcome, player.getHand(), targetCard, game); Cards cards = new CardsImpl(targetCard.getTargets()); - if (cards.isEmpty()) { - return false; - } player.putCardsOnBottomOfLibrary(cards, game, source, true); player.drawCards(cards.size() + 1, source.getSourceId(), game); return true; diff --git a/Mage.Sets/src/mage/cards/v/ValakutStoneforge.java b/Mage.Sets/src/mage/cards/v/ValakutStoneforge.java deleted file mode 100644 index add770d138e..00000000000 --- a/Mage.Sets/src/mage/cards/v/ValakutStoneforge.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.v; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.RedManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class ValakutStoneforge extends CardImpl { - - public ValakutStoneforge(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Valakut Stoneforge enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {R}. - this.addAbility(new RedManaAbility()); - } - - private ValakutStoneforge(final ValakutStoneforge card) { - super(card); - } - - @Override - public ValakutStoneforge copy() { - return new ValakutStoneforge(this); - } -} diff --git a/Mage.Sets/src/mage/cards/v/ValiantChangeling.java b/Mage.Sets/src/mage/cards/v/ValiantChangeling.java index e562dd6e0c6..5177f12e583 100644 --- a/Mage.Sets/src/mage/cards/v/ValiantChangeling.java +++ b/Mage.Sets/src/mage/cards/v/ValiantChangeling.java @@ -35,6 +35,7 @@ public final class ValiantChangeling extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.ALL, new ValiantChangelingCostReductionEffect())); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Double strike diff --git a/Mage.Sets/src/mage/cards/v/VastwoodFortification.java b/Mage.Sets/src/mage/cards/v/VastwoodFortification.java index c341edc1800..ba57b770253 100644 --- a/Mage.Sets/src/mage/cards/v/VastwoodFortification.java +++ b/Mage.Sets/src/mage/cards/v/VastwoodFortification.java @@ -1,28 +1,46 @@ package mage.cards.v; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.mana.GreenManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class VastwoodFortification extends CardImpl { +public final class VastwoodFortification extends ModalDoubleFacesCard { public VastwoodFortification(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + super(ownerId, setInfo, + new CardType[]{CardType.INSTANT}, new SubType[]{}, "{G}", + "Vastwood Thicket", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.v.VastwoodThicket.class; + // 1. + // Vastwood Fortification + // Instant // Put a +1/+1 counter on target creature. - this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getLeftHalfCard().getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // 2. + // Vastwood Thicket + // Land + + // Vastwood Thicket enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {G}. + this.getRightHalfCard().addAbility(new GreenManaAbility()); } private VastwoodFortification(final VastwoodFortification card) { diff --git a/Mage.Sets/src/mage/cards/v/VastwoodThicket.java b/Mage.Sets/src/mage/cards/v/VastwoodThicket.java deleted file mode 100644 index 418cd4a379b..00000000000 --- a/Mage.Sets/src/mage/cards/v/VastwoodThicket.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.v; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class VastwoodThicket extends CardImpl { - - public VastwoodThicket(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Vastwood Thicket enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {G}. - this.addAbility(new GreenManaAbility()); - } - - private VastwoodThicket(final VastwoodThicket card) { - super(card); - } - - @Override - public VastwoodThicket copy() { - return new VastwoodThicket(this); - } -} diff --git a/Mage.Sets/src/mage/cards/v/VeiledSentry.java b/Mage.Sets/src/mage/cards/v/VeiledSentry.java index 933b8aaca4c..a625f8f072a 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledSentry.java +++ b/Mage.Sets/src/mage/cards/v/VeiledSentry.java @@ -1,6 +1,5 @@ package mage.cards.v; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbility; import mage.abilities.common.SpellCastOpponentTriggeredAbility; @@ -9,24 +8,16 @@ import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import static mage.constants.Layer.PTChangingEffects_7; -import static mage.constants.Layer.TypeChangingEffects_4; -import mage.constants.Outcome; -import mage.constants.SetTargetPointer; -import mage.constants.SubLayer; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class VeiledSentry extends CardImpl { @@ -79,15 +70,10 @@ class VeiledSentryEffect extends ContinuousEffectImpl { } switch (layer) { case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - veiledSentry.getCardType().clear(); - if (!veiledSentry.isCreature()) { - veiledSentry.addCardType(CardType.CREATURE); - } - if (!veiledSentry.getSubtype(game).contains(SubType.ILLUSION)) { - veiledSentry.getSubtype(game).add(SubType.ILLUSION); - } - } + veiledSentry.getCardType().clear(); + veiledSentry.removeAllSubTypes(game); + veiledSentry.addCardType(CardType.CREATURE); + veiledSentry.addSubType(game, SubType.ILLUSION); break; case PTChangingEffects_7: diff --git a/Mage.Sets/src/mage/cards/v/VenomousChangeling.java b/Mage.Sets/src/mage/cards/v/VenomousChangeling.java index eec819e7085..956c7fa3ac9 100644 --- a/Mage.Sets/src/mage/cards/v/VenomousChangeling.java +++ b/Mage.Sets/src/mage/cards/v/VenomousChangeling.java @@ -23,6 +23,7 @@ public final class VenomousChangeling extends CardImpl { this.toughness = new MageInt(3); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Deathtouch diff --git a/Mage.Sets/src/mage/cards/v/VeteranAdventurer.java b/Mage.Sets/src/mage/cards/v/VeteranAdventurer.java index 10f54a1c836..b5da4fa64d0 100644 --- a/Mage.Sets/src/mage/cards/v/VeteranAdventurer.java +++ b/Mage.Sets/src/mage/cards/v/VeteranAdventurer.java @@ -3,7 +3,7 @@ package mage.cards.v; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.PartyCount; -import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.HasSubtypesSourceEffect; import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; import mage.abilities.hint.common.PartyCountHint; import mage.abilities.keyword.VigilanceAbility; @@ -24,15 +24,13 @@ public final class VeteranAdventurer extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}"); this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.CLERIC); - this.subtype.add(SubType.ROGUE); - this.subtype.add(SubType.WARRIOR); - this.subtype.add(SubType.WIZARD); this.power = new MageInt(5); this.toughness = new MageInt(5); // Veteran Adventurer is also a Cleric, Rogue, Warrior, and Wizard. - this.addAbility(new SimpleStaticAbility(new InfoEffect("{this} is also a Cleric, Rogue, Warrior, and Wizard"))); + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new HasSubtypesSourceEffect(SubType.CLERIC, SubType.ROGUE, SubType.WARRIOR, SubType.WIZARD) + )); // This spell costs {1} less to cast for each creature in your party. this.addAbility(new SimpleStaticAbility( diff --git a/Mage.Sets/src/mage/cards/v/VisionCharm.java b/Mage.Sets/src/mage/cards/v/VisionCharm.java index 9d8920bbd05..73dc1433791 100644 --- a/Mage.Sets/src/mage/cards/v/VisionCharm.java +++ b/Mage.Sets/src/mage/cards/v/VisionCharm.java @@ -1,19 +1,13 @@ package mage.cards.v; -import java.util.Iterator; -import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.PhaseOutTargetEffect; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; -import mage.abilities.mana.BlackManaAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.abilities.mana.RedManaAbility; -import mage.abilities.mana.WhiteManaAbility; +import mage.abilities.mana.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.Choice; @@ -28,8 +22,10 @@ import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetArtifactPermanent; +import java.util.Iterator; +import java.util.UUID; + /** - * * @author tre3qwerty */ public final class VisionCharm extends CardImpl { @@ -42,18 +38,15 @@ public final class VisionCharm extends CardImpl { this.getSpellAbility().addTarget(new TargetPlayer()); // or choose a land type and a basic land type. Each land of the first chosen type becomes the second chosen type until end of turn; - Mode mode = new Mode(); - mode.addEffect(new VisionCharmEffect()); - this.getSpellAbility().addMode(mode); + this.getSpellAbility().addMode(new Mode(new VisionCharmEffect())); // or target artifact phases out. - mode = new Mode(); - mode.addEffect(new PhaseOutTargetEffect()); + Mode mode = new Mode(new PhaseOutTargetEffect()); mode.addTarget(new TargetArtifactPermanent()); this.getSpellAbility().addMode(mode); } - public VisionCharm(final VisionCharm card) { + private VisionCharm(final VisionCharm card) { super(card); } @@ -68,12 +61,12 @@ class VisionCharmEffect extends ContinuousEffectImpl { private String targetLandType; private SubType targetBasicLandType; - public VisionCharmEffect() { - super(Duration.EndOfTurn, Outcome.Neutral); + VisionCharmEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); staticText = "Choose a land type and a basic land type. Each land of the first chosen type becomes the second chosen type until end of turn."; } - public VisionCharmEffect(final VisionCharmEffect effect) { + private VisionCharmEffect(final VisionCharmEffect effect) { super(effect); targetLandType = effect.targetLandType; targetBasicLandType = effect.targetBasicLandType; @@ -119,51 +112,35 @@ class VisionCharmEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - // TODO fix to use SubType enum - for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { + public boolean apply(Game game, Ability source) { + for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { Permanent land = it.next().getPermanent(game); - if (land != null) { - switch (layer) { - case TypeChangingEffects_4: - land.getSubtype(game).clear(); - land.getSubtype(game).add(targetBasicLandType); - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - land.getAbilities().clear(); - if (targetBasicLandType.equals(SubType.FOREST)) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (targetBasicLandType.equals(SubType.PLAINS)) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (targetBasicLandType.equals(SubType.MOUNTAIN)) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (targetBasicLandType.equals(SubType.ISLAND)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (targetBasicLandType.equals(SubType.SWAMP)) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - } - } - } else { + if (land == null) { it.remove(); + continue; + } + land.getSubtype(game).removeAll(SubType.getLandTypes()); + land.addSubType(game, targetBasicLandType); + land.removeAllAbilities(source.getSourceId(), game); + switch (targetBasicLandType) { + case FOREST: + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + break; + case PLAINS: + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + break; + case MOUNTAIN: + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + break; + case ISLAND: + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + break; + case SWAMP: + land.addAbility(new BlackManaAbility(), 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.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; - } } diff --git a/Mage.Sets/src/mage/cards/v/VizierOfManyFaces.java b/Mage.Sets/src/mage/cards/v/VizierOfManyFaces.java index e449f79c6d5..75a3ddd30eb 100644 --- a/Mage.Sets/src/mage/cards/v/VizierOfManyFaces.java +++ b/Mage.Sets/src/mage/cards/v/VizierOfManyFaces.java @@ -47,7 +47,6 @@ public final class VizierOfManyFaces extends CardImpl { // Embalm {3}{U}{U} this.addAbility(new EmbalmAbility(new ManaCostsImpl("{3}{U}{U}"), this)); - } public VizierOfManyFaces(final VizierOfManyFaces card) { @@ -70,21 +69,21 @@ class VizierOfManyFacesApplyToPermanent extends ApplyToPermanent { @Override public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { for (Permanent entering : game.getPermanentsEntering().values()) { - if (entering.getId().equals(copyToObjectId) && entering instanceof PermanentToken) { - UUID originalCardId = ((PermanentToken) entering).getToken().getCopySourceCard().getId(); - EmbalmedThisTurnWatcher watcher = game.getState().getWatcher(EmbalmedThisTurnWatcher.class); - if (watcher != null) { - for (MageObjectReference mor : watcher.getEmbalmedThisTurnCards()) { - if (Objects.equals(mor.getSourceId(), originalCardId) && game.getState().getZoneChangeCounter(originalCardId) == mor.getZoneChangeCounter()) { - permanent.getManaCost().clear(); - if (!permanent.hasSubtype(SubType.ZOMBIE, game)) { - permanent.getSubtype(game).add(SubType.ZOMBIE); - } - permanent.getColor(game).setColor(ObjectColor.WHITE); - - } - } + if (!entering.getId().equals(copyToObjectId) || !(entering instanceof PermanentToken)) { + continue; + } + UUID originalCardId = ((PermanentToken) entering).getToken().getCopySourceCard().getId(); + EmbalmedThisTurnWatcher watcher = game.getState().getWatcher(EmbalmedThisTurnWatcher.class); + if (watcher == null) { + continue; + } + for (MageObjectReference mor : watcher.getEmbalmedThisTurnCards()) { + if (!Objects.equals(mor.getSourceId(), originalCardId) || game.getState().getZoneChangeCounter(originalCardId) != mor.getZoneChangeCounter()) { + continue; } + permanent.getManaCost().clear(); + permanent.addSubType(game, SubType.ZOMBIE); + permanent.getColor(game).setColor(ObjectColor.WHITE); } } return true; @@ -117,5 +116,4 @@ class EmbalmedThisTurnWatcher extends Watcher { super.reset(); embalmedThisTurnTokens.clear(); } - } diff --git a/Mage.Sets/src/mage/cards/v/VolatileClaws.java b/Mage.Sets/src/mage/cards/v/VolatileClaws.java index a336762c305..9d06a0c6fbf 100644 --- a/Mage.Sets/src/mage/cards/v/VolatileClaws.java +++ b/Mage.Sets/src/mage/cards/v/VolatileClaws.java @@ -1,14 +1,17 @@ package mage.cards.v; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.abilities.keyword.ChangelingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; +import mage.constants.*; import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import java.util.Iterator; import java.util.UUID; /** @@ -23,10 +26,7 @@ public final class VolatileClaws extends CardImpl { this.getSpellAbility().addEffect(new BoostControlledEffect( 2, 0, Duration.EndOfTurn ).setText("until end of turn, creatures you control get +2/+0")); - this.getSpellAbility().addEffect(new GainAbilityControlledEffect( - ChangelingAbility.getInstance(), Duration.EndOfTurn, - StaticFilters.FILTER_PERMANENT_CREATURE - ).setText("and gain all creature types")); + this.getSpellAbility().addEffect(new VolatileClawsEffect()); } private VolatileClaws(final VolatileClaws card) { @@ -38,3 +38,43 @@ public final class VolatileClaws extends CardImpl { return new VolatileClaws(this); } } + +class VolatileClawsEffect extends ContinuousEffectImpl { + + VolatileClawsEffect() { + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); + staticText = "and gain all creature types"; + } + + private VolatileClawsEffect(final VolatileClawsEffect effect) { + super(effect); + } + + @Override + public VolatileClawsEffect copy() { + return new VolatileClawsEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + for (Permanent perm : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURES, source.getControllerId(), source.getSourceId(), game + )) { + affectedObjectList.add(new MageObjectReference(perm, 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(); // no longer on the battlefield, remove reference to object + continue; + } + permanent.setIsAllCreatureTypes(true); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VolrathsShapeshifter.java b/Mage.Sets/src/mage/cards/v/VolrathsShapeshifter.java index a2d7f982487..04d4eeef9d1 100644 --- a/Mage.Sets/src/mage/cards/v/VolrathsShapeshifter.java +++ b/Mage.Sets/src/mage/cards/v/VolrathsShapeshifter.java @@ -1,6 +1,5 @@ package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -15,6 +14,8 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** * @author ImperatorPrime */ @@ -84,11 +85,10 @@ class VolrathsShapeshifterEffect extends ContinuousEffectImpl { permanent.addCardType(type); } - permanent.getSubtype(game).clear(); + permanent.removeAllSubTypes(game); + permanent.setIsAllCreatureTypes(card.isAllCreatureTypes()); for (SubType type : card.getSubtype(game)) { - if (!permanent.hasSubtype(type, game)) { - permanent.getSubtype(game).add(type); - } + permanent.addSubType(game, type); } permanent.getSuperType().clear(); diff --git a/Mage.Sets/src/mage/cards/v/VowOfTorment.java b/Mage.Sets/src/mage/cards/v/VowOfTorment.java new file mode 100644 index 00000000000..7b993b8601c --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VowOfTorment.java @@ -0,0 +1,54 @@ +package mage.cards.v; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantAttackControllerAttachedEffect; +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.*; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VowOfTorment extends CardImpl { + + public VowOfTorment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + 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 gets +2/+2, has menace, and can't attack you or a planeswalker you control. + ability = new SimpleStaticAbility(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield)); + ability.addEffect(new GainAbilityAttachedEffect( + new MenaceAbility(), AttachmentType.AURA, Duration.WhileOnBattlefield + ).setText(", has menace")); + ability.addEffect(new CantAttackControllerAttachedEffect(AttachmentType.AURA) + .setText(", and can't attack you or a planeswalker you control")); + this.addAbility(ability); + } + + private VowOfTorment(final VowOfTorment card) { + super(card); + } + + @Override + public VowOfTorment copy() { + return new VowOfTorment(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WallOfStolenIdentity.java b/Mage.Sets/src/mage/cards/w/WallOfStolenIdentity.java index 842336238cf..845850f9fa3 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfStolenIdentity.java +++ b/Mage.Sets/src/mage/cards/w/WallOfStolenIdentity.java @@ -3,28 +3,32 @@ package mage.cards.w; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.keyword.DefenderAbility; -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.target.TargetPermanent; -import mage.util.functions.ApplyToPermanent; -import java.util.UUID; 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.effects.EntersBattlefieldEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +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.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.Target; +import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import mage.util.functions.ApplyToPermanent; + +import java.util.UUID; /** * @author TheElk801 @@ -46,11 +50,8 @@ public final class WallOfStolenIdentity extends CardImpl { // 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( - Zone.BATTLEFIELD, - new EntersBattlefieldEffect( - new WallOfStolenIdentityCopyEffect(), - rule, - true)); + new EntersBattlefieldEffect(new WallOfStolenIdentityCopyEffect(), rule, true) + ); this.addAbility(ability); } @@ -86,53 +87,54 @@ class WallOfStolenIdentityCopyEffect extends OneShotEffect { permanent = game.getPermanentEntering(source.getSourceId()); } final Permanent sourcePermanent = permanent; - if (controller != null - && sourcePermanent != null) { - Target target = new TargetPermanent(new FilterCreaturePermanent("target creature (you copy from)")); - target.setRequired(true); - if (source instanceof SimpleStaticAbility) { - target = new TargetPermanent(new FilterCreaturePermanent("creature (you copy from)")); - target.setRequired(false); - target.setNotTarget(true); - } - if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) { - controller.choose(Outcome.Copy, target, source.getSourceId(), game); - Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget()); - if (copyFromPermanent != null) { - game.copyPermanent(copyFromPermanent, sourcePermanent.getId(), source, new ApplyToPermanent() { - @Override - public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { - permanent.getSubtype(game).add(SubType.WALL); - permanent.getAbilities().add(DefenderAbility.getInstance()); - return true; - } - - @Override - public boolean apply(Game game, MageObject mageObject, Ability source, UUID copyToObjectId) { - mageObject.getSubtype(game).add(SubType.WALL); - mageObject.getAbilities().add(DefenderAbility.getInstance()); - return true; - } - - }); - - copyFromPermanent.tap(game); - // Incredibly, you can't just add a fixed target to a continuousrulemodifyingeffect, thus the workaround. - ContinuousRuleModifyingEffect effect = new DontUntapInControllersUntapStepSourceEffect(); - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, 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); - return true; - } - } + if (controller == null + || sourcePermanent == null) { + return false; } - return false; + Target target = new TargetPermanent(new FilterCreaturePermanent("target creature (you copy from)")); + target.setRequired(true); + if (source instanceof SimpleStaticAbility) { + target = new TargetPermanent(new FilterCreaturePermanent("creature (you copy from)")); + target.setRequired(false); + target.setNotTarget(true); + } + if (!target.canChoose(source.getSourceId(), source.getControllerId(), game)) { + return false; + } + controller.choose(Outcome.Copy, target, source.getSourceId(), game); + Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget()); + if (copyFromPermanent == null) { + return false; + } + game.copyPermanent(copyFromPermanent, sourcePermanent.getId(), source, new ApplyToPermanent() { + @Override + public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { + permanent.addSubType(game, SubType.WALL); + permanent.getAbilities().add(DefenderAbility.getInstance()); + return true; + } + + @Override + public boolean apply(Game game, MageObject mageObject, Ability source, UUID copyToObjectId) { + mageObject.addSubType(game, SubType.WALL); + mageObject.getAbilities().add(DefenderAbility.getInstance()); + return true; + } + }); + + copyFromPermanent.tap(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); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/w/WarRoom.java b/Mage.Sets/src/mage/cards/w/WarRoom.java new file mode 100644 index 00000000000..3d73f81e6c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WarRoom.java @@ -0,0 +1,102 @@ +package mage.cards.w; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +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.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterMana; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WarRoom extends CardImpl { + + public WarRoom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {3}, {T}, Pay life equal to the number of colors in your commanders' color identity: Draw a card. + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), new GenericManaCost(3) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new PayLifeCost( + WarRoomValue.instance, "life equal to the number of colors in your commanders' color identity" + )); + this.addAbility(ability); + } + + private WarRoom(final WarRoom card) { + super(card); + } + + @Override + public WarRoom copy() { + return new WarRoom(this); + } +} + +enum WarRoomValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Player controller = game.getPlayer(sourceAbility.getControllerId()); + if (controller == null) { + return 0; + } + ObjectColor color = new ObjectColor(); + // if no commander then cost can't be paid + boolean hasCommander = false; + for (UUID commanderId : game.getCommandersIds(controller)) { + Card commander = game.getCard(commanderId); + if (commander == null) { + continue; + } + FilterMana commanderMana = commander.getColorIdentity(); + if (commanderMana.isWhite()) { + color.setWhite(true); + } + if (commanderMana.isBlue()) { + color.setBlue(true); + } + if (commanderMana.isBlack()) { + color.setBlack(true); + } + if (commanderMana.isRed()) { + color.setRed(true); + } + if (commanderMana.isGreen()) { + color.setGreen(true); + } + hasCommander = true; + } + return hasCommander ? color.getColorCount() : Integer.MAX_VALUE; + } + + @Override + public WarRoomValue copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/w/WarSpikeChangeling.java b/Mage.Sets/src/mage/cards/w/WarSpikeChangeling.java index 2be2e235842..0b9e1bb25e5 100644 --- a/Mage.Sets/src/mage/cards/w/WarSpikeChangeling.java +++ b/Mage.Sets/src/mage/cards/w/WarSpikeChangeling.java @@ -24,6 +24,7 @@ public final class WarSpikeChangeling extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), new ColoredManaCost(ColoredManaSymbol.R))); } diff --git a/Mage.Sets/src/mage/cards/w/WebweaverChangeling.java b/Mage.Sets/src/mage/cards/w/WebweaverChangeling.java index e96b7e72e40..58f136aa132 100644 --- a/Mage.Sets/src/mage/cards/w/WebweaverChangeling.java +++ b/Mage.Sets/src/mage/cards/w/WebweaverChangeling.java @@ -28,6 +28,7 @@ public final class WebweaverChangeling extends CardImpl { this.toughness = new MageInt(5); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Reach diff --git a/Mage.Sets/src/mage/cards/w/WeightOfConscience.java b/Mage.Sets/src/mage/cards/w/WeightOfConscience.java index 73e09126cb9..49f6d7c665e 100644 --- a/Mage.Sets/src/mage/cards/w/WeightOfConscience.java +++ b/Mage.Sets/src/mage/cards/w/WeightOfConscience.java @@ -134,7 +134,7 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent { Permanent firstTargetCreature = game.getPermanent(firstTargetId); if (firstTargetCreature != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) { - if (!permanent.getId().equals(firstTargetId) && firstTargetCreature.shareSubtypes(permanent, game)) { + if (!permanent.getId().equals(firstTargetId) && firstTargetCreature.shareCreatureTypes(permanent, game)) { possibleTargets.add(permanent.getId()); } } @@ -148,7 +148,7 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent { public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { for (Permanent permanent1 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) { for (Permanent permanent2 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) { - if (!Objects.equals(permanent1, permanent2) && permanent1.shareSubtypes(permanent2, game)) { + if (!Objects.equals(permanent1, permanent2) && permanent1.shareCreatureTypes(permanent2, game)) { return true; } } @@ -173,7 +173,7 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent { } } else { Permanent firstTarget = game.getPermanent(this.getTargets().get(0)); - if (firstTarget != null && firstTarget.shareSubtypes(targetPermanent, game)) { + if (firstTarget != null && firstTarget.shareCreatureTypes(targetPermanent, game)) { return true; } } diff --git a/Mage.Sets/src/mage/cards/w/WildwoodScourge.java b/Mage.Sets/src/mage/cards/w/WildwoodScourge.java index 737b4d5b49e..a67d78efe1a 100644 --- a/Mage.Sets/src/mage/cards/w/WildwoodScourge.java +++ b/Mage.Sets/src/mage/cards/w/WildwoodScourge.java @@ -33,7 +33,7 @@ public final class WildwoodScourge extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); // Whenever one or more +1/+1 counters are put on another non-Hydra creature you control, put a +1/+1 counter on Wildwood Scourge. - this.addAbility(new EnduringScalelordTriggeredAbility()); + this.addAbility(new WildwoodScourgeTriggeredAbility()); } private WildwoodScourge(final WildwoodScourge card) { @@ -46,19 +46,19 @@ public final class WildwoodScourge extends CardImpl { } } -class EnduringScalelordTriggeredAbility extends TriggeredAbilityImpl { +class WildwoodScourgeTriggeredAbility extends TriggeredAbilityImpl { - EnduringScalelordTriggeredAbility() { + WildwoodScourgeTriggeredAbility() { super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); } - private EnduringScalelordTriggeredAbility(final EnduringScalelordTriggeredAbility ability) { + private WildwoodScourgeTriggeredAbility(final WildwoodScourgeTriggeredAbility ability) { super(ability); } @Override - public EnduringScalelordTriggeredAbility copy() { - return new EnduringScalelordTriggeredAbility(this); + public WildwoodScourgeTriggeredAbility copy() { + return new WildwoodScourgeTriggeredAbility(this); } @Override @@ -73,11 +73,11 @@ class EnduringScalelordTriggeredAbility extends TriggeredAbilityImpl { if (permanent == null) { permanent = game.getPermanentEntering(event.getTargetId()); } - return (permanent != null + return permanent != null && !event.getTargetId().equals(this.getSourceId()) && permanent.isCreature() - && !permanent.getSubtype(game).contains(SubType.HYDRA) - && permanent.isControlledBy(this.getControllerId())); + && !permanent.hasSubtype(SubType.HYDRA, game) + && permanent.isControlledBy(this.getControllerId()); } return false; } diff --git a/Mage.Sets/src/mage/cards/w/WingsOfVelisVel.java b/Mage.Sets/src/mage/cards/w/WingsOfVelisVel.java index 87cb49aa9c7..85dd21bbb8b 100644 --- a/Mage.Sets/src/mage/cards/w/WingsOfVelisVel.java +++ b/Mage.Sets/src/mage/cards/w/WingsOfVelisVel.java @@ -1,47 +1,40 @@ - package mage.cards.w; -import java.util.UUID; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainAllCreatureTypesTargetEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessTargetEffect; import mage.abilities.keyword.ChangelingAbility; 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.Layer; -import mage.constants.SubLayer; +import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class WingsOfVelisVel extends CardImpl { public WingsOfVelisVel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.INSTANT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.INSTANT}, "{1}{U}"); this.subtype.add(SubType.SHAPESHIFTER); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); // Target creature becomes 4/4, gains all creature types, and gains flying until end of turn. + this.getSpellAbility().addEffect(new SetPowerToughnessTargetEffect(4, 4, Duration.EndOfTurn) + .setText("Target creature becomes 4/4")); + this.getSpellAbility().addEffect(new GainAllCreatureTypesTargetEffect(Duration.EndOfTurn) + .setText(", gains all creature types")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn) + .setText(", and gains flying until end of turn")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - Effect effect = new SetPowerToughnessTargetEffect(4, 4, Duration.EndOfTurn); - effect.setText("Target creature becomes 4/4"); - this.getSpellAbility().addEffect(effect); - - effect = new GainAbilityTargetEffect(ChangelingAbility.getInstance(), Duration.EndOfTurn, null, false, Layer.TypeChangingEffects_4, SubLayer.NA); - effect.setText(", gains all creature types"); - this.getSpellAbility().addEffect(effect); - - effect = new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); - effect.setText(", and gains flying until end of turn"); - this.getSpellAbility().addEffect(effect); } public WingsOfVelisVel(final WingsOfVelisVel card) { diff --git a/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java b/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java index 0badf53c393..92dc12451e6 100644 --- a/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java +++ b/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java @@ -15,7 +15,6 @@ import mage.game.permanent.Permanent; import java.util.UUID; /** - * * @author jmharmon */ @@ -32,7 +31,7 @@ public final class WishfulMerfolk extends CardImpl { this.addAbility(DefenderAbility.getInstance()); // {1}{U}: Wishful Merfolk loses defender and becomes a Human until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new WishfulMerfolkEffect(), new ManaCostsImpl("{1}{U}"))); + this.addAbility(new SimpleActivatedAbility(new WishfulMerfolkEffect(), new ManaCostsImpl("{1}{U}"))); } public WishfulMerfolk(final WishfulMerfolk card) { @@ -64,23 +63,19 @@ class WishfulMerfolkEffect extends ContinuousEffectImpl { @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); - } - break; - case TypeChangingEffects_4: - if (permanent.getSubtype(game).contains(SubType.MERFOLK)) { - permanent.getSubtype(game).clear(); - permanent.getSubtype(game).add(SubType.HUMAN); - } - break; - } - return true; + if (permanent == null) { + return false; } - return false; + switch (layer) { + case AbilityAddingRemovingEffects_6: + permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game); + break; + case TypeChangingEffects_4: + permanent.removeAllCreatureTypes(game); + permanent.addSubType(game, SubType.HUMAN); + break; + } + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/w/WoodlandChangeling.java b/Mage.Sets/src/mage/cards/w/WoodlandChangeling.java index c475a823e81..081425f6c86 100644 --- a/Mage.Sets/src/mage/cards/w/WoodlandChangeling.java +++ b/Mage.Sets/src/mage/cards/w/WoodlandChangeling.java @@ -22,6 +22,7 @@ public final class WoodlandChangeling extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); // Changeling + this.setIsAllCreatureTypes(true); this.addAbility(ChangelingAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/x/Xenograft.java b/Mage.Sets/src/mage/cards/x/Xenograft.java index 2e08278a7a9..cce12a2e17e 100644 --- a/Mage.Sets/src/mage/cards/x/Xenograft.java +++ b/Mage.Sets/src/mage/cards/x/Xenograft.java @@ -1,8 +1,6 @@ package mage.cards.x; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; @@ -15,8 +13,10 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.List; +import java.util.UUID; + /** - * * @author North */ public final class Xenograft extends CardImpl { @@ -54,16 +54,16 @@ class XenograftAddSubtypeEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { SubType subType = ChooseCreatureTypeEffect.getChosenCreatureType(source.getSourceId(), game); - if (subType != null) { - List permanents = game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game); - for (Permanent permanent : permanents) { - if (permanent != null && !permanent.hasSubtype(subType, game)) { - permanent.getSubtype(game).add(subType); - } - } - return true; + if (subType == null) { + return false; } - return false; + List permanents = game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game); + for (Permanent permanent : permanents) { + if (permanent != null) { + permanent.addSubType(game, subType); + } + } + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/z/ZofBloodbog.java b/Mage.Sets/src/mage/cards/z/ZofBloodbog.java deleted file mode 100644 index eb8b68346fa..00000000000 --- a/Mage.Sets/src/mage/cards/z/ZofBloodbog.java +++ /dev/null @@ -1,37 +0,0 @@ -package mage.cards.z; - -import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.mana.BlackManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public final class ZofBloodbog extends CardImpl { - - public ZofBloodbog(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - - this.modalDFC = true; - this.nightCard = true; - - // Zof Bloodbog enters the battlefield tapped. - this.addAbility(new EntersBattlefieldTappedAbility()); - - // {T}: Add {B}. - this.addAbility(new BlackManaAbility()); - } - - private ZofBloodbog(final ZofBloodbog card) { - super(card); - } - - @Override - public ZofBloodbog copy() { - return new ZofBloodbog(this); - } -} diff --git a/Mage.Sets/src/mage/cards/z/ZofConsumption.java b/Mage.Sets/src/mage/cards/z/ZofConsumption.java index 0a4fc18ec7b..40fa33648e0 100644 --- a/Mage.Sets/src/mage/cards/z/ZofConsumption.java +++ b/Mage.Sets/src/mage/cards/z/ZofConsumption.java @@ -1,27 +1,45 @@ package mage.cards.z; +import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.mana.BlackManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; +import mage.constants.SubType; import java.util.UUID; /** - * @author TheElk801 + * @author JayDi85 */ -public final class ZofConsumption extends CardImpl { +public final class ZofConsumption extends ModalDoubleFacesCard { public ZofConsumption(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}"); + super(ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{4}{B}{B}", + "Zof Bloodbog", new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); - this.modalDFC = true; - this.secondSideCardClazz = mage.cards.z.ZofBloodbog.class; + // 1. + // Zof Consumption + // Sorcery // Each opponent loses 4 life and you gain 4 life. - this.getSpellAbility().addEffect(new LoseLifeOpponentsEffect(4)); - this.getSpellAbility().addEffect(new GainLifeEffect(4).concatBy("and")); + this.getLeftHalfCard().getSpellAbility().addEffect(new LoseLifeOpponentsEffect(4)); + this.getLeftHalfCard().getSpellAbility().addEffect(new GainLifeEffect(4).concatBy("and")); + + // 2. + // Zof Bloodbog + // Land + + // Zof Bloodbog enters the battlefield tapped. + this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {B}. + this.getRightHalfCard().addAbility(new BlackManaAbility()); } private ZofConsumption(final ZofConsumption card) { diff --git a/Mage.Sets/src/mage/sets/CommanderLegends.java b/Mage.Sets/src/mage/sets/CommanderLegends.java index 12d6afd2175..3d49f05ee25 100644 --- a/Mage.Sets/src/mage/sets/CommanderLegends.java +++ b/Mage.Sets/src/mage/sets/CommanderLegends.java @@ -27,40 +27,169 @@ public final class CommanderLegends extends ExpansionSet { this.ratioBoosterMythic = 8; this.maxCardNumberInBooster = 361; + cards.add(new SetCardInfo("Acidic Slime", 421, Rarity.UNCOMMON, mage.cards.a.AcidicSlime.class)); cards.add(new SetCardInfo("Alena, Kessig Trapper", 160, Rarity.UNCOMMON, mage.cards.a.AlenaKessigTrapper.class)); + cards.add(new SetCardInfo("Amareth, the Lustrous", 266, Rarity.RARE, mage.cards.a.AmarethTheLustrous.class)); + cards.add(new SetCardInfo("Amphin Mutineer", 55, Rarity.RARE, mage.cards.a.AmphinMutineer.class)); + cards.add(new SetCardInfo("Ancestral Blade", 5, Rarity.COMMON, mage.cards.a.AncestralBlade.class)); + cards.add(new SetCardInfo("Ancient Animus", 215, Rarity.COMMON, mage.cards.a.AncientAnimus.class)); + cards.add(new SetCardInfo("Apex Devastator", 217, Rarity.MYTHIC, mage.cards.a.ApexDevastator.class)); + cards.add(new SetCardInfo("Arcane Signet", 297, Rarity.UNCOMMON, mage.cards.a.ArcaneSignet.class)); + cards.add(new SetCardInfo("Archon of Coronation", 9, Rarity.MYTHIC, mage.cards.a.ArchonOfCoronation.class)); + cards.add(new SetCardInfo("Armillary Sphere", 298, Rarity.COMMON, mage.cards.a.ArmillarySphere.class)); + cards.add(new SetCardInfo("Armix, Filigree Thrasher", 108, Rarity.UNCOMMON, mage.cards.a.ArmixFiligreeThrasher.class)); + cards.add(new SetCardInfo("Aurora Phoenix", 161, Rarity.RARE, mage.cards.a.AuroraPhoenix.class)); + cards.add(new SetCardInfo("Austere Command", 12, Rarity.RARE, mage.cards.a.AustereCommand.class)); + cards.add(new SetCardInfo("Averna, the Chaos Bloom", 269, Rarity.RARE, mage.cards.a.AvernaTheChaosBloom.class)); + cards.add(new SetCardInfo("Bitter Revelation", 109, Rarity.COMMON, mage.cards.b.BitterRevelation.class)); cards.add(new SetCardInfo("Bladegriff Prototype", 300, Rarity.RARE, mage.cards.b.BladegriffPrototype.class)); + cards.add(new SetCardInfo("Blasphemous Act", 162, Rarity.RARE, mage.cards.b.BlasphemousAct.class)); + cards.add(new SetCardInfo("Brago, King Eternal", 516, Rarity.MYTHIC, mage.cards.b.BragoKingEternal.class)); + cards.add(new SetCardInfo("Brazen Freebooter", 164, Rarity.COMMON, mage.cards.b.BrazenFreebooter.class)); cards.add(new SetCardInfo("Briarblade Adept", 111, Rarity.COMMON, mage.cards.b.BriarbladeAdept.class)); + cards.add(new SetCardInfo("Brinelin, the Moon Kraken", 60, Rarity.UNCOMMON, mage.cards.b.BrinelinTheMoonKraken.class)); + cards.add(new SetCardInfo("Cage of Hands", 14, Rarity.COMMON, mage.cards.c.CageOfHands.class)); + cards.add(new SetCardInfo("Captain's Call", 15, Rarity.COMMON, mage.cards.c.CaptainsCall.class)); + cards.add(new SetCardInfo("Cast Down", 112, Rarity.UNCOMMON, mage.cards.c.CastDown.class)); + cards.add(new SetCardInfo("Charcoal Diamond", 303, Rarity.COMMON, mage.cards.c.CharcoalDiamond.class)); + cards.add(new SetCardInfo("Coastline Marauders", 168, Rarity.UNCOMMON, mage.cards.c.CoastlineMarauders.class)); + cards.add(new SetCardInfo("Command Beacon", 349, Rarity.RARE, mage.cards.c.CommandBeacon.class)); cards.add(new SetCardInfo("Command Tower", 350, Rarity.COMMON, mage.cards.c.CommandTower.class)); cards.add(new SetCardInfo("Commander's Sphere", 306, Rarity.COMMON, mage.cards.c.CommandersSphere.class)); cards.add(new SetCardInfo("Confiscate", 62, Rarity.UNCOMMON, mage.cards.c.Confiscate.class)); + cards.add(new SetCardInfo("Court Street Denizen", 17, Rarity.COMMON, mage.cards.c.CourtStreetDenizen.class)); + cards.add(new SetCardInfo("Court of Bounty", 220, Rarity.RARE, mage.cards.c.CourtOfBounty.class)); + cards.add(new SetCardInfo("Court of Cunning", 63, Rarity.RARE, mage.cards.c.CourtOfCunning.class)); + cards.add(new SetCardInfo("Court of Grace", 16, Rarity.RARE, mage.cards.c.CourtOfGrace.class)); + cards.add(new SetCardInfo("Cuombajj Witches", 116, Rarity.UNCOMMON, mage.cards.c.CuombajjWitches.class)); + cards.add(new SetCardInfo("Demonic Lore", 118, Rarity.UNCOMMON, mage.cards.d.DemonicLore.class)); + cards.add(new SetCardInfo("Doomed Traveler", 19, Rarity.COMMON, mage.cards.d.DoomedTraveler.class)); + cards.add(new SetCardInfo("Dragon Mantle", 174, Rarity.COMMON, mage.cards.d.DragonMantle.class)); + cards.add(new SetCardInfo("Eligeth, Crossroads Augur", 66, Rarity.RARE, mage.cards.e.EligethCrossroadsAugur.class)); + cards.add(new SetCardInfo("Emberwilde Captain", 175, Rarity.RARE, mage.cards.e.EmberwildeCaptain.class)); + cards.add(new SetCardInfo("Entourage of Trest", 224, Rarity.COMMON, mage.cards.e.EntourageOfTrest.class)); + cards.add(new SetCardInfo("Exquisite Huntmaster", 122, Rarity.COMMON, mage.cards.e.ExquisiteHuntmaster.class)); + cards.add(new SetCardInfo("Eyeblight Cullers", 124, Rarity.COMMON, mage.cards.e.EyeblightCullers.class)); + cards.add(new SetCardInfo("Fall from Favor", 68, Rarity.COMMON, mage.cards.f.FallFromFavor.class)); + cards.add(new SetCardInfo("Farhaven Elf", 225, Rarity.COMMON, mage.cards.f.FarhavenElf.class)); + cards.add(new SetCardInfo("First Response", 22, Rarity.UNCOMMON, mage.cards.f.FirstResponse.class)); + cards.add(new SetCardInfo("Fleshbag Marauder", 128, Rarity.COMMON, mage.cards.f.FleshbagMarauder.class)); + cards.add(new SetCardInfo("Flood of Recollection", 61, Rarity.UNCOMMON, mage.cards.f.FloodOfRecollection.class)); + cards.add(new SetCardInfo("Forceful Denial", 69, Rarity.COMMON, mage.cards.f.ForcefulDenial.class)); + cards.add(new SetCardInfo("Furnace Celebration", 181, Rarity.UNCOMMON, mage.cards.f.FurnaceCelebration.class)); + cards.add(new SetCardInfo("Fyndhorn Elves", 228, Rarity.COMMON, mage.cards.f.FyndhornElves.class)); + cards.add(new SetCardInfo("Ghen, Arcanum Weaver", 275, Rarity.RARE, mage.cards.g.GhenArcanumWeaver.class)); + cards.add(new SetCardInfo("Ghost of Ramirez DePietro", 71, Rarity.UNCOMMON, mage.cards.g.GhostOfRamirezDePietro.class)); + cards.add(new SetCardInfo("Gift of Paradise", 229, Rarity.COMMON, mage.cards.g.GiftOfParadise.class)); + cards.add(new SetCardInfo("Gnostro, Voice of the Crags", 276, Rarity.RARE, mage.cards.g.GnostroVoiceOfTheCrags.class)); + cards.add(new SetCardInfo("Gor Muldrak, Amphinologist", 277, Rarity.RARE, mage.cards.g.GorMuldrakAmphinologist.class)); cards.add(new SetCardInfo("Halana, Kessig Ranger", 231, Rarity.UNCOMMON, mage.cards.h.HalanaKessigRanger.class)); cards.add(new SetCardInfo("Horizon Scholar", 73, Rarity.UNCOMMON, mage.cards.h.HorizonScholar.class)); + cards.add(new SetCardInfo("Horizon Stone", 315, Rarity.RARE, mage.cards.h.HorizonStone.class)); + cards.add(new SetCardInfo("Hunter's Insight", 232, Rarity.UNCOMMON, mage.cards.h.HuntersInsight.class)); + cards.add(new SetCardInfo("Ikra Shidiqi, the Usurper", 519, Rarity.MYTHIC, mage.cards.i.IkraShidiqiTheUsurper.class)); + cards.add(new SetCardInfo("Immaculate Magistrate", 234, Rarity.RARE, mage.cards.i.ImmaculateMagistrate.class)); + cards.add(new SetCardInfo("Imperious Perfect", 235, Rarity.UNCOMMON, mage.cards.i.ImperiousPerfect.class)); + cards.add(new SetCardInfo("Inspiring Roar", 23, Rarity.COMMON, mage.cards.i.InspiringRoar.class)); + cards.add(new SetCardInfo("Intangible Virtue", 24, Rarity.UNCOMMON, mage.cards.i.IntangibleVirtue.class)); cards.add(new SetCardInfo("Interpret the Signs", 75, Rarity.UNCOMMON, mage.cards.i.InterpretTheSigns.class)); + cards.add(new SetCardInfo("Jeweled Lotus", 319, Rarity.MYTHIC, mage.cards.j.JeweledLotus.class)); + cards.add(new SetCardInfo("Kamahl, Heart of Krosa", 237, Rarity.MYTHIC, mage.cards.k.KamahlHeartOfKrosa.class)); + cards.add(new SetCardInfo("Kediss, Emberclaw Familiar", 188, Rarity.UNCOMMON, mage.cards.k.KedissEmberclawFamiliar.class)); cards.add(new SetCardInfo("Keeper of the Accord", 27, Rarity.RARE, mage.cards.k.KeeperOfTheAccord.class)); + cards.add(new SetCardInfo("Keleth, Sunmane Familiar", 28, Rarity.UNCOMMON, mage.cards.k.KelethSunmaneFamiliar.class)); + cards.add(new SetCardInfo("Keskit, the Flesh Sculptor", 131, Rarity.UNCOMMON, mage.cards.k.KeskitTheFleshSculptor.class)); + cards.add(new SetCardInfo("Kinsbaile Courier", 29, Rarity.COMMON, mage.cards.k.KinsbaileCourier.class)); + cards.add(new SetCardInfo("Kitesail Corsair", 76, Rarity.COMMON, mage.cards.k.KitesailCorsair.class)); + cards.add(new SetCardInfo("Kitesail Skirmisher", 77, Rarity.COMMON, mage.cards.k.KitesailSkirmisher.class)); + cards.add(new SetCardInfo("Kodama of the East Tree", 239, Rarity.RARE, mage.cards.k.KodamaOfTheEastTree.class)); + cards.add(new SetCardInfo("Kor Cartographer", 30, Rarity.COMMON, mage.cards.k.KorCartographer.class)); + cards.add(new SetCardInfo("Krark, the Thumbless", 189, Rarity.RARE, mage.cards.k.KrarkTheThumbless.class)); + cards.add(new SetCardInfo("Kydele, Chosen of Kruphix", 524, Rarity.MYTHIC, mage.cards.k.KydeleChosenOfKruphix.class)); + cards.add(new SetCardInfo("Laboratory Drudge", 78, Rarity.RARE, mage.cards.l.LaboratoryDrudge.class)); + cards.add(new SetCardInfo("Livio, Oathsworn Sentinel", 31, Rarity.RARE, mage.cards.l.LivioOathswornSentinel.class)); + cards.add(new SetCardInfo("Ludevic, Necro-Alchemist", 525, Rarity.MYTHIC, mage.cards.l.LudevicNecroAlchemist.class)); cards.add(new SetCardInfo("Maelstrom Colossus", 322, Rarity.COMMON, mage.cards.m.MaelstromColossus.class)); + cards.add(new SetCardInfo("Maelstrom Wanderer", 526, Rarity.MYTHIC, mage.cards.m.MaelstromWanderer.class)); + cards.add(new SetCardInfo("Makeshift Munitions", 191, Rarity.COMMON, mage.cards.m.MakeshiftMunitions.class)); cards.add(new SetCardInfo("Mana Confluence", 721, Rarity.MYTHIC, mage.cards.m.ManaConfluence.class)); cards.add(new SetCardInfo("Marble Diamond", 323, Rarity.COMMON, mage.cards.m.MarbleDiamond.class)); cards.add(new SetCardInfo("Mask of Memory", 324, Rarity.UNCOMMON, mage.cards.m.MaskOfMemory.class)); + cards.add(new SetCardInfo("Meteoric Mace", 192, Rarity.UNCOMMON, mage.cards.m.MeteoricMace.class)); cards.add(new SetCardInfo("Mindless Automaton", 326, Rarity.UNCOMMON, mage.cards.m.MindlessAutomaton.class)); + cards.add(new SetCardInfo("Mnemonic Deluge", 82, Rarity.MYTHIC, mage.cards.m.MnemonicDeluge.class)); + cards.add(new SetCardInfo("Mulldrifter", 400, Rarity.UNCOMMON, mage.cards.m.Mulldrifter.class)); + cards.add(new SetCardInfo("Myriad Landscape", 487, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); + cards.add(new SetCardInfo("Najeela, the Blade-Blossom", 514, Rarity.MYTHIC, mage.cards.n.NajeelaTheBladeBlossom.class)); + cards.add(new SetCardInfo("Natural Reclamation", 245, Rarity.COMMON, mage.cards.n.NaturalReclamation.class)); + cards.add(new SetCardInfo("Nekusar, the Mindrazer", 529, Rarity.MYTHIC, mage.cards.n.NekusarTheMindrazer.class)); + cards.add(new SetCardInfo("Ninth Bridge Patrol", 33, Rarity.COMMON, mage.cards.n.NinthBridgePatrol.class)); + cards.add(new SetCardInfo("Null Caller", 140, Rarity.UNCOMMON, mage.cards.n.NullCaller.class)); + cards.add(new SetCardInfo("Numa, Joraga Chieftain", 246, Rarity.UNCOMMON, mage.cards.n.NumaJoragaChieftain.class)); + cards.add(new SetCardInfo("Nymris, Oona's Trickster", 288, Rarity.RARE, mage.cards.n.NymrisOonasTrickster.class)); + cards.add(new SetCardInfo("Obeka, Brute Chronologist", 289, Rarity.RARE, mage.cards.o.ObekaBruteChronologist.class)); + cards.add(new SetCardInfo("Omenspeaker", 83, Rarity.COMMON, mage.cards.o.Omenspeaker.class)); + cards.add(new SetCardInfo("Path of Ancestry", 353, Rarity.COMMON, mage.cards.p.PathOfAncestry.class)); + cards.add(new SetCardInfo("Patron of the Valiant", 37, Rarity.UNCOMMON, mage.cards.p.PatronOfTheValiant.class)); + cards.add(new SetCardInfo("Perilous Myr", 330, Rarity.COMMON, mage.cards.p.PerilousMyr.class)); + cards.add(new SetCardInfo("Phyrexian Rager", 142, Rarity.COMMON, mage.cards.p.PhyrexianRager.class)); cards.add(new SetCardInfo("Phyrexian Triniform", 331, Rarity.MYTHIC, mage.cards.p.PhyrexianTriniform.class)); + cards.add(new SetCardInfo("Prava of the Steel Legion", 38, Rarity.UNCOMMON, mage.cards.p.PravaOfTheSteelLegion.class)); + cards.add(new SetCardInfo("Preordain", 84, Rarity.COMMON, mage.cards.p.Preordain.class)); + cards.add(new SetCardInfo("Profane Transfusion", 145, Rarity.MYTHIC, mage.cards.p.ProfaneTransfusion.class)); + cards.add(new SetCardInfo("Prophetic Prism", 334, Rarity.COMMON, mage.cards.p.PropheticPrism.class)); cards.add(new SetCardInfo("Prossh, Skyraider of Kher", 530, Rarity.MYTHIC, mage.cards.p.ProsshSkyraiderOfKher.class)); cards.add(new SetCardInfo("Prying Eyes", 86, Rarity.COMMON, mage.cards.p.PryingEyes.class)); + cards.add(new SetCardInfo("Queen Marchesa", 531, Rarity.MYTHIC, mage.cards.q.QueenMarchesa.class)); + cards.add(new SetCardInfo("Radiant, Serra Archangel", 40, Rarity.UNCOMMON, mage.cards.r.RadiantSerraArchangel.class)); cards.add(new SetCardInfo("Raise the Alarm", 41, Rarity.COMMON, mage.cards.r.RaiseTheAlarm.class)); + cards.add(new SetCardInfo("Rakdos, Lord of Riots", 532, Rarity.MYTHIC, mage.cards.r.RakdosLordOfRiots.class)); + cards.add(new SetCardInfo("Ramos, Dragon Engine", 545, Rarity.MYTHIC, mage.cards.r.RamosDragonEngine.class)); + cards.add(new SetCardInfo("Ravos, Soultender", 533, Rarity.MYTHIC, mage.cards.r.RavosSoultender.class)); cards.add(new SetCardInfo("Rebbec, Architect of Ascension", 42, Rarity.UNCOMMON, mage.cards.r.RebbecArchitectOfAscension.class)); cards.add(new SetCardInfo("Rejuvenating Springs", 354, Rarity.RARE, mage.cards.r.RejuvenatingSprings.class)); + cards.add(new SetCardInfo("Reliquary Tower", 488, Rarity.UNCOMMON, mage.cards.r.ReliquaryTower.class)); + cards.add(new SetCardInfo("Return to Dust", 43, Rarity.UNCOMMON, mage.cards.r.ReturnToDust.class)); + cards.add(new SetCardInfo("Rings of Brighthearth", 335, Rarity.RARE, mage.cards.r.RingsOfBrighthearth.class)); + cards.add(new SetCardInfo("Rograkh, Son of Rohgahh", 197, Rarity.UNCOMMON, mage.cards.r.RograkhSonOfRohgahh.class)); + cards.add(new SetCardInfo("Run Away Together", 87, Rarity.COMMON, mage.cards.r.RunAwayTogether.class)); + cards.add(new SetCardInfo("Sandstone Oracle", 336, Rarity.UNCOMMON, mage.cards.s.SandstoneOracle.class)); + cards.add(new SetCardInfo("Scroll Rack", 337, Rarity.MYTHIC, mage.cards.s.ScrollRack.class)); cards.add(new SetCardInfo("Sengir, the Dark Baron", 149, Rarity.RARE, mage.cards.s.SengirTheDarkBaron.class)); cards.add(new SetCardInfo("Siani, Eye of the Storm", 95, Rarity.UNCOMMON, mage.cards.s.SianiEyeOfTheStorm.class)); + cards.add(new SetCardInfo("Sidar Kondo of Jamuraa", 535, Rarity.MYTHIC, mage.cards.s.SidarKondoOfJamuraa.class)); + cards.add(new SetCardInfo("Silas Renn, Seeker Adept", 536, Rarity.MYTHIC, mage.cards.s.SilasRennSeekerAdept.class)); cards.add(new SetCardInfo("Siren Stormtamer", 96, Rarity.UNCOMMON, mage.cards.s.SirenStormtamer.class)); + cards.add(new SetCardInfo("Sky Diamond", 341, Rarity.COMMON, mage.cards.s.SkyDiamond.class)); + cards.add(new SetCardInfo("Slash the Ranks", 47, Rarity.RARE, mage.cards.s.SlashTheRanks.class)); + cards.add(new SetCardInfo("Soul's Fire", 200, Rarity.COMMON, mage.cards.s.SoulsFire.class)); + cards.add(new SetCardInfo("Soul's Might", 257, Rarity.COMMON, mage.cards.s.SoulsMight.class)); cards.add(new SetCardInfo("Spectator Seating", 356, Rarity.RARE, mage.cards.s.SpectatorSeating.class)); + cards.add(new SetCardInfo("Staff of Domination", 343, Rarity.RARE, mage.cards.s.StaffOfDomination.class)); + cards.add(new SetCardInfo("Staunch Throneguard", 344, Rarity.COMMON, mage.cards.s.StaunchThroneguard.class)); cards.add(new SetCardInfo("Supreme Will", 102, Rarity.UNCOMMON, mage.cards.s.SupremeWill.class)); + cards.add(new SetCardInfo("Sweet-Gum Recluse", 260, Rarity.RARE, mage.cards.s.SweetGumRecluse.class)); cards.add(new SetCardInfo("Tevesh Szat, Doom of Fools", 153, Rarity.MYTHIC, mage.cards.t.TeveshSzatDoomOfFools.class)); + cards.add(new SetCardInfo("Thalisse, Reverent Medium", 291, Rarity.UNCOMMON, mage.cards.t.ThalisseReverentMedium.class)); cards.add(new SetCardInfo("Thorn of the Black Rose", 154, Rarity.COMMON, mage.cards.t.ThornOfTheBlackRose.class)); + cards.add(new SetCardInfo("Thrasios, Triton Hero", 538, Rarity.MYTHIC, mage.cards.t.ThrasiosTritonHero.class)); + cards.add(new SetCardInfo("Three Visits", 261, Rarity.UNCOMMON, mage.cards.t.ThreeVisits.class)); cards.add(new SetCardInfo("Training Center", 358, Rarity.RARE, mage.cards.t.TrainingCenter.class)); + cards.add(new SetCardInfo("Tymna the Weaver", 539, Rarity.MYTHIC, mage.cards.t.TymnaTheWeaver.class)); cards.add(new SetCardInfo("Undergrowth Stadium", 359, Rarity.RARE, mage.cards.u.UndergrowthStadium.class)); + cards.add(new SetCardInfo("Valakut Invoker", 206, Rarity.COMMON, mage.cards.v.ValakutInvoker.class)); cards.add(new SetCardInfo("Vampiric Tutor", 156, Rarity.MYTHIC, mage.cards.v.VampiricTutor.class)); cards.add(new SetCardInfo("Vault of Champions", 360, Rarity.RARE, mage.cards.v.VaultOfChampions.class)); cards.add(new SetCardInfo("Vial Smasher the Fierce", 540, Rarity.MYTHIC, mage.cards.v.VialSmasherTheFierce.class)); + cards.add(new SetCardInfo("Vow of Duty", 54, Rarity.UNCOMMON, mage.cards.v.VowOfDuty.class)); + cards.add(new SetCardInfo("Vow of Flight", 105, Rarity.UNCOMMON, mage.cards.v.VowOfFlight.class)); + cards.add(new SetCardInfo("Vow of Lightning", 209, Rarity.UNCOMMON, mage.cards.v.VowOfLightning.class)); + cards.add(new SetCardInfo("Vow of Torment", 159, Rarity.UNCOMMON, mage.cards.v.VowOfTorment.class)); + cards.add(new SetCardInfo("Vow of Wildness", 262, Rarity.UNCOMMON, mage.cards.v.VowOfWildness.class)); + cards.add(new SetCardInfo("War Room", 361, Rarity.RARE, mage.cards.w.WarRoom.class)); cards.add(new SetCardInfo("Warden of Evos Isle", 106, Rarity.UNCOMMON, mage.cards.w.WardenOfEvosIsle.class)); + cards.add(new SetCardInfo("Wild Celebrants", 212, Rarity.COMMON, mage.cards.w.WildCelebrants.class)); cards.add(new SetCardInfo("Xenagos, God of Revels", 541, Rarity.MYTHIC, mage.cards.x.XenagosGodOfRevels.class)); + cards.add(new SetCardInfo("Zur the Enchanter", 544, Rarity.MYTHIC, mage.cards.z.ZurTheEnchanter.class)); } } diff --git a/Mage.Sets/src/mage/sets/ZendikarRising.java b/Mage.Sets/src/mage/sets/ZendikarRising.java index 4e2fcbf7c5e..6bdb88858b1 100644 --- a/Mage.Sets/src/mage/sets/ZendikarRising.java +++ b/Mage.Sets/src/mage/sets/ZendikarRising.java @@ -1,94 +1,15 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.cards.a.AkoumWarrior; import mage.constants.Rarity; import mage.constants.SetType; -import java.util.Arrays; -import java.util.List; - /** * @author TheElk801 */ public final class ZendikarRising extends ExpansionSet { - private static final List unfinishedLand = Arrays.asList( - "Agadeem, the Undercrypt", - "Akoum Teeth", - "Bala Ged Sanctuary", - "Beyeen Coast", - "Blackbloom Bog", - "Branchloft Pathway", - "Boulderloft Pathway", - "Brightclimb Pathway", - "Grimclimb Pathway", - "Clearwater Pathway", - "Murkwater Pathway", - "Cragcrown Pathway", - "Timbercrown Pathway", - "Emeria, Shattered Skyclave", - "Glasspool Shore", - "Hagra Broodpit", - "Jwari Ruins", - "Kabira Plateau", - "Kazandu Valley", - "Kazuul's Cliffs", - "Khalni Territory", - "Makindi Mesas", - "Malakir Mire", - "Needleverge Pathway", - "Pillarverge Pathway", - "Ondu Skyruins", - "Pelakka Caverns", - "Riverglide Pathway", - "Lavaglide Pathway", - "Sea Gate, Reborn", - "Sejiri Glacier", - "Shatterskull, the Hammer Pass", - "Silundi Isle", - "Skyclave Basilica", - "Song-Mad Ruins", - "Spikefield Cave", - "Tangled Vale", - "Turntimber, Serpentine Wood", - "Umara Skyfalls", - "Valakut Stoneforge", - "Vastwood Thicket", - "Zof Bloodbog" - ); - private static final List unfinishedNonland = Arrays.asList( - "Agadeem's Awakening", - "Akoum Warrior", - "Bala Ged Recovery", - "Beyeen Veil", - "Blackbloom Rogue", - "Emeria's Call", - "Glasspool Mimic", - "Hagra Mauling", - "Jwari Disruption", - "Kabira Takedown", - "Kazandu Mammoth", - "Kazuul's Fury", - "Khalni Ambush", - "Makindi Stampede", - "Malakir Rebirth", - "Ondu Inversion", - "Pelakka Predation", - "Sea Gate Restoration", - "Sejiri Shelter", - "Shatterskull Smashing", - "Silundi Vision", - "Skyclave Cleric", - "Song-Mad Treachery", - "Spikefield Hazard", - "Tangled Florahedron", - "Turntimber Symbiosis", - "Umara Wizard", - "Valakut Awakening", - "Vastwood Fortification", - "Zof Consumption" - ); - private static final ZendikarRising instance = new ZendikarRising(); public static ZendikarRising getInstance() { @@ -110,82 +31,51 @@ public final class ZendikarRising extends ExpansionSet { cards.add(new SetCardInfo("Acquisitions Expert", 89, Rarity.UNCOMMON, mage.cards.a.AcquisitionsExpert.class)); cards.add(new SetCardInfo("Adventure Awaits", 177, Rarity.COMMON, mage.cards.a.AdventureAwaits.class)); - cards.add(new SetCardInfo("Agadeem's Awakening", 336, Rarity.MYTHIC, mage.cards.a.AgadeemsAwakening.class)); cards.add(new SetCardInfo("Agadeem's Awakening", 90, Rarity.MYTHIC, mage.cards.a.AgadeemsAwakening.class)); - cards.add(new SetCardInfo("Agadeem, the Undercrypt", 336, Rarity.MYTHIC, mage.cards.a.AgadeemTheUndercrypt.class)); - cards.add(new SetCardInfo("Agadeem, the Undercrypt", 90, Rarity.MYTHIC, mage.cards.a.AgadeemTheUndercrypt.class)); - cards.add(new SetCardInfo("Akiri, Fearless Voyager", 220, Rarity.RARE, mage.cards.a.AkiriFearlessVoyager.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Akiri, Fearless Voyager", 365, Rarity.RARE, mage.cards.a.AkiriFearlessVoyager.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Akoum Hellhound", 133, Rarity.COMMON, mage.cards.a.AkoumHellhound.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Akoum Hellhound", 299, Rarity.COMMON, mage.cards.a.AkoumHellhound.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Akoum Teeth", 134, Rarity.UNCOMMON, mage.cards.a.AkoumTeeth.class)); - cards.add(new SetCardInfo("Akoum Warrior", 134, Rarity.UNCOMMON, mage.cards.a.AkoumWarrior.class)); + cards.add(new SetCardInfo("Akiri, Fearless Voyager", 220, Rarity.RARE, mage.cards.a.AkiriFearlessVoyager.class)); + cards.add(new SetCardInfo("Akoum Hellhound", 133, Rarity.COMMON, mage.cards.a.AkoumHellhound.class)); + cards.add(new SetCardInfo("Akoum Warrior", 134, Rarity.UNCOMMON, AkoumWarrior.class)); cards.add(new SetCardInfo("Allied Assault", 1, Rarity.UNCOMMON, mage.cards.a.AlliedAssault.class)); - cards.add(new SetCardInfo("Ancient Greenwarden", 178, Rarity.MYTHIC, mage.cards.a.AncientGreenwarden.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ancient Greenwarden", 357, Rarity.MYTHIC, mage.cards.a.AncientGreenwarden.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Angel of Destiny", 2, Rarity.MYTHIC, mage.cards.a.AngelOfDestiny.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Angel of Destiny", 314, Rarity.MYTHIC, mage.cards.a.AngelOfDestiny.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ancient Greenwarden", 178, Rarity.MYTHIC, mage.cards.a.AncientGreenwarden.class)); + cards.add(new SetCardInfo("Angel of Destiny", 2, Rarity.MYTHIC, mage.cards.a.AngelOfDestiny.class)); cards.add(new SetCardInfo("Angelheart Protector", 3, Rarity.COMMON, mage.cards.a.AngelheartProtector.class)); cards.add(new SetCardInfo("Anticognition", 45, Rarity.COMMON, mage.cards.a.Anticognition.class)); - cards.add(new SetCardInfo("Archon of Emeria", 315, Rarity.RARE, mage.cards.a.ArchonOfEmeria.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Archon of Emeria", 4, Rarity.RARE, mage.cards.a.ArchonOfEmeria.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Archpriest of Iona", 316, Rarity.RARE, mage.cards.a.ArchpriestOfIona.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Archpriest of Iona", 5, Rarity.RARE, mage.cards.a.ArchpriestOfIona.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Archon of Emeria", 4, Rarity.RARE, mage.cards.a.ArchonOfEmeria.class)); + cards.add(new SetCardInfo("Archpriest of Iona", 5, Rarity.RARE, mage.cards.a.ArchpriestOfIona.class)); cards.add(new SetCardInfo("Ardent Electromancer", 135, Rarity.COMMON, mage.cards.a.ArdentElectromancer.class)); - cards.add(new SetCardInfo("Ashaya, Soul of the Wild", 179, Rarity.MYTHIC, mage.cards.a.AshayaSoulOfTheWild.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ashaya, Soul of the Wild", 358, Rarity.MYTHIC, mage.cards.a.AshayaSoulOfTheWild.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashaya, Soul of the Wild", 179, Rarity.MYTHIC, mage.cards.a.AshayaSoulOfTheWild.class)); cards.add(new SetCardInfo("Attended Healer", 6, Rarity.UNCOMMON, mage.cards.a.AttendedHealer.class)); cards.add(new SetCardInfo("Bala Ged Recovery", 180, Rarity.UNCOMMON, mage.cards.b.BalaGedRecovery.class)); - cards.add(new SetCardInfo("Bala Ged Sanctuary", 180, Rarity.UNCOMMON, mage.cards.b.BalaGedSanctuary.class)); cards.add(new SetCardInfo("Base Camp", 257, Rarity.UNCOMMON, mage.cards.b.BaseCamp.class)); - cards.add(new SetCardInfo("Beyeen Coast", 46, Rarity.UNCOMMON, mage.cards.b.BeyeenCoast.class)); cards.add(new SetCardInfo("Beyeen Veil", 46, Rarity.UNCOMMON, mage.cards.b.BeyeenVeil.class)); - cards.add(new SetCardInfo("Blackbloom Bog", 91, Rarity.UNCOMMON, mage.cards.b.BlackbloomBog.class)); cards.add(new SetCardInfo("Blackbloom Rogue", 91, Rarity.UNCOMMON, mage.cards.b.BlackbloomRogue.class)); cards.add(new SetCardInfo("Blood Beckoning", 92, Rarity.COMMON, mage.cards.b.BloodBeckoning.class)); cards.add(new SetCardInfo("Blood Price", 93, Rarity.COMMON, mage.cards.b.BloodPrice.class)); - cards.add(new SetCardInfo("Bloodchief's Thirst", 388, Rarity.UNCOMMON, mage.cards.b.BloodchiefsThirst.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Bloodchief's Thirst", 94, Rarity.UNCOMMON, mage.cards.b.BloodchiefsThirst.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Boulderloft Pathway", 258, Rarity.RARE, mage.cards.b.BoulderloftPathway.class)); - cards.add(new SetCardInfo("Boulderloft Pathway", 284, Rarity.RARE, mage.cards.b.BoulderloftPathway.class)); + cards.add(new SetCardInfo("Bloodchief's Thirst", 94, Rarity.UNCOMMON, mage.cards.b.BloodchiefsThirst.class)); cards.add(new SetCardInfo("Branchloft Pathway", 258, Rarity.RARE, mage.cards.b.BranchloftPathway.class)); - cards.add(new SetCardInfo("Branchloft Pathway", 284, Rarity.RARE, mage.cards.b.BranchloftPathway.class)); cards.add(new SetCardInfo("Brightclimb Pathway", 259, Rarity.RARE, mage.cards.b.BrightclimbPathway.class)); - cards.add(new SetCardInfo("Brightclimb Pathway", 285, Rarity.RARE, mage.cards.b.BrightclimbPathway.class)); cards.add(new SetCardInfo("Broken Wings", 181, Rarity.COMMON, mage.cards.b.BrokenWings.class)); - cards.add(new SetCardInfo("Brushfire Elemental", 221, Rarity.UNCOMMON, mage.cards.b.BrushfireElemental.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Brushfire Elemental", 311, Rarity.UNCOMMON, mage.cards.b.BrushfireElemental.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Brushfire Elemental", 221, Rarity.UNCOMMON, mage.cards.b.BrushfireElemental.class)); cards.add(new SetCardInfo("Bubble Snare", 47, Rarity.COMMON, mage.cards.b.BubbleSnare.class)); - cards.add(new SetCardInfo("Canopy Baloth", 182, Rarity.COMMON, mage.cards.c.CanopyBaloth.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Canopy Baloth", 304, Rarity.COMMON, mage.cards.c.CanopyBaloth.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Canyon Jerboa", 290, Rarity.UNCOMMON, mage.cards.c.CanyonJerboa.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Canyon Jerboa", 7, Rarity.UNCOMMON, mage.cards.c.CanyonJerboa.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Canopy Baloth", 182, Rarity.COMMON, mage.cards.c.CanopyBaloth.class)); + cards.add(new SetCardInfo("Canyon Jerboa", 7, Rarity.UNCOMMON, mage.cards.c.CanyonJerboa.class)); cards.add(new SetCardInfo("Cascade Seer", 48, Rarity.COMMON, mage.cards.c.CascadeSeer.class)); - cards.add(new SetCardInfo("Charix, the Raging Isle", 325, Rarity.RARE, mage.cards.c.CharixTheRagingIsle.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Charix, the Raging Isle", 386, Rarity.RARE, mage.cards.c.CharixTheRagingIsle.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Charix, the Raging Isle", 49, Rarity.RARE, mage.cards.c.CharixTheRagingIsle.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Charix, the Raging Isle", 49, Rarity.RARE, mage.cards.c.CharixTheRagingIsle.class)); cards.add(new SetCardInfo("Chilling Trap", 50, Rarity.COMMON, mage.cards.c.ChillingTrap.class)); cards.add(new SetCardInfo("Cinderclasm", 136, Rarity.UNCOMMON, mage.cards.c.Cinderclasm.class)); cards.add(new SetCardInfo("Cleansing Wildfire", 137, Rarity.COMMON, mage.cards.c.CleansingWildfire.class)); cards.add(new SetCardInfo("Clearwater Pathway", 260, Rarity.RARE, mage.cards.c.ClearwaterPathway.class)); - cards.add(new SetCardInfo("Clearwater Pathway", 286, Rarity.RARE, mage.cards.c.ClearwaterPathway.class)); cards.add(new SetCardInfo("Cleric of Chill Depths", 51, Rarity.COMMON, mage.cards.c.ClericOfChillDepths.class)); cards.add(new SetCardInfo("Cleric of Life's Bond", 222, Rarity.UNCOMMON, mage.cards.c.ClericOfLifesBond.class)); cards.add(new SetCardInfo("Cliffhaven Kitesail", 243, Rarity.COMMON, mage.cards.c.CliffhavenKitesail.class)); cards.add(new SetCardInfo("Cliffhaven Sell-Sword", 8, Rarity.COMMON, mage.cards.c.CliffhavenSellSword.class)); cards.add(new SetCardInfo("Concerted Defense", 52, Rarity.UNCOMMON, mage.cards.c.ConcertedDefense.class)); - cards.add(new SetCardInfo("Confounding Conundrum", 326, Rarity.RARE, mage.cards.c.ConfoundingConundrum.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Confounding Conundrum", 53, Rarity.RARE, mage.cards.c.ConfoundingConundrum.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Coralhelm Chronicler", 327, Rarity.RARE, mage.cards.c.CoralhelmChronicler.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Coralhelm Chronicler", 54, Rarity.RARE, mage.cards.c.CoralhelmChronicler.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Coveted Prize", 337, Rarity.RARE, mage.cards.c.CovetedPrize.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Coveted Prize", 95, Rarity.RARE, mage.cards.c.CovetedPrize.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Confounding Conundrum", 53, Rarity.RARE, mage.cards.c.ConfoundingConundrum.class)); + cards.add(new SetCardInfo("Coralhelm Chronicler", 54, Rarity.RARE, mage.cards.c.CoralhelmChronicler.class)); + cards.add(new SetCardInfo("Coveted Prize", 95, Rarity.RARE, mage.cards.c.CovetedPrize.class)); cards.add(new SetCardInfo("Cragcrown Pathway", 261, Rarity.RARE, mage.cards.c.CragcrownPathway.class)); - cards.add(new SetCardInfo("Cragcrown Pathway", 287, Rarity.RARE, mage.cards.c.CragcrownPathway.class)); - cards.add(new SetCardInfo("Cragplate Baloth", 183, Rarity.RARE, mage.cards.c.CragplateBaloth.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Cragplate Baloth", 359, Rarity.RARE, mage.cards.c.CragplateBaloth.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Crawling Barrens", 262, Rarity.RARE, mage.cards.c.CrawlingBarrens.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Crawling Barrens", 378, Rarity.RARE, mage.cards.c.CrawlingBarrens.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cragplate Baloth", 183, Rarity.RARE, mage.cards.c.CragplateBaloth.class)); + cards.add(new SetCardInfo("Crawling Barrens", 262, Rarity.RARE, mage.cards.c.CrawlingBarrens.class)); cards.add(new SetCardInfo("Cunning Geysermage", 55, Rarity.COMMON, mage.cards.c.CunningGeysermage.class)); cards.add(new SetCardInfo("Dauntless Survivor", 184, Rarity.COMMON, mage.cards.d.DauntlessSurvivor.class)); cards.add(new SetCardInfo("Dauntless Unity", 9, Rarity.COMMON, mage.cards.d.DauntlessUnity.class)); @@ -194,132 +84,77 @@ public final class ZendikarRising extends ExpansionSet { cards.add(new SetCardInfo("Demon's Disciple", 97, Rarity.UNCOMMON, mage.cards.d.DemonsDisciple.class)); cards.add(new SetCardInfo("Disenchant", 10, Rarity.COMMON, mage.cards.d.Disenchant.class)); cards.add(new SetCardInfo("Drana's Silencer", 99, Rarity.COMMON, mage.cards.d.DranasSilencer.class)); - cards.add(new SetCardInfo("Drana, the Last Bloodchief", 338, Rarity.MYTHIC, mage.cards.d.DranaTheLastBloodchief.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Drana, the Last Bloodchief", 98, Rarity.MYTHIC, mage.cards.d.DranaTheLastBloodchief.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Dreadwurm", 100, Rarity.COMMON, mage.cards.d.Dreadwurm.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Dreadwurm", 297, Rarity.COMMON, mage.cards.d.Dreadwurm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Drana, the Last Bloodchief", 98, Rarity.MYTHIC, mage.cards.d.DranaTheLastBloodchief.class)); + cards.add(new SetCardInfo("Dreadwurm", 100, Rarity.COMMON, mage.cards.d.Dreadwurm.class)); cards.add(new SetCardInfo("Emeria Captain", 11, Rarity.UNCOMMON, mage.cards.e.EmeriaCaptain.class)); cards.add(new SetCardInfo("Emeria's Call", 12, Rarity.MYTHIC, mage.cards.e.EmeriasCall.class)); - cards.add(new SetCardInfo("Emeria's Call", 317, Rarity.MYTHIC, mage.cards.e.EmeriasCall.class)); - cards.add(new SetCardInfo("Emeria, Shattered Skyclave", 12, Rarity.MYTHIC, mage.cards.e.EmeriaShatteredSkyclave.class)); - cards.add(new SetCardInfo("Emeria, Shattered Skyclave", 317, Rarity.MYTHIC, mage.cards.e.EmeriaShatteredSkyclave.class)); cards.add(new SetCardInfo("Expedition Champion", 138, Rarity.COMMON, mage.cards.e.ExpeditionChampion.class)); cards.add(new SetCardInfo("Expedition Diviner", 57, Rarity.COMMON, mage.cards.e.ExpeditionDiviner.class)); cards.add(new SetCardInfo("Expedition Healer", 13, Rarity.COMMON, mage.cards.e.ExpeditionHealer.class)); cards.add(new SetCardInfo("Expedition Skulker", 101, Rarity.COMMON, mage.cards.e.ExpeditionSkulker.class)); cards.add(new SetCardInfo("Farsight Adept", 14, Rarity.COMMON, mage.cards.f.FarsightAdept.class)); - cards.add(new SetCardInfo("Fearless Fledgling", 15, Rarity.UNCOMMON, mage.cards.f.FearlessFledgling.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fearless Fledgling", 291, Rarity.UNCOMMON, mage.cards.f.FearlessFledgling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fearless Fledgling", 15, Rarity.UNCOMMON, mage.cards.f.FearlessFledgling.class)); cards.add(new SetCardInfo("Feed the Swarm", 102, Rarity.COMMON, mage.cards.f.FeedTheSwarm.class)); - cards.add(new SetCardInfo("Felidar Retreat", 16, Rarity.RARE, mage.cards.f.FelidarRetreat.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Felidar Retreat", 292, Rarity.RARE, mage.cards.f.FelidarRetreat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Felidar Retreat", 16, Rarity.RARE, mage.cards.f.FelidarRetreat.class)); cards.add(new SetCardInfo("Field Research", 58, Rarity.COMMON, mage.cards.f.FieldResearch.class)); cards.add(new SetCardInfo("Fireblade Charger", 139, Rarity.UNCOMMON, mage.cards.f.FirebladeCharger.class)); cards.add(new SetCardInfo("Fissure Wizard", 140, Rarity.COMMON, mage.cards.f.FissureWizard.class)); cards.add(new SetCardInfo("Forest", 278, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Forest", 279, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Forest", 280, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Forest", 384, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Forsaken Monument", 244, Rarity.MYTHIC, mage.cards.f.ForsakenMonument.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forsaken Monument", 374, Rarity.MYTHIC, mage.cards.f.ForsakenMonument.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forsaken Monument", 244, Rarity.MYTHIC, mage.cards.f.ForsakenMonument.class)); cards.add(new SetCardInfo("Ghastly Gloomhunter", 103, Rarity.COMMON, mage.cards.g.GhastlyGloomhunter.class)); cards.add(new SetCardInfo("Glacial Grasp", 59, Rarity.COMMON, mage.cards.g.GlacialGrasp.class)); - cards.add(new SetCardInfo("Glasspool Mimic", 328, Rarity.RARE, mage.cards.g.GlasspoolMimic.class)); cards.add(new SetCardInfo("Glasspool Mimic", 60, Rarity.RARE, mage.cards.g.GlasspoolMimic.class)); - cards.add(new SetCardInfo("Glasspool Shore", 328, Rarity.RARE, mage.cards.g.GlasspoolShore.class)); - cards.add(new SetCardInfo("Glasspool Shore", 60, Rarity.RARE, mage.cards.g.GlasspoolShore.class)); cards.add(new SetCardInfo("Gnarlid Colony", 185, Rarity.COMMON, mage.cards.g.GnarlidColony.class)); cards.add(new SetCardInfo("Goma Fada Vanguard", 141, Rarity.UNCOMMON, mage.cards.g.GomaFadaVanguard.class)); - cards.add(new SetCardInfo("Grakmaw, Skyclave Ravager", 223, Rarity.RARE, mage.cards.g.GrakmawSkyclaveRavager.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Grakmaw, Skyclave Ravager", 366, Rarity.RARE, mage.cards.g.GrakmawSkyclaveRavager.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Grimclimb Pathway", 259, Rarity.RARE, mage.cards.g.GrimclimbPathway.class)); - cards.add(new SetCardInfo("Grimclimb Pathway", 285, Rarity.RARE, mage.cards.g.GrimclimbPathway.class)); + cards.add(new SetCardInfo("Grakmaw, Skyclave Ravager", 223, Rarity.RARE, mage.cards.g.GrakmawSkyclaveRavager.class)); cards.add(new SetCardInfo("Grotag Bug-Catcher", 142, Rarity.COMMON, mage.cards.g.GrotagBugCatcher.class)); cards.add(new SetCardInfo("Grotag Night-Runner", 143, Rarity.UNCOMMON, mage.cards.g.GrotagNightRunner.class)); cards.add(new SetCardInfo("Guul Draz Mucklord", 104, Rarity.COMMON, mage.cards.g.GuulDrazMucklord.class)); - cards.add(new SetCardInfo("Hagra Broodpit", 106, Rarity.RARE, mage.cards.h.HagraBroodpit.class)); - cards.add(new SetCardInfo("Hagra Broodpit", 339, Rarity.RARE, mage.cards.h.HagraBroodpit.class)); cards.add(new SetCardInfo("Hagra Constrictor", 105, Rarity.COMMON, mage.cards.h.HagraConstrictor.class)); cards.add(new SetCardInfo("Hagra Mauling", 106, Rarity.RARE, mage.cards.h.HagraMauling.class)); - cards.add(new SetCardInfo("Hagra Mauling", 339, Rarity.RARE, mage.cards.h.HagraMauling.class)); cards.add(new SetCardInfo("Highborn Vampire", 107, Rarity.COMMON, mage.cards.h.HighbornVampire.class)); cards.add(new SetCardInfo("Inordinate Rage", 144, Rarity.COMMON, mage.cards.i.InordinateRage.class)); - cards.add(new SetCardInfo("Inscription of Abundance", 186, Rarity.RARE, mage.cards.i.InscriptionOfAbundance.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Inscription of Abundance", 360, Rarity.RARE, mage.cards.i.InscriptionOfAbundance.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Inscription of Insight", 329, Rarity.RARE, mage.cards.i.InscriptionOfInsight.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Inscription of Insight", 61, Rarity.RARE, mage.cards.i.InscriptionOfInsight.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Inscription of Ruin", 108, Rarity.RARE, mage.cards.i.InscriptionOfRuin.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Inscription of Ruin", 340, Rarity.RARE, mage.cards.i.InscriptionOfRuin.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Into the Roil", 387, Rarity.COMMON, mage.cards.i.IntoTheRoil.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Into the Roil", 62, Rarity.COMMON, mage.cards.i.IntoTheRoil.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inscription of Abundance", 186, Rarity.RARE, mage.cards.i.InscriptionOfAbundance.class)); + cards.add(new SetCardInfo("Inscription of Insight", 61, Rarity.RARE, mage.cards.i.InscriptionOfInsight.class)); + cards.add(new SetCardInfo("Inscription of Ruin", 108, Rarity.RARE, mage.cards.i.InscriptionOfRuin.class)); + cards.add(new SetCardInfo("Into the Roil", 62, Rarity.COMMON, mage.cards.i.IntoTheRoil.class)); cards.add(new SetCardInfo("Iridescent Hornbeetle", 187, Rarity.UNCOMMON, mage.cards.i.IridescentHornbeetle.class)); cards.add(new SetCardInfo("Island", 269, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_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, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Jace, Mirror Mage", 281, Rarity.MYTHIC, mage.cards.j.JaceMirrorMage.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Jace, Mirror Mage", 63, Rarity.MYTHIC, mage.cards.j.JaceMirrorMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jace, Mirror Mage", 63, Rarity.MYTHIC, mage.cards.j.JaceMirrorMage.class)); cards.add(new SetCardInfo("Joraga Visionary", 188, Rarity.COMMON, mage.cards.j.JoragaVisionary.class)); cards.add(new SetCardInfo("Journey to Oblivion", 17, Rarity.UNCOMMON, mage.cards.j.JourneyToOblivion.class)); cards.add(new SetCardInfo("Jwari Disruption", 64, Rarity.UNCOMMON, mage.cards.j.JwariDisruption.class)); - cards.add(new SetCardInfo("Jwari Ruins", 64, Rarity.UNCOMMON, mage.cards.j.JwariRuins.class)); cards.add(new SetCardInfo("Kabira Outrider", 18, Rarity.COMMON, mage.cards.k.KabiraOutrider.class)); - cards.add(new SetCardInfo("Kabira Plateau", 19, Rarity.UNCOMMON, mage.cards.k.KabiraPlateau.class)); cards.add(new SetCardInfo("Kabira Takedown", 19, Rarity.UNCOMMON, mage.cards.k.KabiraTakedown.class)); - cards.add(new SetCardInfo("Kargan Intimidator", 145, Rarity.RARE, mage.cards.k.KarganIntimidator.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kargan Intimidator", 347, Rarity.RARE, mage.cards.k.KarganIntimidator.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kargan Warleader", 224, Rarity.UNCOMMON, mage.cards.k.KarganWarleader.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kargan Warleader", 391, Rarity.UNCOMMON, mage.cards.k.KarganWarleader.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kaza, Roil Chaser", 225, Rarity.RARE, mage.cards.k.KazaRoilChaser.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kaza, Roil Chaser", 367, Rarity.RARE, mage.cards.k.KazaRoilChaser.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kargan Intimidator", 145, Rarity.RARE, mage.cards.k.KarganIntimidator.class)); + cards.add(new SetCardInfo("Kargan Warleader", 224, Rarity.UNCOMMON, mage.cards.k.KarganWarleader.class)); + cards.add(new SetCardInfo("Kaza, Roil Chaser", 225, Rarity.RARE, mage.cards.k.KazaRoilChaser.class)); cards.add(new SetCardInfo("Kazandu Mammoth", 189, Rarity.RARE, mage.cards.k.KazanduMammoth.class)); - cards.add(new SetCardInfo("Kazandu Mammoth", 305, Rarity.RARE, mage.cards.k.KazanduMammoth.class)); - cards.add(new SetCardInfo("Kazandu Nectarpot", 190, Rarity.COMMON, mage.cards.k.KazanduNectarpot.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kazandu Nectarpot", 306, Rarity.COMMON, mage.cards.k.KazanduNectarpot.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kazandu Nectarpot", 190, Rarity.COMMON, mage.cards.k.KazanduNectarpot.class)); cards.add(new SetCardInfo("Kazandu Stomper", 191, Rarity.COMMON, mage.cards.k.KazanduStomper.class)); - cards.add(new SetCardInfo("Kazandu Valley", 189, Rarity.RARE, mage.cards.k.KazanduValley.class)); - cards.add(new SetCardInfo("Kazandu Valley", 305, Rarity.RARE, mage.cards.k.KazanduValley.class)); - cards.add(new SetCardInfo("Kazuul's Cliffs", 146, Rarity.UNCOMMON, mage.cards.k.KazuulsCliffs.class)); cards.add(new SetCardInfo("Kazuul's Fury", 146, Rarity.UNCOMMON, mage.cards.k.KazuulsFury.class)); cards.add(new SetCardInfo("Khalni Ambush", 192, Rarity.UNCOMMON, mage.cards.k.KhalniAmbush.class)); - cards.add(new SetCardInfo("Khalni Territory", 192, Rarity.UNCOMMON, mage.cards.k.KhalniTerritory.class)); cards.add(new SetCardInfo("Kitesail Cleric", 20, Rarity.UNCOMMON, mage.cards.k.KitesailCleric.class)); cards.add(new SetCardInfo("Kor Blademaster", 21, Rarity.UNCOMMON, mage.cards.k.KorBlademaster.class)); cards.add(new SetCardInfo("Kor Celebrant", 22, Rarity.COMMON, mage.cards.k.KorCelebrant.class)); - cards.add(new SetCardInfo("Lavaglide Pathway", 264, Rarity.RARE, mage.cards.l.LavaglidePathway.class)); - cards.add(new SetCardInfo("Lavaglide Pathway", 289, Rarity.RARE, mage.cards.l.LavaglidePathway.class)); - cards.add(new SetCardInfo("Legion Angel", 23, Rarity.RARE, mage.cards.l.LegionAngel.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Legion Angel", 318, Rarity.RARE, mage.cards.l.LegionAngel.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Leyline Tyrant", 147, Rarity.MYTHIC, mage.cards.l.LeylineTyrant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Leyline Tyrant", 348, Rarity.MYTHIC, mage.cards.l.LeylineTyrant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Linvala, Shield of Sea Gate", 226, Rarity.RARE, mage.cards.l.LinvalaShieldOfSeaGate.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Linvala, Shield of Sea Gate", 368, Rarity.RARE, mage.cards.l.LinvalaShieldOfSeaGate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Legion Angel", 23, Rarity.RARE, mage.cards.l.LegionAngel.class)); + cards.add(new SetCardInfo("Leyline Tyrant", 147, Rarity.MYTHIC, mage.cards.l.LeylineTyrant.class)); + cards.add(new SetCardInfo("Linvala, Shield of Sea Gate", 226, Rarity.RARE, mage.cards.l.LinvalaShieldOfSeaGate.class)); cards.add(new SetCardInfo("Lithoform Blight", 109, Rarity.UNCOMMON, mage.cards.l.LithoformBlight.class)); - cards.add(new SetCardInfo("Lithoform Engine", 245, Rarity.MYTHIC, mage.cards.l.LithoformEngine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Lithoform Engine", 375, Rarity.MYTHIC, mage.cards.l.LithoformEngine.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Living Tempest", 65, Rarity.COMMON, mage.cards.l.LivingTempest.class)); - cards.add(new SetCardInfo("Lotus Cobra", 193, Rarity.RARE, mage.cards.l.LotusCobra.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Lotus Cobra", 307, Rarity.RARE, mage.cards.l.LotusCobra.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lotus Cobra", 193, Rarity.RARE, mage.cards.l.LotusCobra.class)); cards.add(new SetCardInfo("Lullmage's Domination", 66, Rarity.UNCOMMON, mage.cards.l.LullmagesDomination.class)); cards.add(new SetCardInfo("Lullmage's Familiar", 227, Rarity.UNCOMMON, mage.cards.l.LullmagesFamiliar.class)); - cards.add(new SetCardInfo("Luminarch Aspirant", 24, Rarity.RARE, mage.cards.l.LuminarchAspirant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Luminarch Aspirant", 319, Rarity.RARE, mage.cards.l.LuminarchAspirant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Maddening Cacophony", 330, Rarity.RARE, mage.cards.m.MaddeningCacophony.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Maddening Cacophony", 67, Rarity.RARE, mage.cards.m.MaddeningCacophony.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Magmatic Channeler", 148, Rarity.RARE, mage.cards.m.MagmaticChanneler.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Magmatic Channeler", 349, Rarity.RARE, mage.cards.m.MagmaticChanneler.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Makindi Mesas", 26, Rarity.UNCOMMON, mage.cards.m.MakindiMesas.class)); - cards.add(new SetCardInfo("Makindi Ox", 25, Rarity.COMMON, mage.cards.m.MakindiOx.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Makindi Ox", 293, Rarity.COMMON, mage.cards.m.MakindiOx.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Luminarch Aspirant", 24, Rarity.RARE, mage.cards.l.LuminarchAspirant.class)); + cards.add(new SetCardInfo("Maddening Cacophony", 67, Rarity.RARE, mage.cards.m.MaddeningCacophony.class)); + cards.add(new SetCardInfo("Magmatic Channeler", 148, Rarity.RARE, mage.cards.m.MagmaticChanneler.class)); + cards.add(new SetCardInfo("Makindi Ox", 25, Rarity.COMMON, mage.cards.m.MakindiOx.class)); cards.add(new SetCardInfo("Makindi Stampede", 26, Rarity.UNCOMMON, mage.cards.m.MakindiStampede.class)); cards.add(new SetCardInfo("Malakir Blood-Priest", 110, Rarity.COMMON, mage.cards.m.MalakirBloodPriest.class)); - cards.add(new SetCardInfo("Malakir Mire", 111, Rarity.UNCOMMON, mage.cards.m.MalakirMire.class)); cards.add(new SetCardInfo("Malakir Rebirth", 111, Rarity.UNCOMMON, mage.cards.m.MalakirRebirth.class)); cards.add(new SetCardInfo("Marauding Blight-Priest", 112, Rarity.COMMON, mage.cards.m.MaraudingBlightPriest.class)); - cards.add(new SetCardInfo("Master of Winds", 331, Rarity.RARE, mage.cards.m.MasterOfWinds.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Master of Winds", 68, Rarity.RARE, mage.cards.m.MasterOfWinds.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Maul of the Skyclaves", 27, Rarity.RARE, mage.cards.m.MaulOfTheSkyclaves.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Maul of the Skyclaves", 320, Rarity.RARE, mage.cards.m.MaulOfTheSkyclaves.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Master of Winds", 68, Rarity.RARE, mage.cards.m.MasterOfWinds.class)); + cards.add(new SetCardInfo("Maul of the Skyclaves", 27, Rarity.RARE, mage.cards.m.MaulOfTheSkyclaves.class)); cards.add(new SetCardInfo("Merfolk Falconer", 69, Rarity.UNCOMMON, mage.cards.m.MerfolkFalconer.class)); cards.add(new SetCardInfo("Merfolk Windrobber", 70, Rarity.UNCOMMON, mage.cards.m.MerfolkWindrobber.class)); cards.add(new SetCardInfo("Mesa Lynx", 28, Rarity.COMMON, mage.cards.m.MesaLynx.class)); @@ -327,66 +162,37 @@ public final class ZendikarRising extends ExpansionSet { cards.add(new SetCardInfo("Mind Carver", 113, Rarity.UNCOMMON, mage.cards.m.MindCarver.class)); cards.add(new SetCardInfo("Mind Drain", 114, Rarity.COMMON, mage.cards.m.MindDrain.class)); cards.add(new SetCardInfo("Molten Blast", 149, Rarity.COMMON, mage.cards.m.MoltenBlast.class)); - cards.add(new SetCardInfo("Moraug, Fury of Akoum", 150, Rarity.MYTHIC, mage.cards.m.MoraugFuryOfAkoum.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Moraug, Fury of Akoum", 300, Rarity.MYTHIC, mage.cards.m.MoraugFuryOfAkoum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Moraug, Fury of Akoum", 150, Rarity.MYTHIC, mage.cards.m.MoraugFuryOfAkoum.class)); cards.add(new SetCardInfo("Moss-Pit Skeleton", 228, Rarity.UNCOMMON, mage.cards.m.MossPitSkeleton.class)); cards.add(new SetCardInfo("Mountain", 275, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 276, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 277, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 383, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Murasa Brute", 195, Rarity.COMMON, mage.cards.m.MurasaBrute.class)); cards.add(new SetCardInfo("Murasa Rootgrazer", 229, Rarity.UNCOMMON, mage.cards.m.MurasaRootgrazer.class)); cards.add(new SetCardInfo("Murasa Sproutling", 196, Rarity.UNCOMMON, mage.cards.m.MurasaSproutling.class)); - cards.add(new SetCardInfo("Murkwater Pathway", 260, Rarity.RARE, mage.cards.m.MurkwaterPathway.class)); - cards.add(new SetCardInfo("Murkwater Pathway", 286, Rarity.RARE, mage.cards.m.MurkwaterPathway.class)); - cards.add(new SetCardInfo("Myriad Construct", 246, Rarity.RARE, mage.cards.m.MyriadConstruct.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Myriad Construct", 376, Rarity.RARE, mage.cards.m.MyriadConstruct.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myriad Construct", 246, Rarity.RARE, mage.cards.m.MyriadConstruct.class)); cards.add(new SetCardInfo("Nahiri's Binding", 29, Rarity.COMMON, mage.cards.n.NahirisBinding.class)); - cards.add(new SetCardInfo("Nahiri's Lithoforming", 151, Rarity.RARE, mage.cards.n.NahirisLithoforming.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Nahiri's Lithoforming", 350, Rarity.RARE, mage.cards.n.NahirisLithoforming.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Nahiri, Heir of the Ancients", 230, Rarity.MYTHIC, mage.cards.n.NahiriHeirOfTheAncients.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Nahiri, Heir of the Ancients", 282, Rarity.MYTHIC, mage.cards.n.NahiriHeirOfTheAncients.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nahiri's Lithoforming", 151, Rarity.RARE, mage.cards.n.NahirisLithoforming.class)); + cards.add(new SetCardInfo("Nahiri, Heir of the Ancients", 230, Rarity.MYTHIC, mage.cards.n.NahiriHeirOfTheAncients.class)); cards.add(new SetCardInfo("Needleverge Pathway", 263, Rarity.RARE, mage.cards.n.NeedlevergePathway.class)); - cards.add(new SetCardInfo("Needleverge Pathway", 288, Rarity.RARE, mage.cards.n.NeedlevergePathway.class)); cards.add(new SetCardInfo("Negate", 71, Rarity.COMMON, mage.cards.n.Negate.class)); - cards.add(new SetCardInfo("Nighthawk Scavenger", 115, Rarity.RARE, mage.cards.n.NighthawkScavenger.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Nighthawk Scavenger", 341, Rarity.RARE, mage.cards.n.NighthawkScavenger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nighthawk Scavenger", 115, Rarity.RARE, mage.cards.n.NighthawkScavenger.class)); cards.add(new SetCardInfo("Nimana Skitter-Sneak", 116, Rarity.COMMON, mage.cards.n.NimanaSkitterSneak.class)); cards.add(new SetCardInfo("Nimana Skydancer", 117, Rarity.COMMON, mage.cards.n.NimanaSkydancer.class)); - cards.add(new SetCardInfo("Nimble Trapfinder", 332, Rarity.RARE, mage.cards.n.NimbleTrapfinder.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Nimble Trapfinder", 72, Rarity.RARE, mage.cards.n.NimbleTrapfinder.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Nissa of Shadowed Boughs", 231, Rarity.MYTHIC, mage.cards.n.NissaOfShadowedBoughs.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Nissa of Shadowed Boughs", 283, Rarity.MYTHIC, mage.cards.n.NissaOfShadowedBoughs.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nimble Trapfinder", 72, Rarity.RARE, mage.cards.n.NimbleTrapfinder.class)); + cards.add(new SetCardInfo("Nissa of Shadowed Boughs", 231, Rarity.MYTHIC, mage.cards.n.NissaOfShadowedBoughs.class)); cards.add(new SetCardInfo("Nissa's Zendikon", 197, Rarity.COMMON, mage.cards.n.NissasZendikon.class)); - cards.add(new SetCardInfo("Nullpriest of Oblivion", 118, Rarity.RARE, mage.cards.n.NullpriestOfOblivion.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Nullpriest of Oblivion", 342, Rarity.RARE, mage.cards.n.NullpriestOfOblivion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nullpriest of Oblivion", 118, Rarity.RARE, mage.cards.n.NullpriestOfOblivion.class)); cards.add(new SetCardInfo("Oblivion's Hunger", 119, Rarity.COMMON, mage.cards.o.OblivionsHunger.class)); - cards.add(new SetCardInfo("Omnath, Locus of Creation", 232, Rarity.MYTHIC, mage.cards.o.OmnathLocusOfCreation.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Omnath, Locus of Creation", 312, Rarity.MYTHIC, mage.cards.o.OmnathLocusOfCreation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Omnath, Locus of Creation", 232, Rarity.MYTHIC, mage.cards.o.OmnathLocusOfCreation.class)); cards.add(new SetCardInfo("Ondu Inversion", 30, Rarity.RARE, mage.cards.o.OnduInversion.class)); - cards.add(new SetCardInfo("Ondu Inversion", 321, Rarity.RARE, mage.cards.o.OnduInversion.class)); - cards.add(new SetCardInfo("Ondu Skyruins", 30, Rarity.RARE, mage.cards.o.OnduSkyruins.class)); - cards.add(new SetCardInfo("Ondu Skyruins", 321, Rarity.RARE, mage.cards.o.OnduSkyruins.class)); - cards.add(new SetCardInfo("Orah, Skyclave Hierophant", 233, Rarity.RARE, mage.cards.o.OrahSkyclaveHierophant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Orah, Skyclave Hierophant", 369, Rarity.RARE, mage.cards.o.OrahSkyclaveHierophant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Orah, Skyclave Hierophant", 385, Rarity.RARE, mage.cards.o.OrahSkyclaveHierophant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Oran-Rief Ooze", 198, Rarity.RARE, mage.cards.o.OranRiefOoze.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Oran-Rief Ooze", 361, Rarity.RARE, mage.cards.o.OranRiefOoze.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Orah, Skyclave Hierophant", 233, Rarity.RARE, mage.cards.o.OrahSkyclaveHierophant.class)); + cards.add(new SetCardInfo("Oran-Rief Ooze", 198, Rarity.RARE, mage.cards.o.OranRiefOoze.class)); cards.add(new SetCardInfo("Paired Tactician", 31, Rarity.UNCOMMON, mage.cards.p.PairedTactician.class)); - cards.add(new SetCardInfo("Pelakka Caverns", 120, Rarity.UNCOMMON, mage.cards.p.PelakkaCaverns.class)); cards.add(new SetCardInfo("Pelakka Predation", 120, Rarity.UNCOMMON, mage.cards.p.PelakkaPredation.class)); - cards.add(new SetCardInfo("Phylath, World Sculptor", 234, Rarity.RARE, mage.cards.p.PhylathWorldSculptor.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phylath, World Sculptor", 313, Rarity.RARE, mage.cards.p.PhylathWorldSculptor.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Pillarverge Pathway", 263, Rarity.RARE, mage.cards.p.PillarvergePathway.class)); - cards.add(new SetCardInfo("Pillarverge Pathway", 288, Rarity.RARE, mage.cards.p.PillarvergePathway.class)); + cards.add(new SetCardInfo("Phylath, World Sculptor", 234, Rarity.RARE, mage.cards.p.PhylathWorldSculptor.class)); cards.add(new SetCardInfo("Plains", 266, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Plains", 267, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Plains", 268, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Plains", 380, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Practiced Tactics", 32, Rarity.COMMON, mage.cards.p.PracticedTactics.class)); cards.add(new SetCardInfo("Pressure Point", 33, Rarity.COMMON, mage.cards.p.PressurePoint.class)); - cards.add(new SetCardInfo("Prowling Felidar", 294, Rarity.COMMON, mage.cards.p.ProwlingFelidar.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Prowling Felidar", 34, Rarity.COMMON, mage.cards.p.ProwlingFelidar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prowling Felidar", 34, Rarity.COMMON, mage.cards.p.ProwlingFelidar.class)); cards.add(new SetCardInfo("Pyroclastic Hellion", 152, Rarity.COMMON, mage.cards.p.PyroclasticHellion.class)); cards.add(new SetCardInfo("Rabid Bite", 199, Rarity.COMMON, mage.cards.r.RabidBite.class)); cards.add(new SetCardInfo("Ravager's Mace", 235, Rarity.UNCOMMON, mage.cards.r.RavagersMace.class)); @@ -394,173 +200,102 @@ public final class ZendikarRising extends ExpansionSet { cards.add(new SetCardInfo("Relic Amulet", 247, Rarity.UNCOMMON, mage.cards.r.RelicAmulet.class)); cards.add(new SetCardInfo("Relic Axe", 248, Rarity.UNCOMMON, mage.cards.r.RelicAxe.class)); cards.add(new SetCardInfo("Relic Golem", 249, Rarity.UNCOMMON, mage.cards.r.RelicGolem.class)); - cards.add(new SetCardInfo("Relic Robber", 153, Rarity.RARE, mage.cards.r.RelicRobber.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Relic Robber", 351, Rarity.RARE, mage.cards.r.RelicRobber.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Relic Robber", 153, Rarity.RARE, mage.cards.r.RelicRobber.class)); cards.add(new SetCardInfo("Relic Vial", 250, Rarity.UNCOMMON, mage.cards.r.RelicVial.class)); cards.add(new SetCardInfo("Resolute Strike", 35, Rarity.COMMON, mage.cards.r.ResoluteStrike.class)); cards.add(new SetCardInfo("Risen Riptide", 73, Rarity.COMMON, mage.cards.r.RisenRiptide.class)); cards.add(new SetCardInfo("Riverglide Pathway", 264, Rarity.RARE, mage.cards.r.RiverglidePathway.class)); - cards.add(new SetCardInfo("Riverglide Pathway", 289, Rarity.RARE, mage.cards.r.RiverglidePathway.class)); cards.add(new SetCardInfo("Rockslide Sorcerer", 154, Rarity.UNCOMMON, mage.cards.r.RockslideSorcerer.class)); - cards.add(new SetCardInfo("Roil Eruption", 155, Rarity.COMMON, mage.cards.r.RoilEruption.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Roil Eruption", 389, Rarity.COMMON, mage.cards.r.RoilEruption.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Roiling Regrowth", 201, Rarity.UNCOMMON, mage.cards.r.RoilingRegrowth.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Roiling Regrowth", 390, Rarity.UNCOMMON, mage.cards.r.RoilingRegrowth.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Roiling Vortex", 156, Rarity.RARE, mage.cards.r.RoilingVortex.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Roiling Vortex", 352, Rarity.RARE, mage.cards.r.RoilingVortex.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Roil Eruption", 155, Rarity.COMMON, mage.cards.r.RoilEruption.class)); + cards.add(new SetCardInfo("Roiling Regrowth", 201, Rarity.UNCOMMON, mage.cards.r.RoilingRegrowth.class)); + cards.add(new SetCardInfo("Roiling Vortex", 156, Rarity.RARE, mage.cards.r.RoilingVortex.class)); cards.add(new SetCardInfo("Roost of Drakes", 74, Rarity.UNCOMMON, mage.cards.r.RoostOfDrakes.class)); - cards.add(new SetCardInfo("Ruin Crab", 295, Rarity.UNCOMMON, mage.cards.r.RuinCrab.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ruin Crab", 75, Rarity.UNCOMMON, mage.cards.r.RuinCrab.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ruin Crab", 75, Rarity.UNCOMMON, mage.cards.r.RuinCrab.class)); cards.add(new SetCardInfo("Scale the Heights", 202, Rarity.COMMON, mage.cards.s.ScaleTheHeights.class)); cards.add(new SetCardInfo("Scavenged Blade", 157, Rarity.COMMON, mage.cards.s.ScavengedBlade.class)); cards.add(new SetCardInfo("Scion of the Swarm", 121, Rarity.UNCOMMON, mage.cards.s.ScionOfTheSwarm.class)); cards.add(new SetCardInfo("Scorch Rider", 158, Rarity.COMMON, mage.cards.s.ScorchRider.class)); - cards.add(new SetCardInfo("Scourge of the Skyclaves", 122, Rarity.MYTHIC, mage.cards.s.ScourgeOfTheSkyclaves.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Scourge of the Skyclaves", 343, Rarity.MYTHIC, mage.cards.s.ScourgeOfTheSkyclaves.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Scute Swarm", 203, Rarity.RARE, mage.cards.s.ScuteSwarm.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Scute Swarm", 308, Rarity.RARE, mage.cards.s.ScuteSwarm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scourge of the Skyclaves", 122, Rarity.MYTHIC, mage.cards.s.ScourgeOfTheSkyclaves.class)); + cards.add(new SetCardInfo("Scute Swarm", 203, Rarity.RARE, mage.cards.s.ScuteSwarm.class)); cards.add(new SetCardInfo("Sea Gate Banneret", 36, Rarity.COMMON, mage.cards.s.SeaGateBanneret.class)); cards.add(new SetCardInfo("Sea Gate Colossus", 251, Rarity.COMMON, mage.cards.s.SeaGateColossus.class)); - cards.add(new SetCardInfo("Sea Gate Restoration", 333, Rarity.MYTHIC, mage.cards.s.SeaGateRestoration.class)); cards.add(new SetCardInfo("Sea Gate Restoration", 76, Rarity.MYTHIC, mage.cards.s.SeaGateRestoration.class)); - cards.add(new SetCardInfo("Sea Gate Stormcaller", 334, Rarity.MYTHIC, mage.cards.s.SeaGateStormcaller.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sea Gate Stormcaller", 77, Rarity.MYTHIC, mage.cards.s.SeaGateStormcaller.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sea Gate, Reborn", 333, Rarity.MYTHIC, mage.cards.s.SeaGateReborn.class)); - cards.add(new SetCardInfo("Sea Gate, Reborn", 76, Rarity.MYTHIC, mage.cards.s.SeaGateReborn.class)); + cards.add(new SetCardInfo("Sea Gate Stormcaller", 77, Rarity.MYTHIC, mage.cards.s.SeaGateStormcaller.class)); cards.add(new SetCardInfo("Seafloor Stalker", 78, Rarity.COMMON, mage.cards.s.SeafloorStalker.class)); - cards.add(new SetCardInfo("Sejiri Glacier", 37, Rarity.UNCOMMON, mage.cards.s.SejiriGlacier.class)); cards.add(new SetCardInfo("Sejiri Shelter", 37, Rarity.UNCOMMON, mage.cards.s.SejiriShelter.class)); cards.add(new SetCardInfo("Shadow Stinger", 123, Rarity.UNCOMMON, mage.cards.s.ShadowStinger.class)); - cards.add(new SetCardInfo("Shadows' Verdict", 124, Rarity.RARE, mage.cards.s.ShadowsVerdict.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shadows' Verdict", 344, Rarity.RARE, mage.cards.s.ShadowsVerdict.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shatterskull Charger", 159, Rarity.RARE, mage.cards.s.ShatterskullCharger.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shatterskull Charger", 353, Rarity.RARE, mage.cards.s.ShatterskullCharger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadows' Verdict", 124, Rarity.RARE, mage.cards.s.ShadowsVerdict.class)); + cards.add(new SetCardInfo("Shatterskull Charger", 159, Rarity.RARE, mage.cards.s.ShatterskullCharger.class)); cards.add(new SetCardInfo("Shatterskull Minotaur", 160, Rarity.UNCOMMON, mage.cards.s.ShatterskullMinotaur.class)); cards.add(new SetCardInfo("Shatterskull Smashing", 161, Rarity.MYTHIC, mage.cards.s.ShatterskullSmashing.class)); - cards.add(new SetCardInfo("Shatterskull Smashing", 354, Rarity.MYTHIC, mage.cards.s.ShatterskullSmashing.class)); - cards.add(new SetCardInfo("Shatterskull, the Hammer Pass", 161, Rarity.MYTHIC, mage.cards.s.ShatterskullTheHammerPass.class)); - cards.add(new SetCardInfo("Shatterskull, the Hammer Pass", 354, Rarity.MYTHIC, mage.cards.s.ShatterskullTheHammerPass.class)); cards.add(new SetCardInfo("Shell Shield", 79, Rarity.COMMON, mage.cards.s.ShellShield.class)); cards.add(new SetCardInfo("Shepherd of Heroes", 38, Rarity.COMMON, mage.cards.s.ShepherdOfHeroes.class)); - cards.add(new SetCardInfo("Silundi Isle", 80, Rarity.UNCOMMON, mage.cards.s.SilundiIsle.class)); cards.add(new SetCardInfo("Silundi Vision", 80, Rarity.UNCOMMON, mage.cards.s.SilundiVision.class)); cards.add(new SetCardInfo("Sizzling Barrage", 162, Rarity.COMMON, mage.cards.s.SizzlingBarrage.class)); - cards.add(new SetCardInfo("Skyclave Apparition", 322, Rarity.RARE, mage.cards.s.SkyclaveApparition.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Skyclave Apparition", 39, Rarity.RARE, mage.cards.s.SkyclaveApparition.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Skyclave Basilica", 40, Rarity.UNCOMMON, mage.cards.s.SkyclaveBasilica.class)); + cards.add(new SetCardInfo("Skyclave Apparition", 39, Rarity.RARE, mage.cards.s.SkyclaveApparition.class)); cards.add(new SetCardInfo("Skyclave Cleric", 40, Rarity.UNCOMMON, mage.cards.s.SkyclaveCleric.class)); - cards.add(new SetCardInfo("Skyclave Geopede", 163, Rarity.UNCOMMON, mage.cards.s.SkyclaveGeopede.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Skyclave Geopede", 301, Rarity.UNCOMMON, mage.cards.s.SkyclaveGeopede.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Skyclave Pick-Axe", 204, Rarity.UNCOMMON, mage.cards.s.SkyclavePickAxe.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Skyclave Pick-Axe", 309, Rarity.UNCOMMON, mage.cards.s.SkyclavePickAxe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skyclave Geopede", 163, Rarity.UNCOMMON, mage.cards.s.SkyclaveGeopede.class)); + cards.add(new SetCardInfo("Skyclave Pick-Axe", 204, Rarity.UNCOMMON, mage.cards.s.SkyclavePickAxe.class)); cards.add(new SetCardInfo("Skyclave Plunder", 81, Rarity.UNCOMMON, mage.cards.s.SkyclavePlunder.class)); - cards.add(new SetCardInfo("Skyclave Relic", 252, Rarity.RARE, mage.cards.s.SkyclaveRelic.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Skyclave Relic", 377, Rarity.RARE, mage.cards.s.SkyclaveRelic.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skyclave Relic", 252, Rarity.RARE, mage.cards.s.SkyclaveRelic.class)); cards.add(new SetCardInfo("Skyclave Sentinel", 253, Rarity.COMMON, mage.cards.s.SkyclaveSentinel.class)); - cards.add(new SetCardInfo("Skyclave Shade", 125, Rarity.RARE, mage.cards.s.SkyclaveShade.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Skyclave Shade", 298, Rarity.RARE, mage.cards.s.SkyclaveShade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skyclave Shade", 125, Rarity.RARE, mage.cards.s.SkyclaveShade.class)); cards.add(new SetCardInfo("Skyclave Shadowcat", 126, Rarity.UNCOMMON, mage.cards.s.SkyclaveShadowcat.class)); - cards.add(new SetCardInfo("Skyclave Squid", 296, Rarity.COMMON, mage.cards.s.SkyclaveSquid.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Skyclave Squid", 82, Rarity.COMMON, mage.cards.s.SkyclaveSquid.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skyclave Squid", 82, Rarity.COMMON, mage.cards.s.SkyclaveSquid.class)); cards.add(new SetCardInfo("Smite the Monstrous", 42, Rarity.COMMON, mage.cards.s.SmiteTheMonstrous.class)); cards.add(new SetCardInfo("Sneaking Guide", 164, Rarity.COMMON, mage.cards.s.SneakingGuide.class)); cards.add(new SetCardInfo("Soaring Thought-Thief", 236, Rarity.UNCOMMON, mage.cards.s.SoaringThoughtThief.class)); - cards.add(new SetCardInfo("Song-Mad Ruins", 165, Rarity.UNCOMMON, mage.cards.s.SongMadRuins.class)); cards.add(new SetCardInfo("Song-Mad Treachery", 165, Rarity.UNCOMMON, mage.cards.s.SongMadTreachery.class)); - cards.add(new SetCardInfo("Soul Shatter", 127, Rarity.RARE, mage.cards.s.SoulShatter.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soul Shatter", 345, Rarity.RARE, mage.cards.s.SoulShatter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soul Shatter", 127, Rarity.RARE, mage.cards.s.SoulShatter.class)); cards.add(new SetCardInfo("Spare Supplies", 254, Rarity.COMMON, mage.cards.s.SpareSupplies.class)); - cards.add(new SetCardInfo("Spikefield Cave", 166, Rarity.UNCOMMON, mage.cards.s.SpikefieldCave.class)); cards.add(new SetCardInfo("Spikefield Hazard", 166, Rarity.UNCOMMON, mage.cards.s.SpikefieldHazard.class)); - cards.add(new SetCardInfo("Spitfire Lagac", 167, Rarity.COMMON, mage.cards.s.SpitfireLagac.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Spitfire Lagac", 302, Rarity.COMMON, mage.cards.s.SpitfireLagac.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spitfire Lagac", 167, Rarity.COMMON, mage.cards.s.SpitfireLagac.class)); cards.add(new SetCardInfo("Spoils of Adventure", 237, Rarity.UNCOMMON, mage.cards.s.SpoilsOfAdventure.class)); cards.add(new SetCardInfo("Springmantle Cleric", 205, Rarity.UNCOMMON, mage.cards.s.SpringmantleCleric.class)); - cards.add(new SetCardInfo("Squad Commander", 323, Rarity.RARE, mage.cards.s.SquadCommander.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Squad Commander", 41, Rarity.RARE, mage.cards.s.SquadCommander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Squad Commander", 41, Rarity.RARE, mage.cards.s.SquadCommander.class)); cards.add(new SetCardInfo("Stonework Packbeast", 255, Rarity.COMMON, mage.cards.s.StoneworkPackbeast.class)); cards.add(new SetCardInfo("Strength of Solidarity", 206, Rarity.COMMON, mage.cards.s.StrengthOfSolidarity.class)); cards.add(new SetCardInfo("Subtle Strike", 128, Rarity.COMMON, mage.cards.s.SubtleStrike.class)); cards.add(new SetCardInfo("Sure-Footed Infiltrator", 83, Rarity.UNCOMMON, mage.cards.s.SureFootedInfiltrator.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", 274, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 382, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Swarm Shambler", 207, Rarity.RARE, mage.cards.s.SwarmShambler.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swarm Shambler", 362, Rarity.RARE, mage.cards.s.SwarmShambler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swarm Shambler", 207, Rarity.RARE, mage.cards.s.SwarmShambler.class)); cards.add(new SetCardInfo("Synchronized Spellcraft", 168, Rarity.COMMON, mage.cards.s.SynchronizedSpellcraft.class)); - cards.add(new SetCardInfo("Taborax, Hope's Demise", 129, Rarity.RARE, mage.cards.t.TaboraxHopesDemise.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Taborax, Hope's Demise", 346, Rarity.RARE, mage.cards.t.TaboraxHopesDemise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Taborax, Hope's Demise", 129, Rarity.RARE, mage.cards.t.TaboraxHopesDemise.class)); cards.add(new SetCardInfo("Tajuru Blightblade", 208, Rarity.COMMON, mage.cards.t.TajuruBlightblade.class)); - cards.add(new SetCardInfo("Tajuru Paragon", 209, Rarity.RARE, mage.cards.t.TajuruParagon.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Tajuru Paragon", 363, Rarity.RARE, mage.cards.t.TajuruParagon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tajuru Paragon", 209, Rarity.RARE, mage.cards.t.TajuruParagon.class)); cards.add(new SetCardInfo("Tajuru Snarecaster", 210, Rarity.COMMON, mage.cards.t.TajuruSnarecaster.class)); cards.add(new SetCardInfo("Tangled Florahedron", 211, Rarity.UNCOMMON, mage.cards.t.TangledFlorahedron.class)); - cards.add(new SetCardInfo("Tangled Vale", 211, Rarity.UNCOMMON, mage.cards.t.TangledVale.class)); cards.add(new SetCardInfo("Taunting Arbormage", 212, Rarity.UNCOMMON, mage.cards.t.TauntingArbormage.class)); cards.add(new SetCardInfo("Tazeem Raptor", 43, Rarity.COMMON, mage.cards.t.TazeemRaptor.class)); cards.add(new SetCardInfo("Tazeem Roilmage", 84, Rarity.COMMON, mage.cards.t.TazeemRoilmage.class)); - cards.add(new SetCardInfo("Tazri, Beacon of Unity", 324, Rarity.MYTHIC, mage.cards.t.TazriBeaconOfUnity.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Tazri, Beacon of Unity", 44, Rarity.MYTHIC, mage.cards.t.TazriBeaconOfUnity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tazri, Beacon of Unity", 44, Rarity.MYTHIC, mage.cards.t.TazriBeaconOfUnity.class)); cards.add(new SetCardInfo("Teeterpeak Ambusher", 169, Rarity.COMMON, mage.cards.t.TeeterpeakAmbusher.class)); - cards.add(new SetCardInfo("Territorial Scythecat", 213, Rarity.COMMON, mage.cards.t.TerritorialScythecat.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Territorial Scythecat", 310, Rarity.COMMON, mage.cards.t.TerritorialScythecat.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Thieving Skydiver", 335, Rarity.RARE, mage.cards.t.ThievingSkydiver.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Thieving Skydiver", 85, Rarity.RARE, mage.cards.t.ThievingSkydiver.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Throne of Makindi", 265, Rarity.RARE, mage.cards.t.ThroneOfMakindi.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Throne of Makindi", 379, Rarity.RARE, mage.cards.t.ThroneOfMakindi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Territorial Scythecat", 213, Rarity.COMMON, mage.cards.t.TerritorialScythecat.class)); + cards.add(new SetCardInfo("Thieving Skydiver", 85, Rarity.RARE, mage.cards.t.ThievingSkydiver.class)); + cards.add(new SetCardInfo("Throne of Makindi", 265, Rarity.RARE, mage.cards.t.ThroneOfMakindi.class)); cards.add(new SetCardInfo("Thundering Rebuke", 170, Rarity.UNCOMMON, mage.cards.t.ThunderingRebuke.class)); cards.add(new SetCardInfo("Thundering Sparkmage", 171, Rarity.UNCOMMON, mage.cards.t.ThunderingSparkmage.class)); cards.add(new SetCardInfo("Thwart the Grave", 130, Rarity.UNCOMMON, mage.cards.t.ThwartTheGrave.class)); - cards.add(new SetCardInfo("Timbercrown Pathway", 261, Rarity.RARE, mage.cards.t.TimbercrownPathway.class)); - cards.add(new SetCardInfo("Timbercrown Pathway", 287, Rarity.RARE, mage.cards.t.TimbercrownPathway.class)); cards.add(new SetCardInfo("Tormenting Voice", 172, Rarity.COMMON, mage.cards.t.TormentingVoice.class)); cards.add(new SetCardInfo("Tuktuk Rubblefort", 173, Rarity.COMMON, mage.cards.t.TuktukRubblefort.class)); cards.add(new SetCardInfo("Turntimber Ascetic", 214, Rarity.COMMON, mage.cards.t.TurntimberAscetic.class)); cards.add(new SetCardInfo("Turntimber Symbiosis", 215, Rarity.MYTHIC, mage.cards.t.TurntimberSymbiosis.class)); - cards.add(new SetCardInfo("Turntimber Symbiosis", 364, Rarity.MYTHIC, mage.cards.t.TurntimberSymbiosis.class)); - cards.add(new SetCardInfo("Turntimber, Serpentine Wood", 215, Rarity.MYTHIC, mage.cards.t.TurntimberSerpentineWood.class)); - cards.add(new SetCardInfo("Turntimber, Serpentine Wood", 364, Rarity.MYTHIC, mage.cards.t.TurntimberSerpentineWood.class)); cards.add(new SetCardInfo("Umara Mystic", 238, Rarity.UNCOMMON, mage.cards.u.UmaraMystic.class)); - cards.add(new SetCardInfo("Umara Skyfalls", 86, Rarity.UNCOMMON, mage.cards.u.UmaraSkyfalls.class)); cards.add(new SetCardInfo("Umara Wizard", 86, Rarity.UNCOMMON, mage.cards.u.UmaraWizard.class)); cards.add(new SetCardInfo("Utility Knife", 256, Rarity.COMMON, mage.cards.u.UtilityKnife.class)); cards.add(new SetCardInfo("Valakut Awakening", 174, Rarity.RARE, mage.cards.v.ValakutAwakening.class)); - cards.add(new SetCardInfo("Valakut Awakening", 355, Rarity.RARE, mage.cards.v.ValakutAwakening.class)); - cards.add(new SetCardInfo("Valakut Exploration", 175, Rarity.RARE, mage.cards.v.ValakutExploration.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Valakut Exploration", 303, Rarity.RARE, mage.cards.v.ValakutExploration.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Valakut Stoneforge", 174, Rarity.RARE, mage.cards.v.ValakutStoneforge.class)); - cards.add(new SetCardInfo("Valakut Stoneforge", 355, Rarity.RARE, mage.cards.v.ValakutStoneforge.class)); + cards.add(new SetCardInfo("Valakut Exploration", 175, Rarity.RARE, mage.cards.v.ValakutExploration.class)); cards.add(new SetCardInfo("Vanquish the Weak", 131, Rarity.COMMON, mage.cards.v.VanquishTheWeak.class)); cards.add(new SetCardInfo("Vastwood Fortification", 216, Rarity.UNCOMMON, mage.cards.v.VastwoodFortification.class)); cards.add(new SetCardInfo("Vastwood Surge", 217, Rarity.UNCOMMON, mage.cards.v.VastwoodSurge.class)); - cards.add(new SetCardInfo("Vastwood Thicket", 216, Rarity.UNCOMMON, mage.cards.v.VastwoodThicket.class)); - cards.add(new SetCardInfo("Verazol, the Split Current", 239, Rarity.RARE, mage.cards.v.VerazolTheSplitCurrent.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Verazol, the Split Current", 370, Rarity.RARE, mage.cards.v.VerazolTheSplitCurrent.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Veteran Adventurer", 218, Rarity.UNCOMMON, mage.cards.v.VeteranAdventurer.class)); cards.add(new SetCardInfo("Vine Gecko", 219, Rarity.UNCOMMON, mage.cards.v.VineGecko.class)); - cards.add(new SetCardInfo("Wayward Guide-Beast", 176, Rarity.RARE, mage.cards.w.WaywardGuideBeast.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Wayward Guide-Beast", 356, Rarity.RARE, mage.cards.w.WaywardGuideBeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wayward Guide-Beast", 176, Rarity.RARE, mage.cards.w.WaywardGuideBeast.class)); cards.add(new SetCardInfo("Windrider Wizard", 87, Rarity.UNCOMMON, mage.cards.w.WindriderWizard.class)); - cards.add(new SetCardInfo("Yasharn, Implacable Earth", 240, Rarity.RARE, mage.cards.y.YasharnImplacableEarth.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Yasharn, Implacable Earth", 371, Rarity.RARE, mage.cards.y.YasharnImplacableEarth.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Zagras, Thief of Heartbeats", 241, Rarity.RARE, mage.cards.z.ZagrasThiefOfHeartbeats.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Zagras, Thief of Heartbeats", 372, Rarity.RARE, mage.cards.z.ZagrasThiefOfHeartbeats.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Zareth San, the Trickster", 242, Rarity.RARE, mage.cards.z.ZarethSanTheTrickster.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Zareth San, the Trickster", 373, Rarity.RARE, mage.cards.z.ZarethSanTheTrickster.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Zof Bloodbog", 132, Rarity.UNCOMMON, mage.cards.z.ZofBloodbog.class)); + cards.add(new SetCardInfo("Yasharn, Implacable Earth", 240, Rarity.RARE, mage.cards.y.YasharnImplacableEarth.class)); + cards.add(new SetCardInfo("Zagras, Thief of Heartbeats", 241, Rarity.RARE, mage.cards.z.ZagrasThiefOfHeartbeats.class)); + cards.add(new SetCardInfo("Zareth San, the Trickster", 242, Rarity.RARE, mage.cards.z.ZarethSanTheTrickster.class)); cards.add(new SetCardInfo("Zof Consumption", 132, Rarity.UNCOMMON, mage.cards.z.ZofConsumption.class)); cards.add(new SetCardInfo("Zulaport Duelist", 88, Rarity.COMMON, mage.cards.z.ZulaportDuelist.class)); - - cards.removeIf(setCardInfo -> checkName(setCardInfo.getName())); // remove when mechanics are fully implemented - } - - private static boolean checkName(String name) { - boolean keepNonland = false; - keepNonland = true; // comment out this line to test front faces of MDFCs - if (keepNonland && unfinishedNonland.contains(name)) { - return true; - } - return unfinishedLand.contains(name); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ChangelingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ChangelingTest.java index 1c745fb4099..a694a6cc216 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ChangelingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ChangelingTest.java @@ -2,7 +2,10 @@ package org.mage.test.cards.abilities.keywords; import mage.abilities.Ability; +import mage.abilities.keyword.ChangelingAbility; +import mage.abilities.keyword.HasteAbility; import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.permanent.Permanent; import org.junit.Assert; @@ -10,7 +13,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class ChangelingTest extends CardTestPlayerBase { @@ -80,7 +82,7 @@ public class ChangelingTest extends CardTestPlayerBase { /** * NOTE: As of 05/06/2017 this test is failing due to a bug in code. * See issue #3316 - * + *

* Kaseto, Orochi Archmage do not give Chameleon Colossus +2/+2 , even though Chameleon Colossus should have the "snake" type */ @Test @@ -103,10 +105,10 @@ public class ChangelingTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Chameleon Colossus"); /* Nessian Asp {4}{G} - 4/5 - * Creature — Snake - * Reach - * {6}{G}: Monstrosity 4. (If this creature isn't monstrous, put four +1/+1 counters on it and it becomes monstrous.) - */ + * Creature — Snake + * Reach + * {6}{G}: Monstrosity 4. (If this creature isn't monstrous, put four +1/+1 counters on it and it becomes monstrous.) + */ addCard(Zone.BATTLEFIELD, playerA, "Nessian Asp"); @@ -122,4 +124,41 @@ public class ChangelingTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Chameleon Colossus", 6, 6); } + + @Test + public void testLoseAllCreatureTypes() { + addCard(Zone.BATTLEFIELD, playerA, "Game-Trail Changeling"); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Chieftain"); + addCard(Zone.HAND, playerA, "Nameless Inversion"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nameless Inversion", "Game-Trail Changeling"); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + // Should have no creature types but still have the Changeling ability + assertPowerToughness(playerA, "Game-Trail Changeling", 7, 1); + assertNotSubtype("Game-Trail Changeling", SubType.SHAPESHIFTER); + assertAbility(playerA, "Game-Trail Changeling", HasteAbility.getInstance(), false); + assertAbility(playerA, "Game-Trail Changeling", ChangelingAbility.getInstance(), true); + } + + @Test + public void testLoseAbilities() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerB, "Game-Trail Changeling"); + addCard(Zone.HAND, playerA, "Merfolk Trickster"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Merfolk Trickster"); + addTarget(playerA, "Game-Trail Changeling"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTapped("Game-Trail Changeling", true); + assertSubtype("Game-Trail Changeling", SubType.GOBLIN); + assertSubtype("Game-Trail Changeling", SubType.ELF); + assertSubtype("Game-Trail Changeling", SubType.SHAPESHIFTER); + assertAbility(playerB, "Game-Trail Changeling", ChangelingAbility.getInstance(), false); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/DisruptingShoalTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/DisruptingShoalTest.java index 43f4468d968..59b499611c5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/DisruptingShoalTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/DisruptingShoalTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.abilities.oneshot.counterspell; import mage.constants.PhaseStep; @@ -7,11 +6,20 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class DisruptingShoalTest extends CardTestPlayerBase { + // https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/762504-disrupting-shoal-and-fuse-split-cards + // For the purpose of Disrupting Shoal, a split card in your hand has a converted mana cost equal to the sum of + // the converted mana costs of its two halves, because "[t]he mana cost of a split card is the combined mana + // costs of its two halves" while it's in your hand (C.R. 708.4, 708.4b, 202.3). For example, Beck & Call + // has converted mana cost 8 while it's in your hand. You can exile a split card with Disrupting Shoal + // only if that card has converted mana cost X. Whether the card has fuse or not doesn't matter. + // Nevertheless, Disrupting Shoal can still target a spell even if X doesn't match that spell's converted + // mana cost, because Disrupting Shoal requires only a "spell" as a target (C.R. 114.1a, 601.2c); it's just + // that it will do nothing if X doesn't match the targeted spell's converted mana cost when it resolves. + @Test public void testWithManaPaymentEqual() { addCard(Zone.HAND, playerA, "Silvercoat Lion"); @@ -23,11 +31,13 @@ public class DisruptingShoalTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Island", 4); // {X}{U}{U} castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Disrupting Shoal", "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Disrupting Shoal", "Silvercoat Lion", "Silvercoat Lion"); setChoice(playerB, "X=2"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerB, "Disrupting Shoal", 1); assertGraveyardCount(playerA, "Silvercoat Lion", 1); @@ -44,11 +54,13 @@ public class DisruptingShoalTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Island", 3); // {X}{U}{U} castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Disrupting Shoal", "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Disrupting Shoal", "Silvercoat Lion", "Silvercoat Lion"); setChoice(playerB, "X=1"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerB, "Disrupting Shoal", 1); assertPermanentCount(playerA, "Silvercoat Lion", 1); @@ -57,11 +69,12 @@ public class DisruptingShoalTest extends CardTestPlayerBase { /** * Test that Disrupting Shoal can be played with alternate casting costs And * the X Value is equal to the CMC of the exiled blue card - * */ @Test public void testWithBlueCardsInHand() { addCard(Zone.HAND, playerA, "Pillarfield Ox"); + + // Counter target spell with converted mana cost 2. addCard(Zone.HAND, playerA, "Spell Snare"); addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); addCard(Zone.BATTLEFIELD, playerA, "Island", 4); @@ -72,39 +85,42 @@ public class DisruptingShoalTest extends CardTestPlayerBase { addCard(Zone.HAND, playerB, "Mistfire Adept", 2); // blue cards with 4 CMC to pay Disrupting Shoal addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + // cast spell with cmc = 4 castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pillarfield Ox"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Disrupting Shoal", "Pillarfield Ox", "Pillarfield Ox"); - playerB.addChoice("Yes"); // use alternate costs = Mistfire Adept = CMC = 4 - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spell Snare", "Disrupting Shoal", "Disrupting Shoal"); + setChoice(playerB, "Yes"); // use alternate costs + setChoice(playerB, "Mistfire Adept"); // pay to cast Mistfire Adept (CMC = 4) - setStopAt(1, PhaseStep.CLEANUP); + // rules: 202.3e When calculating the converted mana cost of an object with an {X} in its mana cost, + // X is treated as 0 while the object is not on the stack, and X is treated as the number chosen for + // it while the object is on the stack. + // + // SO Spell Snare can't be played here (it need cmc 2, but Disrupting Shoal got cmc = 4 + 2) + checkPlayableAbility("can't cast Spell Snare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Spell Snare", false); + //castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spell Snare", "Disrupting Shoal", "Disrupting Shoal"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); assertGraveyardCount(playerB, "Disrupting Shoal", 1); - assertExileCount(playerB, 1); // Mistfire Adept - assertHandCount(playerB, "Mistfire Adept", 1); // One Left + assertExileCount(playerB, 1); // Mistfire Adept + assertHandCount(playerB, "Mistfire Adept", 1); // One Left - assertHandCount(playerA, "Spell Snare", 1); // Can't be cast -> no valid target + assertHandCount(playerA, "Spell Snare", 1); // Can't be cast -> no valid target assertGraveyardCount(playerA, "Pillarfield Ox", 1); } - /** - * Test that Disrupting Shoal can be played with alternate casting costs And - * the X Value can be equal to either half of a fuse card. - * - * Reported bug: "Casting Disrupting Shoal pitching Far // Away does not - * counter spells with converted mana cost 2 or 3, which it should. Instead - * it does counter spells with converted mana cost 5, which it shouldn't". - */ @Test - public void testWithFuseCardCounterCMCTwo() { + public void testWithFuseCardCounterCMC_LeftIgnore() { - // CMC 2 and CMC 3 + // cmc 2 addCard(Zone.HAND, playerA, "Grizzly Bears"); // 2/2 {1}{G} addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); @@ -117,33 +133,29 @@ public class DisruptingShoalTest extends CardTestPlayerBase { * {2}{B} Instant Target player sacrifices a creature. Fuse (You may * cast one or both halves of this card from your hand.) */ - addCard(Zone.HAND, playerB, "Far // Away", 1); + addCard(Zone.HAND, playerB, "Far // Away", 1); // cmc 2 + 3 castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Disrupting Shoal", "Grizzly Bears"); - playerB.addChoice("Yes"); // use alternate costs = 2 CMC = Far + // try to pay by split card, but can't counter -- X <> bear's cmc + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Disrupting Shoal", "Grizzly Bears", "Grizzly Bears"); + setChoice(playerB, "Yes"); // use alternative cost + setChoice(playerB, "Far // Away"); // pay by card (cmc = 5, so X = 5 too) - setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertExileCount(playerB, 1); // Far // Away should be exiled as part of Disrupting alternative cost assertGraveyardCount(playerB, "Disrupting Shoal", 1); - assertPermanentCount(playerA, "Grizzly Bears", 0); // should have been countered by Shoal - assertGraveyardCount(playerA, "Grizzly Bears", 1); + assertPermanentCount(playerA, "Grizzly Bears", 1); // can't counter (cmc 2 <> x = 5) + assertGraveyardCount(playerA, "Grizzly Bears", 0); } - /** - * Test that Disrupting Shoal can be played with alternate casting costs And - * the X Value can be equal to either half of a fuse card. - * - * Reported bug: "Casting Disrupting Shoal pitching Far // Away does not - * counter spells with converted mana cost 2 or 3, which it should. Instead - * it does counter spells with converted mana cost 5, which it shouldn't". - */ @Test - public void testWithFuseCardCounterCMCThree() { - + public void testWithFuseCardCounterCMC_RightIgnore() { + // cmc 3 addCard(Zone.HAND, playerA, "Centaur Courser"); // 3/3 {2}{G} addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); @@ -157,30 +169,25 @@ public class DisruptingShoalTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Centaur Courser"); + // try to pay by split card, but can't counter -- X <> centaur's cmc castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Disrupting Shoal", "Centaur Courser", "Centaur Courser"); - playerB.addChoice("Yes"); // use alternate costs = 3 CMC = Away + setChoice(playerB, "Yes"); // use alternative cost + setChoice(playerB, "Far // Away"); // pay by card (cmc = 5, so X = 5 too) - setStopAt(1, PhaseStep.CLEANUP); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertExileCount(playerB, 1); // Far // Away should be exiled as part of Disrupting alternative cost assertGraveyardCount(playerB, "Disrupting Shoal", 1); - assertPermanentCount(playerA, "Centaur Courser", 0); // should have been countered by Shoal - assertGraveyardCount(playerA, "Centaur Courser", 1); + assertPermanentCount(playerA, "Centaur Courser", 1); // can't counter (cmc 3 <> x = 5) + assertGraveyardCount(playerA, "Centaur Courser", 0); } - /** - * Test that Disrupting Shoal can be played with alternate casting costs And - * the X Value can be equal to either half of a fuse card. Not the combined - * cost of both. - * - * Reported bug: "Casting Disrupting Shoal pitching Far // Away does not - * counter spells with converted mana cost 2 or 3, which it should. Instead - * it does counter spells with converted mana cost 5, which it shouldn't". - */ @Test - public void testWithFuseCardShouldNotCounterCMCFive() { - + public void testWithFuseCardShouldNotCounterCMC_BothUses() { + // cmc 5 addCard(Zone.HAND, playerA, "Air Elemental"); // 4/4 Flying {3}{U}{U} addCard(Zone.BATTLEFIELD, playerA, "Island", 5); @@ -194,15 +201,19 @@ public class DisruptingShoalTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Air Elemental"); + // try to pay by split card and it works castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Disrupting Shoal", "Air Elemental", "Air Elemental"); - playerB.addChoice("Yes"); // use alternate costs = 2 or 3 CMC = Far // Away, not the combined cost! + setChoice(playerB, "Yes"); // use alternative cost + setChoice(playerB, "Far // Away"); // pay by card (cmc = 5, so X = 5 too) - setStopAt(1, PhaseStep.CLEANUP); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertExileCount(playerB, 1); // Far // Away should be exiled as part of Disrupting alternative cost assertGraveyardCount(playerB, "Disrupting Shoal", 1); - assertPermanentCount(playerA, "Air Elemental", 1); // should NOT have been countered by Shoal - assertGraveyardCount(playerA, "Air Elemental", 0); + assertPermanentCount(playerA, "Air Elemental", 0); // will counter cause (cmc 5 == x = 5) + assertGraveyardCount(playerA, "Air Elemental", 1); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ChangelingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ChangelingTest.java index c57888e58cb..a27ae3a7f6f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ChangelingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ChangelingTest.java @@ -1,5 +1,6 @@ package org.mage.test.cards.continuous; +import mage.constants.CardType; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; @@ -8,29 +9,33 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class ChangelingTest extends CardTestPlayerBase { // Mistform Ultimus is every creature type - private final String ultimus = "Mistform Ultimus"; + private static final String ultimus = "Mistform Ultimus"; // each creature gets +1/+1 for each creature you control that shares a creatureype - private final String coatOfArms = "Coat of Arms"; + private static final String coatOfArms = "Coat of Arms"; // all merfolk get +1/+1 - private final String lordOfAtlantis = "Lord of Atlantis"; + private static final String lordOfAtlantis = "Lord of Atlantis"; // all illusions get +1/+1 - private final String lordOfUnreal = "Lord of the Unreal"; + private static final String lordOfUnreal = "Lord of the Unreal"; // mutavault becomes a token that is all creature types - private final String mutavault = "Mutavault"; - + private static final String mutavault = "Mutavault"; + // vehicles have no creature type + private static final String copter = "Smuggler's Copter"; // 2/2 changeling - private final String woodlandChangeling = "Woodland Changeling"; + private static final String woodlandChangeling = "Woodland Changeling"; @Test - public void coatOfArmsTest(){ + public void coatOfArmsTest() { addCard(Zone.BATTLEFIELD, playerA, ultimus); addCard(Zone.BATTLEFIELD, playerA, coatOfArms); addCard(Zone.BATTLEFIELD, playerA, lordOfAtlantis); addCard(Zone.BATTLEFIELD, playerA, lordOfUnreal); addCard(Zone.BATTLEFIELD, playerA, mutavault); + addCard(Zone.BATTLEFIELD, playerA, copter); addCard(Zone.BATTLEFIELD, playerA, woodlandChangeling, 2); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: Until end of turn {this} becomes"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew"); + setChoice(playerA, ultimus); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); execute(); @@ -54,10 +59,15 @@ public class ChangelingTest extends CardTestPlayerBase { coat of arms: +5 */ assertPowerToughness(playerA, mutavault, 9, 9); + /* + smuggler's copter; +3 + */ + assertType(copter, CardType.CREATURE, true); + assertPowerToughness(playerA, copter, 3, 3); } @Test - public void testMetallicMimicChangelingTrigger(){ + public void testMetallicMimicChangelingTrigger() { // all creatures with the chosen subtype come into play with a +1/+1 counter final String mimic = "Metallic Mimic"; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/GainAbilityDependenciesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/GainAbilityDependenciesTest.java index 78e4bf1abca..c2eb0444742 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/GainAbilityDependenciesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/GainAbilityDependenciesTest.java @@ -82,7 +82,6 @@ public class GainAbilityDependenciesTest extends CardTestPlayerBase { // equip activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {3}", "Elephant"); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); - checkAbility("must have all type ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elephant", ChangelingAbility.class, true); // attack with +1 token attack(3, playerA, "Elephant", playerB); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SubTypeChangingEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SubTypeChangingEffectsTest.java index 2f21399ae6c..31a22dd6f69 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SubTypeChangingEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SubTypeChangingEffectsTest.java @@ -1,6 +1,7 @@ package org.mage.test.cards.continuous; import mage.cards.Card; +import mage.constants.CardType; import mage.constants.PhaseStep; import mage.constants.SubType; import mage.constants.Zone; @@ -296,6 +297,49 @@ public class SubTypeChangingEffectsTest extends CardTestPlayerBase { } } + } + @Test + public void testKeepOtherTypes() { + // Dragonshift (2013-04-15) + // Each affected creature will lose all other colors and creature types and be only red, blue, and a Dragon. + // Each will retain any other types it may have had, such as artifact. + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Gingerbrute"); + addCard(Zone.HAND, playerA, "Dragonshift"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dragonshift", "Gingerbrute"); + + setStopAt(1, PhaseStep.END_TURN); + + execute(); + + // Food is an artifact subtype and should not be removed + assertType("Gingerbrute", CardType.ARTIFACT, SubType.FOOD); + // Golem is a creature subtype and should be removed + assertType("Gingerbrute", CardType.CREATURE, SubType.DRAGON); + assertNotSubtype("Gingerbrute", SubType.GOLEM); + } + + @Test + public void testKeepOtherTypes2() { + // Dragonshift (2013-04-15) + // Each affected creature will lose all other colors and creature types and be only red, blue, and a Dragon. + // Each will retain any other types it may have had, such as artifact. + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 7); + addCard(Zone.BATTLEFIELD, playerA, "Gingerbrute"); + addCard(Zone.HAND, playerA, "Dragonshift"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dragonshift with overload"); + + setStopAt(1, PhaseStep.END_TURN); + + execute(); + + // Food is an artifact subtype and should not be removed + assertType("Gingerbrute", CardType.ARTIFACT, SubType.FOOD); + // Golem is a creature subtype and should be removed + assertType("Gingerbrute", CardType.CREATURE, SubType.DRAGON); + assertNotSubtype("Gingerbrute", SubType.GOLEM); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java index 4cb9d4a3cdc..12e66f80fb1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java @@ -1,25 +1,25 @@ package org.mage.test.cards.copy; +import mage.abilities.common.BecomesTargetTriggeredAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.LifelinkAbility; import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.permanent.Permanent; import org.junit.Assert; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.junit.Assert.*; + /** * @author noxx - * + *

* Card: You may have {this} enter the battlefield as a copy of any creature on * the battlefield, except it's an Illusion in addition to its other types and * it gains "When this creature becomes the target of a spell or ability, * sacrifice it." - * */ public class PhantasmalImageTest extends CardTestPlayerBase { @@ -130,6 +130,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase { // CardTestPlayerAPIImpl.assertPowerToughness:351->CardTestPlayerAPIImpl.assertPowerToughness:337 // There is no such creature under player's control with specified power&toughness, player=PlayerA, // cardName=Ravager of the Fells (found similar: 1, one of them: power=8 toughness=8) + /** * Tests copying already transformed creature Makes sure it still has "When * this creature becomes the target of a spell or ability, sacrifice it" @@ -313,7 +314,6 @@ public class PhantasmalImageTest extends CardTestPlayerBase { * the "When this creature becomes the target of a spell or ability, * sacrifice it." ability. I did not pay attention to see if it failed to * become an illusion too. - * */ @Test public void testCopiedFrostTitan() { @@ -426,13 +426,13 @@ public class PhantasmalImageTest extends CardTestPlayerBase { /** * Action Game State 1 -----------------> Game State 2 (On 'field) (Move to * GY) (In graveyard) - * + *

* LTB abilities such as Persist are expceptional in that they trigger based * on their existence and state of objects before the event (Game State 1, * when the card is on the battlefield) rather than after (Game State 2, * when the card is in the graveyard). It doesn't matter that the LTB * ability doesn't exist in Game State 2. [CR 603.6d] - * + *

* 603.6d Normally, objects that exist immediately after an event are * checked to see if the event matched any trigger conditions. Continuous * effects that exist at that time are used to determine what the trigger @@ -446,13 +446,12 @@ public class PhantasmalImageTest extends CardTestPlayerBase { * planeswalks away from a plane will trigger based on their existence, and * the appearance of objects, prior to the event rather than afterward. The * game has to “look back in time” to determine if these abilities trigger. - * + *

* Example: Two creatures are on the battlefield along with an artifact that * has the ability “Whenever a creature dies, you gain 1 life.” Someone * plays a spell that destroys all artifacts, creatures, and enchantments. * The artifact's ability triggers twice, even though the artifact goes to * its owner's graveyard at the same time as the creatures. - * */ @Test public void testPersist() { @@ -549,7 +548,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase { * battlefield. 12:29: Phantasmal Image [466] died 12:29: HipSomHap puts a * Wurm [7d0] token onto the battlefield 12:29: HipSomHap puts a Wurm [186] * token onto the battlefield - * + *

* To the best of my knowledge, the Phantasmal Image [466], which entered * the battlefield as a Wurmcoil Engine, should grant tokens through the * Dies-trigger as well, right? @@ -610,4 +609,69 @@ public class PhantasmalImageTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Elemental", 1); } + + @Test + public void testAnimatedArtifact() { + addCard(Zone.BATTLEFIELD, playerB, "Chimeric Staff"); + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.HAND, playerA, "Phantasmal Image"); + + setChoice(playerB, "X=1"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{X}"); + + setChoice(playerA, "Chimeric Staff"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Phantasmal Image"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + + Permanent staffA = getPermanent("Chimeric Staff", playerA); + assertTrue("Phantasmal Image should be an artifact", staffA.isArtifact()); + assertTrue("Phantasmal Image should not be a creature", !staffA.isCreature()); + assertTrue("Phantasmal Image should not be an Illusion", !staffA.hasSubtype(SubType.ILLUSION, currentGame)); + assertTrue("Phantasmal Image should not be a Construct", !staffA.hasSubtype(SubType.CONSTRUCT, currentGame)); + assertTrue("Phantasmal Image should have the sacrifice trigger", staffA.getAbilities(currentGame).containsClass(BecomesTargetTriggeredAbility.class)); + + Permanent staffB = getPermanent("Chimeric Staff", playerB); + assertTrue("Chimeric Staff should be an artifact", staffB.isArtifact()); + assertTrue("Chimeric Staff should be a creature", staffB.isCreature()); + assertTrue("Chimeric Staff should be a Construct", staffB.hasSubtype(SubType.CONSTRUCT, currentGame)); + } + + @Test + public void testAnimatedTribal() { + addCard(Zone.BATTLEFIELD, playerB, "Cloak and Dagger"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.HAND, playerA, "Karn's Touch"); + addCard(Zone.HAND, playerA, "Phantasmal Image"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Karn's Touch", "Cloak and Dagger"); + + setChoice(playerA, "Cloak and Dagger"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Phantasmal Image"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + + Permanent cloakA = getPermanent("Cloak and Dagger", playerA); + assertTrue("Phantasmal Image should be an artifact", cloakA.isArtifact()); + assertTrue("Phantasmal Image should be tribal", cloakA.isTribal()); + assertTrue("Phantasmal Image should not be a creature", !cloakA.isCreature()); + assertTrue("Phantasmal Image should be a Rogue", cloakA.hasSubtype(SubType.ROGUE, currentGame)); + assertTrue("Phantasmal Image should be an Illusion", cloakA.hasSubtype(SubType.ILLUSION, currentGame)); + assertTrue("Phantasmal Image should be an Equipment", cloakA.hasSubtype(SubType.EQUIPMENT, currentGame)); + assertTrue("Phantasmal Image should have the sacrifice trigger", cloakA.getAbilities(currentGame).containsClass(BecomesTargetTriggeredAbility.class)); + + Permanent cloakB = getPermanent("Cloak and Dagger", playerB); + assertTrue("Cloak and Dagger should be an artifact", cloakB.isArtifact()); + assertTrue("Cloak and Dagger should be a creature", cloakB.isCreature()); + assertTrue("Cloak and Dagger should be tribal", cloakB.isTribal()); + assertTrue("Cloak and Dagger should be a Rogue", cloakB.hasSubtype(SubType.ROGUE, currentGame)); + assertTrue("Cloak and Dagger should be an Equipment", cloakB.hasSubtype(SubType.EQUIPMENT, currentGame)); + } } 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 new file mode 100644 index 00000000000..1df1ac3f5b3 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java @@ -0,0 +1,495 @@ +package org.mage.test.cards.cost.modaldoublefaces; + +import mage.cards.Card; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.permanent.PermanentCard; +import mage.util.CardUtil; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class ModalDoubleFacesCardsTest extends CardTestPlayerBase { + + @Test + public void test_Playable_AsCreature() { + removeAllCardsFromHand(playerA); + + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.HAND, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6 - 1); + addCard(Zone.HAND, playerA, "Mountain", 1); + + // can't cast without mana, but can play land + checkPlayableAbility("before land left", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", false); + checkPlayableAbility("before land right", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true); + checkPlayableAbility("before land both", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior // Akoum Teeth", false); + + // play land + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain"); + // can cast creature, but can't play land + checkPlayableAbility("after land left", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", true); + checkPlayableAbility("after land right", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", false); + checkPlayableAbility("after land both", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior // Akoum Teeth", false); + + // cast creature + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkHandCount("hand after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Teeth", 0); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Playable_AsLand() { + removeAllCardsFromHand(playerA); + + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.HAND, playerA, "Akoum Warrior"); + addCard(Zone.HAND, playerA, "Mountain", 1); + + // cast and play restrictions tested in prev test, so use here simple land play + + checkPlayableAbility("before play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", false); + checkPlayableAbility("before play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Mountain", true); + checkHandCount("before play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + + // play as land + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Teeth"); + checkHandCount("hand after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2 - 1); + checkPermanentCount("after play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0); + checkPermanentCount("after play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Teeth", 1); + checkPlayableAbility("after play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", false); + checkPlayableAbility("can't play second land", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Mountain", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_CostModification() { + removeAllCardsFromHand(playerA); + + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.HAND, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6 - 3); + + addCustomEffect_SpellCostModification(playerA, -3); + + // cast creature + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkHandCount("hand after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Teeth", 0); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_PlayFromNonHand_LibraryByBolassCitadel() { + removeAllCardsFromHand(playerA); + removeAllCardsFromLibrary(playerA); + + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.LIBRARY, playerA, "Akoum Warrior"); + // + // You may play the top card of your library. If you cast a spell this way, pay life equal + // to its converted mana cost rather than pay its mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel"); + + checkLibraryCount("library before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + checkPlayableAbility("can play as land", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", true); + checkPlayableAbility("can play as creature", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true); + + // play as creature + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkLibraryCount("library after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Teeth", 0); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20 - 6); // creature life pay instead mana + } + + @Test + public void test_PlayFromNonHand_GraveyardByYawgmothsAgenda() { + removeAllCardsFromHand(playerA); + removeAllCardsFromLibrary(playerA); + + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.GRAVEYARD, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + // + // You may play cards from your graveyard. + addCard(Zone.BATTLEFIELD, playerA, "Yawgmoth's Agenda"); + + checkGraveyardCount("grave before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + checkPlayableAbility("can play as land", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", true); + checkPlayableAbility("can play as creature", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true); + + // play as creature + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("grave after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Teeth", 0); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Characteristics() { + // rules: + // While a double-faced card isn’t on the stack or battlefield, consider only the characteristics + // of its front face. For example, the above card has only the characteristics of Sejiri Shelter + // in the graveyard, even if it was Sejiri Glacier on the battlefield before it was put into the + // graveyard. Notably, this means that Sejiri Shelter is a nonland card even though you could play + // it as a land + removeAllCardsFromHand(playerA); + removeAllCardsFromLibrary(playerA); + + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.HAND, playerA, "Akoum Warrior"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // stats in hand + Assert.assertEquals(1, getHandCards(playerA).size()); + Card card = getHandCards(playerA).get(0); + Assert.assertFalse("must be non land", card.isLand()); + Assert.assertTrue("must be creature", card.isCreature()); + Assert.assertTrue("must be minotaur", card.getSubtype(currentGame).contains(SubType.MINOTAUR)); + Assert.assertEquals("power", 4, card.getPower().getValue()); + Assert.assertEquals("toughness", 5, card.getToughness().getValue()); + } + + @Test + public void test_PlayFromNonHand_GraveyardByFlashback() { + removeAllCardsFromHand(playerA); + removeAllCardsFromLibrary(playerA); + + // Emeria's Call - Sorcery {4}{W}{W}{W} + // Emeria, Shattered Skyclave - land + // Create two 4/4 white Angel Warrior creature tokens with flying. Non-Angel creatures you control gain indestructible until your next turn. + addCard(Zone.GRAVEYARD, playerA, "Emeria's Call"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + // + // 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. + addCard(Zone.HAND, playerA, "Snapcaster Mage"); // {1}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + checkGraveyardCount("grave before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Emeria's Call", 1); + checkPlayableAbility("can't play as sorcery", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Emeria's Call", false); + checkPlayableAbility("can't play as land", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Emeria, Shattered Skyclave", false); + + // cast Snapcaster and give flashback + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage"); + addTarget(playerA, "Emeria's Call"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("grave before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Emeria's Call", 1); + checkPlayableAbility("can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback", true); + + // cast as sorcery with flashback + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkExileCount("exile after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Emeria's Call", 1); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Emeria's Call", 0); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Emeria, Shattered Skyclave", 0); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Snapcaster Mage", 1); + } + + @Test + public void test_Zones_AfterCast() { + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.HAND, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + + // prepare mdf permanent + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + Card card = currentGame.getState().getBattlefield().getAllPermanents() + .stream() + .filter(p -> CardUtil.haveSameNames(p, "Akoum Warrior", currentGame)) + .findFirst() + .orElse(null); + Assert.assertNotNull(card); + Assert.assertEquals("permanent card must be on battlefield", Zone.BATTLEFIELD, currentGame.getState().getZone(card.getId())); + Assert.assertEquals("main permanent card must be on battlefield", Zone.BATTLEFIELD, currentGame.getState().getZone(card.getMainCard().getId())); + Assert.assertEquals("half card must be on battlefield", Zone.BATTLEFIELD, currentGame.getState().getZone(((PermanentCard) card).getCard().getId())); + Assert.assertEquals("main card must be on battlefield", Zone.BATTLEFIELD, currentGame.getState().getZone(((PermanentCard) card).getCard().getMainCard().getId())); + } + + @Test + public void test_Zones_AfterExile() { + // {2}, {tap}: Exile target permanent you control. + addCard(Zone.BATTLEFIELD, playerA, "Synod Sanctum"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.HAND, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + + // prepare mdf permanent + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + + // exile + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, ", "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("exile", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0); + checkExileCount("exile", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + Card card = currentGame.getState().getExile().getAllCards(currentGame) + .stream() + .filter(p -> CardUtil.haveSameNames(p, "Akoum Warrior", currentGame)) + .findFirst() + .orElse(null); + Assert.assertNotNull(card); + Assert.assertTrue("must be mdf card", card instanceof ModalDoubleFacesCard); + ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card; + Assert.assertEquals("card must be on exile", Zone.EXILED, currentGame.getState().getZone(mdfCard.getId())); + Assert.assertEquals("left part must be on exile", Zone.EXILED, currentGame.getState().getZone(mdfCard.getLeftHalfCard().getId())); + Assert.assertEquals("right part must be on exile", Zone.EXILED, currentGame.getState().getZone(mdfCard.getRightHalfCard().getId())); + } + + @Test + public void test_ExileAndReturnToBattlefield_AsCreature() { + // rules: + // If an effect puts a double-faced card onto the battlefield, it enters with its front face up. If that + // front face can’t be put onto the battlefield, it doesn’t enter the battlefield. For example, if an + // effect exiles Sejiri Glacier and returns it to the battlefield, it remains in exile because an instant + // can’t be put onto the battlefield. + + // +2: Exile target permanent you own. Return it to the battlefield under your control at the beginning of the next end step. + addCard(Zone.BATTLEFIELD, playerA, "Venser, the Sojourner"); + // + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.HAND, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + + // prepare mdf permanent + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + + // exile + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2:", "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("exile", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0); + checkExileCount("exile", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + + // return at the end + showBattlefield("hmm b", 2, PhaseStep.PRECOMBAT_MAIN, playerA); + showExile("hmm e", 2, PhaseStep.PRECOMBAT_MAIN, playerA); + showGraveyard("hmm g", 2, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPermanentCount("return", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + checkExileCount("return", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ExileAndReturnToBattlefield_AsLand() { + // rules: + // If an effect puts a double-faced card onto the battlefield, it enters with its front face up. If that + // front face can’t be put onto the battlefield, it doesn’t enter the battlefield. For example, if an + // effect exiles Sejiri Glacier and returns it to the battlefield, it remains in exile because an instant + // can’t be put onto the battlefield. + + // SO it can't return card as land + + // +2: Exile target permanent you own. Return it to the battlefield under your control at the beginning of the next end step. + addCard(Zone.BATTLEFIELD, playerA, "Venser, the Sojourner"); + // + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.HAND, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + + // prepare mdf permanent + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + + // exile + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2:", "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("exile", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0); + checkExileCount("exile", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + + // return at the end + showBattlefield("hmm b", 2, PhaseStep.PRECOMBAT_MAIN, playerA); + showExile("hmm e", 2, PhaseStep.PRECOMBAT_MAIN, playerA); + showGraveyard("hmm g", 2, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPermanentCount("return", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + checkExileCount("return", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ChooseName_AsCreature() { + // rules: + // If an effect instructs a player to choose a card name, the name of either face may be chosen. If that + // effect or a linked ability refers to a spell with the chosen name being cast and/or a land with the + // chosen name being played, it considers only the chosen name, not the other face’s name. + + // Choose a card name. Until your next turn, spells with the chosen name can’t be cast and lands with the chosen name can’t be played. + addCard(Zone.HAND, playerA, "Conjurer's Ban"); // {W}{B} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.HAND, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + + // can play before + checkPlayableAbility("can play creature before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", true); + checkPlayableAbility("can play land before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true); + + // make restrict for creature + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Conjurer's Ban"); + setChoice(playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("can't play creature after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", false); + checkPlayableAbility("can play land after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true); + + // can play again later + checkPlayableAbility("can play creature again", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", true); + checkPlayableAbility("can play land again", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ChooseName_AsLand() { + // rules: + // If an effect instructs a player to choose a card name, the name of either face may be chosen. If that + // effect or a linked ability refers to a spell with the chosen name being cast and/or a land with the + // chosen name being played, it considers only the chosen name, not the other face’s name. + + // Choose a card name. Until your next turn, spells with the chosen name can’t be cast and lands with the chosen name can’t be played. + addCard(Zone.HAND, playerA, "Conjurer's Ban"); // {W}{B} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // + // Akoum Warrior {5}{R} - creature + // Akoum Teeth - land + addCard(Zone.HAND, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + + // can play before + checkPlayableAbility("can play creature before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", true); + checkPlayableAbility("can play land before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true); + + // make restrict for land + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Conjurer's Ban"); + setChoice(playerA, "Akoum Teeth"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("can play creature after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", true); + checkPlayableAbility("can't play land after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", false); + + // can play again later + checkPlayableAbility("can play creature again", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", true); + checkPlayableAbility("can play land again", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Single_MalakirRebirth() { + // Malakir Rebirth + // Choose target creature. You lose 2 life. Until end of turn, that creature gains "When this creature dies, return it to the battlefield tapped under its owner's control." + addCard(Zone.HAND, playerA, "Malakir Rebirth"); // {B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + // cast instant and give gained ability + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Malakir Rebirth", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1); + + // check gained ability (bear must be returned after die) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1); + checkGraveyardCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lgn/MistformUltimusTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lgn/MistformUltimusTest.java new file mode 100644 index 00000000000..cdaca04442b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lgn/MistformUltimusTest.java @@ -0,0 +1,42 @@ +package org.mage.test.cards.single.lgn; + +import mage.abilities.keyword.HasteAbility; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class MistformUltimusTest extends CardTestPlayerBase { + + private static final String ultimus = "Mistform Ultimus"; + private static final String chieftain = "Goblin Chieftain"; + private static final String gametrail = "Game-Trail Changeling"; + private static final String inversion = "Nameless Inversion"; + + @Test + public void testMistformUltimus() { + addCard(Zone.BATTLEFIELD, playerA, ultimus); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertSubtype(ultimus, SubType.GOBLIN); + } + + @Test + public void testGoblinChieftain() { + addCard(Zone.BATTLEFIELD, playerA, ultimus); + addCard(Zone.BATTLEFIELD, playerA, chieftain); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPowerToughness(playerA, ultimus, 4, 4); + assertAbility(playerA, ultimus, HasteAbility.getInstance(), true); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/thb/OneWithTheStarsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/thb/OneWithTheStarsTest.java new file mode 100644 index 00000000000..8f7dcb38598 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/thb/OneWithTheStarsTest.java @@ -0,0 +1,103 @@ +package org.mage.test.cards.single.thb; + +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.mana.AnyColorManaAbility; +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 OneWithTheStarsTest extends CardTestPlayerBase { + + private static final String stars = "One with the Stars"; + private static final String knight = "Dragonsoul Knight"; + private static final String brute = "Gingerbrute"; + private static final String shrine = "Honden of Cleansing Fire"; + private static final String blossom = "Bitterblossom"; + + private void makeRainbowLand(int count) { + for (int i = 0; i < count; i++) { + addCustomCardWithAbility( + "Rainbow", playerA, new AnyColorManaAbility(), + null, CardType.LAND, "", Zone.BATTLEFIELD + ); + } + } + + @Test + public void testDragonsoulKnight() { + addCard(Zone.HAND, playerA, stars); + addCard(Zone.BATTLEFIELD, playerA, knight); + makeRainbowLand(9); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, stars, knight); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{W}{U}"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertType(knight, CardType.ENCHANTMENT, true); + assertType(knight, CardType.CREATURE, false); + assertNotSubtype(knight, SubType.HUMAN); + assertNotSubtype(knight, SubType.KNIGHT); + assertNotSubtype(knight, SubType.DRAGON); + assertAbility(playerA, knight, FlyingAbility.getInstance(), true); + assertAbility(playerA, knight, TrampleAbility.getInstance(), true); + } + + @Test + public void testGingerbrute() { + addCard(Zone.HAND, playerA, stars); + addCard(Zone.BATTLEFIELD, playerA, brute); + makeRainbowLand(4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, stars, brute); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertType(brute, CardType.ENCHANTMENT, true); + assertType(brute, CardType.ARTIFACT, false); + assertType(brute, CardType.CREATURE, false); + assertNotSubtype(brute, SubType.GOLEM); + assertNotSubtype(brute, SubType.FOOD); + } + + @Test + public void testShrine() { + addCard(Zone.HAND, playerA, stars); + addCard(Zone.BATTLEFIELD, playerA, shrine); + makeRainbowLand(4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, stars, shrine); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertType(shrine, CardType.ENCHANTMENT, SubType.SHRINE); + } + + @Test + public void testBitterblossom() { + addCard(Zone.HAND, playerA, stars); + addCard(Zone.BATTLEFIELD, playerA, blossom); + makeRainbowLand(4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, stars, blossom); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertType(blossom, CardType.ENCHANTMENT, true); + assertType(blossom, CardType.TRIBAL, false); + assertNotSubtype(blossom, SubType.FAERIE); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/BlazingTorchTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/BlazingTorchTest.java index 172aa191e1f..5ca45d9fa8e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/BlazingTorchTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/BlazingTorchTest.java @@ -22,11 +22,13 @@ public class BlazingTorchTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, lion); addCard(Zone.BATTLEFIELD, playerA, torch); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", lion); activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T},", playerB); + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 18); @@ -39,15 +41,21 @@ public class BlazingTorchTest extends CardTestPlayerBase { public void testCardCopied() { addCard(Zone.BATTLEFIELD, playerA, plains, 2); addCard(Zone.BATTLEFIELD, playerA, lion); + // You may have Masterwork of Ingenuity enter the battlefield as a copy of any Equipment on the battlefield. addCard(Zone.HAND, playerA, masterwork); addCard(Zone.BATTLEFIELD, playerB, torch); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, masterwork); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip"); + setChoice(playerA, "Yes"); // use copy + setChoice(playerA, torch); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", lion); activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T},", playerB); + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 18); @@ -69,11 +77,13 @@ public class BlazingTorchTest extends CardTestPlayerBase { setChoice(playerB, torch); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, halo); - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", lion); activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T},", playerB); + setStrictChooseMode(true); setStopAt(3, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); // Ability resolves as it's from the equipped creature but damage is from torch and is prevented 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 75999b3022c..977c05ebd40 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 @@ -813,6 +813,13 @@ public class TestPlayer implements Player { wasProccessed = true; } + // check library count: card name, count + if (params[0].equals(CHECK_COMMAND_LIBRARY_COUNT) && params.length == 3) { + assertLibraryCount(action, game, computerPlayer, params[1], Integer.parseInt(params[2])); + actions.remove(action); + wasProccessed = true; + } + // check hand count: count if (params[0].equals(CHECK_COMMAND_HAND_COUNT) && params.length == 2) { assertHandCount(action, game, computerPlayer, Integer.parseInt(params[1])); @@ -1347,6 +1354,17 @@ public class TestPlayer implements Player { Assert.assertEquals(action.getActionName() + " - card " + permanentName + " must exists in graveyard zone with " + count + " instances", count, foundedCount); } + private void assertLibraryCount(PlayerAction action, Game game, Player player, String permanentName, int count) { + int foundedCount = 0; + for (Card card : player.getLibrary().getCards(game)) { + if (hasObjectTargetNameOrAlias(card, permanentName)) { + foundedCount++; + } + } + + Assert.assertEquals(action.getActionName() + " - card " + permanentName + " must exists in library with " + count + " instances", count, foundedCount); + } + private void assertHandCount(PlayerAction action, Game game, Player player, int count) { Assert.assertEquals(action.getActionName() + " - hand must contain " + count, count, player.getHand().size()); } 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 67b1ce7e49e..54f05da1b79 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 @@ -94,6 +94,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public static final String CHECK_COMMAND_PERMANENT_COUNTERS = "PERMANENT_COUNTERS"; public static final String CHECK_COMMAND_EXILE_COUNT = "EXILE_COUNT"; public static final String CHECK_COMMAND_GRAVEYARD_COUNT = "GRAVEYARD_COUNT"; + public static final String CHECK_COMMAND_LIBRARY_COUNT = "LIBRARY_COUNT"; public static final String CHECK_COMMAND_HAND_COUNT = "HAND_COUNT"; public static final String CHECK_COMMAND_HAND_CARD_COUNT = "HAND_CARD_COUNT"; public static final String CHECK_COMMAND_COMMAND_CARD_COUNT = "COMMAND_CARD_COUNT"; @@ -433,6 +434,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement check(checkName, turnNum, step, player, CHECK_COMMAND_GRAVEYARD_COUNT, permanentName, count.toString()); } + public void checkLibraryCount(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, Integer count) { + //Assert.assertNotEquals("", permanentName); + check(checkName, turnNum, step, player, CHECK_COMMAND_LIBRARY_COUNT, permanentName, count.toString()); + } + public void checkHandCount(String checkName, int turnNum, PhaseStep step, TestPlayer player, Integer count) { check(checkName, turnNum, step, player, CHECK_COMMAND_HAND_COUNT, count.toString()); } @@ -1116,7 +1122,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement //Assert.assertNotEquals("", cardName); Permanent found = getPermanent(cardName); if (subType != null) { - Assert.assertFalse("(Battlefield) card sub-type equal (" + cardName + ':' + subType.getDescription() + ')', found.getSubtype(currentGame).contains(subType)); + Assert.assertFalse("(Battlefield) card sub-type equal (" + cardName + ':' + subType.getDescription() + ')', found.hasSubtype(subType, currentGame)); } } @@ -1130,7 +1136,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement //Assert.assertNotEquals("", cardName); Permanent found = getPermanent(cardName); if (subType != null) { - Assert.assertTrue("(Battlefield) card sub-type equal (" + cardName + ':' + subType.getDescription() + ')', found.getSubtype(currentGame).contains(subType)); + Assert.assertTrue("(Battlefield) card sub-type equal (" + cardName + ':' + subType.getDescription() + ')', found.hasSubtype(subType, currentGame)); } } 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 10fa4fcca9b..2b448af36ce 100644 --- a/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonCard.java +++ b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonCard.java @@ -39,6 +39,7 @@ public final class MtgJsonCard { if ("transform".equals(layout) || "flip".equals(layout) || "adventure".equals(layout) + || "modal_dfc".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/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index bfa2123cbbb..6047be8cfdf 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -4,6 +4,7 @@ import com.google.common.base.CharMatcher; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.ChangelingAbility; import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.MultikickerAbility; import mage.cards.*; @@ -108,9 +109,6 @@ public class VerifyCardDataTest { // subtype skipListCreate(SKIP_LIST_SUBTYPE); skipListAddName(SKIP_LIST_SUBTYPE, "UGL", "Miss Demeanor"); - skipListAddName(SKIP_LIST_SUBTYPE, "ZNR", "Veteran Adventurer"); // TODO: additional types must be added by effect, not direct? - skipListAddName(SKIP_LIST_SUBTYPE, "ZNR", "Stonework Packbeast"); // TODO: additional types must be added by effect, not direct? - skipListAddName(SKIP_LIST_SUBTYPE, "ZNR", "Tajuru Paragon"); // TODO: additional types must be added by effect, not direct? // number skipListCreate(SKIP_LIST_NUMBER); @@ -259,9 +257,12 @@ public class VerifyCardDataTest { int cardIndex = 0; for (Card card : CardScanner.getAllCards()) { cardIndex++; - if (card.isSplitCard()) { + if (card instanceof SplitCard) { check(((SplitCard) card).getLeftHalfCard(), cardIndex); check(((SplitCard) card).getRightHalfCard(), cardIndex); + } else if (card instanceof ModalDoubleFacesCard) { + check(((ModalDoubleFacesCard) card).getLeftHalfCard(), cardIndex); + check(((ModalDoubleFacesCard) card).getRightHalfCard(), cardIndex); } else { check(card, cardIndex); } @@ -1252,6 +1253,10 @@ public class VerifyCardDataTest { fail(card, "abilities", "card have Multikicker ability, but missing it in rules text"); } + if (card.getAbilities().contains(ChangelingAbility.getInstance()) && !card.isAllCreatureTypes()) { + fail(card, "abilities", "card has Changeling but doesn't have isAllCreatureTypes set to true"); + } + // special check: missing or wrong ability/effect hints Map hints = new HashMap<>(); hints.put(MenaceAbility.class, "can't be blocked except by two or more"); @@ -1349,7 +1354,7 @@ public class VerifyCardDataTest { Card card = CardImpl.createCard(cardInfo.getClassName(), testSet); System.out.println(); System.out.println(card.getName() + " " + card.getManaCost().getText()); - if (card instanceof SplitCard) { + if (card instanceof SplitCard || card instanceof ModalDoubleFacesCard) { card.getAbilities().getRules(card.getName()).forEach(this::printAbilityText); } else { card.getRules().forEach(this::printAbilityText); diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java index fd55bb38930..59c99106abe 100644 --- a/Mage/src/main/java/mage/MageObject.java +++ b/Mage/src/main/java/mage/MageObject.java @@ -1,25 +1,25 @@ package mage; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.keyword.ChangelingAbility; import mage.abilities.text.TextPart; import mage.cards.Card; import mage.cards.FrameStyle; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.SubTypeSet; import mage.constants.SuperType; import mage.game.Game; import mage.game.events.ZoneChangeEvent; import mage.util.SubTypeList; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + public interface MageObject extends MageItem, Serializable { String getName(); @@ -126,6 +126,10 @@ public interface MageObject extends MageItem, Serializable { return getCardType().contains(CardType.PLANESWALKER); } + default boolean isTribal() { + return getCardType().contains(CardType.TRIBAL); + } + default boolean isPermanent() { return isCreature() || isArtifact() || isPlaneswalker() || isEnchantment() || isLand(); } @@ -139,6 +143,9 @@ public interface MageObject extends MageItem, Serializable { } default void addSuperType(SuperType superType) { + if (getSuperType().contains(superType)) { + return; + } getSuperType().add(superType); } @@ -151,9 +158,31 @@ public interface MageObject extends MageItem, Serializable { } default void addCardType(CardType cardType) { + if (getCardType().contains(cardType)) { + return; + } getCardType().add(cardType); } + default void addSubType(Game game, SubType... subTypes) { + for (SubType subType : subTypes) { + if (subType.canGain(this) + && !hasSubtype(subType, game)) { + getSubtype(game).add(subType); + } + } + } + + default void removeAllSubTypes(Game game) { + getSubtype(game).clear(); + setIsAllCreatureTypes(false); + } + + default void removeAllCreatureTypes(Game game) { + getSubtype(game).removeAll(SubType.getCreatureTypes()); + setIsAllCreatureTypes(false); + } + /** * Checks whether two cards share card types. * @@ -180,27 +209,36 @@ public interface MageObject extends MageItem, Serializable { return false; } - default boolean shareSubtypes(Card otherCard, Game game) { - - if (otherCard == null) { - throw new IllegalArgumentException("Params can't be null"); + default boolean shareCreatureTypes(Card otherCard, Game game) { + if (!isCreature() && !isTribal()) { + return false; } - - if (this.isCreature() && otherCard.isCreature()) { - if (this.hasAbility(ChangelingAbility.getInstance(), game) - || this.isAllCreatureTypes() - || otherCard.hasAbility(ChangelingAbility.getInstance(), game) - || otherCard.isAllCreatureTypes()) { - return true; - } + if (!otherCard.isCreature() && !otherCard.isTribal()) { + return false; } - for (SubType subtype : this.getSubtype(game)) { - if (otherCard.hasSubtype(subtype, game)) { - return true; - } + boolean isAllA = this.isAllCreatureTypes(); + boolean isAnyA = isAllA || this.getSubtype(game) + .stream() + .map(SubType::getSubTypeSet) + .anyMatch(SubTypeSet.CreatureType::equals); + boolean isAllB = otherCard.isAllCreatureTypes(); + boolean isAnyB = isAllB || otherCard + .getSubtype(game) + .stream() + .map(SubType::getSubTypeSet) + .anyMatch(SubTypeSet.CreatureType::equals); + if (!isAnyA || !isAnyB) { + return false; } - - return false; + if (isAllA) { + return isAllB || isAnyB; + } + return isAnyA + && (isAllB || this + .getSubtype(game) + .stream() + .filter(subType -> subType.getSubTypeSet() == SubTypeSet.CreatureType) + .anyMatch(subType -> otherCard.hasSubtype(subType, game))); } boolean isAllCreatureTypes(); diff --git a/Mage/src/main/java/mage/MageObjectImpl.java b/Mage/src/main/java/mage/MageObjectImpl.java index aa3792e486a..9c737da8947 100644 --- a/Mage/src/main/java/mage/MageObjectImpl.java +++ b/Mage/src/main/java/mage/MageObjectImpl.java @@ -1,7 +1,5 @@ package mage; -import java.util.*; - import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; @@ -11,7 +9,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.keyword.ChangelingAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.abilities.text.TextPart; import mage.abilities.text.TextPartSubType; @@ -20,10 +17,11 @@ import mage.cards.mock.MockCard; import mage.constants.*; import mage.game.Game; import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; import mage.util.GameLog; import mage.util.SubTypeList; +import java.util.*; + public abstract class MageObjectImpl implements MageObject { protected UUID objectId; @@ -230,25 +228,10 @@ public abstract class MageObjectImpl implements MageObject { if (value == null) { return false; } - SubTypeList subtypes = this.getSubtype(game); - if (subtypes.contains(value)) { + if (value.getSubTypeSet() == SubTypeSet.CreatureType && isAllCreatureTypes()) { return true; - } else { - // checking for Changeling - // first make sure input parameter is a creature subtype - // if not, then ChangelingAbility doesn't matter - if (value.getSubTypeSet() != SubTypeSet.CreatureType) { - return false; - } - // as it is a creature subtype, then check the existence of Changeling - Abilities checkList; - if (this instanceof Permanent) { - checkList = ((Permanent) this).getAbilities(game); - } else { - checkList = abilities; - } - return checkList.contains(ChangelingAbility.getInstance()) || isAllCreatureTypes(); } + return getSubtype(game).contains(value); } @Override @@ -289,7 +272,7 @@ public abstract class MageObjectImpl implements MageObject { @Override public void setIsAllCreatureTypes(boolean value) { - isAllCreatureTypes = value; + isAllCreatureTypes = value && (this.isTribal() || this.isCreature()); } @Override diff --git a/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java b/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java index 6c28ca572f9..c1c7aa687be 100644 --- a/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java +++ b/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java @@ -96,7 +96,7 @@ class KinshipBaseEffect extends OneShotEffect { if (card != null) { Cards cards = new CardsImpl(card); controller.lookAtCards(sourcePermanent.getName(), cards, game); - if (sourcePermanent.shareSubtypes(card, game)) { + if (sourcePermanent.shareCreatureTypes(card, game)) { 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) { diff --git a/Mage/src/main/java/mage/abilities/common/AttachedToCreatureSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttachedToCreatureSourceTriggeredAbility.java index 1387a45d9cc..b6d216230be 100644 --- a/Mage/src/main/java/mage/abilities/common/AttachedToCreatureSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttachedToCreatureSourceTriggeredAbility.java @@ -7,10 +7,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import static mage.constants.CardType.CREATURE; - /** - * * @author htrajan */ public class AttachedToCreatureSourceTriggeredAbility extends TriggeredAbilityImpl { @@ -33,7 +30,7 @@ public class AttachedToCreatureSourceTriggeredAbility extends TriggeredAbilityIm @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent attachedPermanent = game.getPermanent(event.getTargetId()); - return attachedPermanent != null && attachedPermanent.getCardType().contains(CREATURE); + return attachedPermanent != null && attachedPermanent.isCreature(); } @Override diff --git a/Mage/src/main/java/mage/abilities/common/LicidAbility.java b/Mage/src/main/java/mage/abilities/common/LicidAbility.java index e5aae7688fe..96fd32d0973 100644 --- a/Mage/src/main/java/mage/abilities/common/LicidAbility.java +++ b/Mage/src/main/java/mage/abilities/common/LicidAbility.java @@ -1,8 +1,5 @@ package mage.abilities.common; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.SpecialAction; @@ -15,22 +12,18 @@ import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.CreateSpecialActionEffect; import mage.abilities.effects.common.RemoveSpecialActionEffect; import mage.abilities.keyword.EnchantAbility; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** - * * @author emerald000 */ public class LicidAbility extends ActivatedAbilityImpl { @@ -116,8 +109,8 @@ class LicidContinuousEffect extends ContinuousEffectImpl { case TypeChangingEffects_4: licid.getCardType().clear(); licid.addCardType(CardType.ENCHANTMENT); - licid.getSubtype(game).clear(); - licid.getSubtype(game).add(SubType.AURA); + licid.removeAllSubTypes(game); + licid.addSubType(game, SubType.AURA); break; case AbilityAddingRemovingEffects_6: List toRemove = new ArrayList<>(); diff --git a/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java index 4ea2bde5c07..c4b34b53d56 100644 --- a/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -11,7 +10,6 @@ import mage.game.stack.Spell; import mage.target.targetpointer.FixedTarget; /** - * * @author North */ public class SpellCastControllerTriggeredAbility extends TriggeredAbilityImpl { @@ -67,7 +65,7 @@ public class SpellCastControllerTriggeredAbility extends TriggeredAbilityImpl { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null && filter.match(spell, getSourceId(), getControllerId(), game)) { if (rememberSource) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(spell.getId())); + this.getEffects().get(0).setTargetPointer(new FixedTarget(spell.getId(), game)); } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java index 8397a6e7185..cb3dba2f6c9 100644 --- a/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java @@ -3,13 +3,13 @@ package mage.abilities.effects; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.cards.Card; +import mage.cards.ModalDoubleFacesCard; +import mage.cards.SplitCard; import mage.constants.*; import mage.game.Game; +import mage.players.Player; import java.util.UUID; -import mage.cards.SplitCard; -import mage.cards.SplitCardHalf; -import mage.players.Player; /** * @author BetaSteward_at_googlemail.com @@ -22,7 +22,7 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements public AsThoughEffectImpl(AsThoughEffectType type, Duration duration, Outcome outcome) { this(type, duration, outcome, false); } - + public AsThoughEffectImpl(AsThoughEffectType type, Duration duration, Outcome outcome, boolean consumable) { super(duration, outcome); this.type = type; @@ -74,12 +74,12 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements * Internal method to do the neccessary to allow the card from objectId to be cast or played (if it's a land) without paying any mana. * Additional costs (like sacrificing or discarding) have still to be payed. * Checks if the card is of the correct type or in the correct zone have to be done before. - * - * @param objectId sourceId of the card to play - * @param source source ability that allows this effect + * + * @param objectId sourceId of the card to play + * @param source source ability that allows this effect * @param affectedControllerId player allowed to play the card * @param game - * @return + * @return */ protected boolean allowCardToPlayWithoutMana(UUID objectId, Ability source, UUID affectedControllerId, Game game) { Player player = game.getPlayer(affectedControllerId); @@ -89,9 +89,14 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements } if (!card.isLand()) { if (card instanceof SplitCard) { - SplitCardHalf leftCard = ((SplitCard) card).getLeftHalfCard(); + Card leftCard = ((SplitCard) card).getLeftHalfCard(); player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts()); - SplitCardHalf rightCard = ((SplitCard) card).getRightHalfCard(); + Card rightCard = ((SplitCard) card).getRightHalfCard(); + player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCard.getSpellAbility().getCosts()); + } else if (card instanceof ModalDoubleFacesCard) { + Card leftCard = ((ModalDoubleFacesCard) card).getLeftHalfCard(); + player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts()); + Card rightCard = ((ModalDoubleFacesCard) card).getRightHalfCard(); player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCard.getSpellAbility().getCosts()); } else { player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index db27aea89ce..2f5ee5fa565 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -1,5 +1,6 @@ package mage.abilities.effects; +import mage.ApprovingObject; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.MageSingleton; @@ -7,6 +8,8 @@ import mage.abilities.StaticAbility; import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect; import mage.abilities.effects.common.continuous.CommanderReplacementEffect; import mage.cards.*; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.Predicate; @@ -29,9 +32,6 @@ import java.io.Serializable; import java.util.*; import java.util.Map.Entry; import java.util.stream.Collectors; -import mage.ApprovingObject; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; /** * @author BetaSteward_at_googlemail.com @@ -334,7 +334,7 @@ public class ContinuousEffects implements Serializable { * @param event * @param game * @return a list of all {@link ReplacementEffect} that apply to the current - * event + * event */ private Map> getApplicableReplacementEffects(GameEvent event, Game game) { Map> replaceEffects = new HashMap<>(); @@ -343,7 +343,7 @@ public class ContinuousEffects implements Serializable { } // boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT); //get all applicable transient Replacement effects - for (Iterator iterator = replacementEffects.iterator(); iterator.hasNext();) { + for (Iterator iterator = replacementEffects.iterator(); iterator.hasNext(); ) { ReplacementEffect effect = iterator.next(); if (!effect.checksEventType(event, game)) { continue; @@ -376,7 +376,7 @@ public class ContinuousEffects implements Serializable { } } - for (Iterator iterator = preventionEffects.iterator(); iterator.hasNext();) { + for (Iterator iterator = preventionEffects.iterator(); iterator.hasNext(); ) { PreventionEffect effect = iterator.next(); if (!effect.checksEventType(event, game)) { continue; @@ -507,7 +507,7 @@ public class ContinuousEffects implements Serializable { * @param controllerId * @param game * @return sourceId of the permitting effect if any exists otherwise returns - * null + * null */ public ApprovingObject asThough(UUID objectId, AsThoughEffectType type, Ability affectedAbility, UUID controllerId, Game game) { List asThoughEffectsList = getApplicableAsThoughEffects(type, game); @@ -515,6 +515,8 @@ public class ContinuousEffects implements Serializable { UUID idToCheck; if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof SplitCardHalf) { idToCheck = ((SplitCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId(); + } else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof ModalDoubleFacesCardHalf) { + idToCheck = ((ModalDoubleFacesCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId(); } else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof AdventureCardSpell && !type.needPlayCardAbility()) { // adventure spell uses alternative characteristics for spell/stack @@ -523,6 +525,8 @@ public class ContinuousEffects implements Serializable { Card card = game.getCard(objectId); if (card instanceof SplitCardHalf) { idToCheck = ((SplitCardHalf) card).getParentCard().getId(); + } else if (card instanceof ModalDoubleFacesCardHalf) { + idToCheck = ((ModalDoubleFacesCardHalf) card).getParentCard().getId(); } else if (card instanceof AdventureCardSpell && !type.needPlayCardAbility()) { // adventure spell uses alternative characteristics for spell/stack @@ -568,24 +572,24 @@ public class ContinuousEffects implements Serializable { } else if (possibleApprovingObjects.size() > 1) { // Select the ability that you use to permit the action Map keyChoices = new HashMap<>(); - for(ApprovingObject approvingObject :possibleApprovingObjects) { - MageObject mageObject = game.getObject(approvingObject.getApprovingAbility().getSourceId()); - keyChoices.put(approvingObject.getApprovingAbility().getId().toString(), + for (ApprovingObject approvingObject : possibleApprovingObjects) { + MageObject mageObject = game.getObject(approvingObject.getApprovingAbility().getSourceId()); + keyChoices.put(approvingObject.getApprovingAbility().getId().toString(), (approvingObject.getApprovingAbility().getRule(mageObject == null ? "" : mageObject.getName())) - + (mageObject == null ? "" : " (" + mageObject.getIdName() + ")")); + + (mageObject == null ? "" : " (" + mageObject.getIdName() + ")")); } Choice choicePermitting = new ChoiceImpl(true); choicePermitting.setMessage("Choose the permitting object"); choicePermitting.setKeyChoices(keyChoices); Player player = game.getPlayer(controllerId); player.choose(Outcome.Detriment, choicePermitting, game); - for(ApprovingObject approvingObject: possibleApprovingObjects) { + for (ApprovingObject approvingObject : possibleApprovingObjects) { if (approvingObject.getApprovingAbility().getId().toString().equals(choicePermitting.getChoiceKey())) { return approvingObject; } } } - + } return null; @@ -833,7 +837,7 @@ public class ContinuousEffects implements Serializable { do { Map> rEffects = getApplicableReplacementEffects(event, game); // Remove all consumed effects (ability dependant) - for (Iterator it1 = rEffects.keySet().iterator(); it1.hasNext();) { + for (Iterator it1 = rEffects.keySet().iterator(); it1.hasNext(); ) { ReplacementEffect entry = it1.next(); if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9. Set consumedAbilitiesIds = consumed.get(entry.getId()); @@ -1018,7 +1022,7 @@ public class ContinuousEffects implements Serializable { .entrySet() .stream() .filter(entry -> dependentTo.contains(entry.getKey().getId()) - && entry.getValue().contains(effect.getId())) + && entry.getValue().contains(effect.getId())) .forEach(entry -> { entry.getValue().remove(effect.getId()); dependentTo.remove(entry.getKey().getId()); @@ -1052,7 +1056,7 @@ public class ContinuousEffects implements Serializable { continue; } // check if waiting effects can be applied now - for (Iterator>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) { + for (Iterator>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) { Map.Entry> entry = iterator.next(); if (!appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself continue; 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 ae3f6c5e860..c857261bac2 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java @@ -103,10 +103,11 @@ public class CopyEffect extends ContinuousEffectImpl { for (CardType type : copyFromObject.getCardType()) { permanent.addCardType(type); } - permanent.getSubtype(game).clear(); - for (SubType type : copyFromObject.getSubtype(game)) { - permanent.getSubtype(game).add(type); - } + + permanent.removeAllSubTypes(game); + permanent.getSubtype(game).addAll(copyFromObject.getSubtype(game)); + permanent.setIsAllCreatureTypes(copyFromObject.isAllCreatureTypes()); + permanent.getSuperType().clear(); for (SuperType type : copyFromObject.getSuperType()) { permanent.addSuperType(type); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java index d5de78070d7..833f89e9992 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java @@ -31,10 +31,11 @@ public class CopyTokenEffect extends ContinuousEffectImpl { for (CardType type : token.getCardType()) { permanent.addCardType(type); } - permanent.getSubtype(game).clear(); + permanent.removeAllSubTypes(game); for (SubType type : token.getSubtype(game)) { - permanent.getSubtype(game).add(type); + permanent.addSubType(game, type); } + permanent.setIsAllCreatureTypes(token.isAllCreatureTypes()); permanent.getSuperType().clear(); for (SuperType type : token.getSuperType()) { permanent.addSuperType(type); 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 3b01e3e4be7..f6f1a2dd036 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -1,10 +1,5 @@ package mage.abilities.effects.common; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; @@ -25,6 +20,11 @@ import mage.util.CardUtil; import mage.util.functions.ApplyToPermanent; import mage.util.functions.EmptyApplyToPermanent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + /** * @author LevelX2 */ @@ -179,10 +179,10 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { token.getSuperType().remove(SuperType.LEGENDARY); } - if (startingLoyalty!=-1){ + if (startingLoyalty != -1) { token.setStartingLoyalty(startingLoyalty); } - if (additionalCardType != null && !token.getCardType().contains(additionalCardType)) { + if (additionalCardType != null) { token.addCardType(additionalCardType); } if (hasHaste) { @@ -199,12 +199,12 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { token.removePTCDA(); token.getToughness().modifyBaseValue(tokenToughness); } - if (additionalSubType != null && !token.hasSubtype(additionalSubType, game)) { - token.getSubtype(game).add(additionalSubType); + if (onlySubType != null) { + token.removeAllCreatureTypes(game); + token.addSubType(game, onlySubType); } - if (onlySubType != null && !token.hasSubtype(onlySubType, game)) { - token.getSubtype(game).clear(); - token.getSubtype(game).add(onlySubType); + if (additionalSubType != null && !token.hasSubtype(additionalSubType, game)) { + token.addSubType(game, additionalSubType); } if (color != null) { token.getColor(game).setColor(color); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java index e013d1b60e8..cd0835983d2 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java @@ -29,9 +29,7 @@ public class AddCardSubTypeTargetEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent target = game.getPermanent(targetPointer.getFirst(game, source)); if (target != null) { - if (!target.hasSubtype(addedSubType, game)) { - target.getSubtype(game).add(addedSubType); - } + target.addSubType(game, addedSubType); } else { if (duration == Duration.Custom) { discard(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubtypeAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubtypeAllEffect.java index 66bfc9bf0ae..a35c0a5af1c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubtypeAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubtypeAllEffect.java @@ -35,8 +35,8 @@ public class AddCardSubtypeAllEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (perm != null && !perm.hasSubtype(addedSubtype, game)) { - perm.getSubtype(game).add(addedSubtype); + if (perm != null) { + perm.addSubType(game, addedSubtype); } } return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubtypeAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubtypeAttachedEffect.java index a98ade7d822..18bf9b5d70f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubtypeAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubtypeAttachedEffect.java @@ -33,8 +33,8 @@ public class AddCardSubtypeAttachedEffect extends ContinuousEffectImpl { Permanent equipment = game.getPermanent(source.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { Permanent target = game.getPermanent(equipment.getAttachedTo()); - if (target != null && !target.hasSubtype(addedSubtype, game)) - target.getSubtype(game).add(addedSubtype); + if (target != null) + target.addSubType(game, addedSubtype); } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeAttachedEffect.java index a09b001d53f..edc0e7f5ef4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeAttachedEffect.java @@ -33,7 +33,7 @@ public class AddCardTypeAttachedEffect extends ContinuousEffectImpl { Permanent equipment = game.getPermanent(source.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { Permanent target = game.getPermanent(equipment.getAttachedTo()); - if (target != null && !target.getCardType().contains(addedCardType)) { + if (target != null) { target.addCardType(addedCardType); } } 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 00892f24aa6..08ce421fea9 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 @@ -1,8 +1,5 @@ - package mage.abilities.effects.common.continuous; -import java.util.ArrayList; -import java.util.Locale; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.Mode; @@ -11,6 +8,9 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.ArrayList; +import java.util.Locale; + /** * @author emerald000 */ @@ -46,9 +46,7 @@ public class AddCardTypeSourceEffect extends ContinuousEffectImpl { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null && affectedObjectList.contains(new MageObjectReference(permanent, game))) { for (CardType cardType : addedCardTypes) { - if (!permanent.getCardType().contains(cardType)) { - permanent.addCardType(cardType); - } + permanent.addCardType(cardType); } return true; } else if (this.getDuration() == Duration.Custom) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeTargetEffect.java index b8f09330394..f5fc9f611ae 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeTargetEffect.java @@ -1,20 +1,15 @@ - package mage.abilities.effects.common.continuous; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; + import java.util.ArrayList; import java.util.Locale; import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.CardType; -import mage.constants.DependencyType; -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 nantuko @@ -48,9 +43,7 @@ public class AddCardTypeTargetEffect extends ContinuousEffectImpl { Permanent target = game.getPermanent(targetId); if (target != null) { for (CardType cardType : addedCardTypes) { - if (!target.getCardType().contains(cardType)) { - target.addCardType(cardType); - } + target.addCardType(cardType); } result = true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddChosenSubtypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddChosenSubtypeEffect.java index cf8c158d7bb..1a6a44e2bed 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddChosenSubtypeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddChosenSubtypeEffect.java @@ -23,8 +23,8 @@ public class AddChosenSubtypeEffect extends ContinuousEffectImpl { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { SubType subType = ChooseCreatureTypeEffect.getChosenCreatureType(permanent.getId(), game); - if (subType != null && !permanent.hasSubtype(subType, game)) { - permanent.getSubtype(game).add(subType); + if (subType != null) { + permanent.addSubType(game, subType); } } return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAllBasicsControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAllBasicsControlledEffect.java index 592f3cc64c6..411c4045a6c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAllBasicsControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAllBasicsControlledEffect.java @@ -14,7 +14,7 @@ import mage.game.permanent.Permanent; public class BecomesAllBasicsControlledEffect extends ContinuousEffectImpl { public BecomesAllBasicsControlledEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); this.staticText = "Lands you control are every basic land type in addition to their other types"; dependencyTypes.add(DependencyType.BecomeMountain); dependencyTypes.add(DependencyType.BecomeForest); @@ -27,65 +27,29 @@ public class BecomesAllBasicsControlledEffect extends ContinuousEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public BecomesAllBasicsControlledEffect copy() { return new BecomesAllBasicsControlledEffect(this); } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents( StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, source.getControllerId(), game)) { - switch (layer) { - case TypeChangingEffects_4: - if (!permanent.hasSubtype(SubType.SWAMP, game)) { - permanent.getSubtype(game).add(SubType.SWAMP); - } - if (!permanent.hasSubtype(SubType.MOUNTAIN, game)) { - permanent.getSubtype(game).add(SubType.MOUNTAIN); - } - if (!permanent.hasSubtype(SubType.FOREST, game)) { - permanent.getSubtype(game).add(SubType.FOREST); - } - if (!permanent.hasSubtype(SubType.ISLAND, game)) { - permanent.getSubtype(game).add(SubType.ISLAND); - } - if (!permanent.hasSubtype(SubType.PLAINS, game)) { - permanent.getSubtype(game).add(SubType.PLAINS); - } - if (permanent.hasSubtype(SubType.SWAMP, game) - && !permanent.getAbilities().containsRule(new BlackManaAbility())) { - permanent.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - if (permanent.hasSubtype(SubType.MOUNTAIN, game) - && !permanent.getAbilities().containsRule(new RedManaAbility())) { - permanent.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (permanent.hasSubtype(SubType.FOREST, game) - && !permanent.getAbilities().containsRule(new GreenManaAbility())) { - permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (permanent.hasSubtype(SubType.ISLAND, game) - && !permanent.getAbilities().containsRule(new BlueManaAbility())) { - permanent.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (permanent.hasSubtype(SubType.PLAINS, game) - && !permanent.getAbilities().containsRule(new WhiteManaAbility())) { - permanent.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - break; - } + permanent.addSubType( + game, + SubType.PLAINS, + SubType.ISLAND, + SubType.SWAMP, + SubType.MOUNTAIN, + SubType.FOREST + ); + permanent.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + permanent.addAbility(new BlueManaAbility(), source.getSourceId(), game); + permanent.addAbility(new BlackManaAbility(), source.getSourceId(), game); + permanent.addAbility(new RedManaAbility(), source.getSourceId(), game); + permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); } return true; } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAuraSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAuraSourceEffect.java index af5fdfba2ab..49b8bd9021d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAuraSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAuraSourceEffect.java @@ -5,18 +5,12 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.EnchantAbility; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.SubType; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; /** - * * @author LevelX2 */ public class BecomesAuraSourceEffect extends ContinuousEffectImpl implements SourceEffect { @@ -58,9 +52,7 @@ public class BecomesAuraSourceEffect extends ContinuousEffectImpl implements Sou switch (layer) { case TypeChangingEffects_4: if (sublayer == SubLayer.NA) { - if (!permanent.hasSubtype(SubType.AURA, game)) { - permanent.getSubtype(game).add(SubType.AURA); - } + permanent.addSubType(game, SubType.AURA); } break; case AbilityAddingRemovingEffects_6: 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 54b223b0329..d7b789d6cae 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 @@ -16,7 +16,7 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { protected List landTypes = new ArrayList<>(); public BecomesBasicLandEnchantedEffect(SubType... landNames) { - super(Duration.WhileOnBattlefield, Outcome.Detriment); + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); landTypes.addAll(Arrays.asList(landNames)); this.staticText = setText(); } @@ -26,68 +26,55 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { this.landTypes.addAll(effect.landTypes); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public BecomesBasicLandEnchantedEffect copy() { return new BecomesBasicLandEnchantedEffect(this); } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - // lands intrictically have the mana ability associated with their type, so added here in layer 4 - permanent.getSubtype(game).removeAll(SubType.getLandTypes()); - permanent.getSubtype(game).addAll(landTypes); - permanent.removeAllAbilities(source.getSourceId(), game); - for (SubType landType : landTypes) { - switch (landType) { - case SWAMP: - if (permanent.hasSubtype(SubType.SWAMP, game)) { - permanent.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - break; - case MOUNTAIN: - if (permanent.hasSubtype(SubType.MOUNTAIN, game)) { - permanent.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - break; - case FOREST: - if (permanent.hasSubtype(SubType.FOREST, game)) { - permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - break; - case ISLAND: - if (permanent.hasSubtype(SubType.ISLAND, game)) { - permanent.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - break; - case PLAINS: - if (permanent.hasSubtype(SubType.PLAINS, game)) { - permanent.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - break; - } - } - break; - } - return true; + if (enchantment == null || enchantment.getAttachedTo() == null) { + return false; + } + Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); + if (permanent == null) { + return false; + } + // lands intrictically have the mana ability associated with their type, so added here in layer 4 + permanent.getSubtype(game).removeAll(SubType.getLandTypes()); + permanent.getSubtype(game).addAll(landTypes); + permanent.removeAllAbilities(source.getSourceId(), game); + for (SubType landType : landTypes) { + switch (landType) { + case PLAINS: + if (permanent.hasSubtype(SubType.PLAINS, game)) { + permanent.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + } + break; + case ISLAND: + if (permanent.hasSubtype(SubType.ISLAND, game)) { + permanent.addAbility(new BlueManaAbility(), source.getSourceId(), game); + } + break; + case SWAMP: + if (permanent.hasSubtype(SubType.SWAMP, game)) { + permanent.addAbility(new BlackManaAbility(), source.getSourceId(), game); + } + break; + case MOUNTAIN: + if (permanent.hasSubtype(SubType.MOUNTAIN, game)) { + permanent.addAbility(new RedManaAbility(), source.getSourceId(), game); + } + break; + case FOREST: + if (permanent.hasSubtype(SubType.FOREST, game)) { + permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); + } + break; } } - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; + return true; } private String setText() { 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 1dde326c6bd..dd30acc1ff9 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 @@ -123,7 +123,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { landTypesToAdd.clear(); for (SubType subtype : landTypes) { if (!land.hasSubtype(subtype, game)) { - land.getSubtype(game).add(subtype); + land.addSubType(game, subtype); landTypesToAdd.add(subtype); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBlackZombieAdditionEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBlackZombieAdditionEffect.java index 6e448439b10..17ddab7d19a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBlackZombieAdditionEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBlackZombieAdditionEffect.java @@ -60,14 +60,10 @@ public class BecomesBlackZombieAdditionEffect extends ContinuousEffectImpl { if (creature != null) { switch (layer) { case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (!creature.hasSubtype(SubType.ZOMBIE, game)) { - creature.getSubtype(game).add(SubType.ZOMBIE); - } - } + creature.addSubType(game, SubType.ZOMBIE); break; case ColorChangingEffects_5: - if (sublayer == SubLayer.NA && this.giveBlackColor) { + if (this.giveBlackColor) { creature.getColor(game).setBlack(true); } break; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java index 66d103472c7..ba392ef745c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java @@ -25,18 +25,18 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { private boolean loseTypes = false; protected boolean loseName = false; - public BecomesCreatureAllEffect(Token token, String theyAreStillType, - FilterPermanent filter, Duration duration, boolean loseColor) { + public BecomesCreatureAllEffect(Token token, String theyAreStillType, + FilterPermanent filter, Duration duration, boolean loseColor) { this(token, theyAreStillType, filter, duration, loseColor, false, false); } - public BecomesCreatureAllEffect(Token token, String theyAreStillType, - FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName) { + public BecomesCreatureAllEffect(Token token, String theyAreStillType, + FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName) { this(token, theyAreStillType, filter, duration, loseColor, loseName, false); } - public BecomesCreatureAllEffect(Token token, String theyAreStillType, - FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName, boolean loseTypes) { + public BecomesCreatureAllEffect(Token token, String theyAreStillType, + FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName, boolean loseTypes) { super(duration, Outcome.BecomeCreature); this.token = token; this.theyAreStillType = theyAreStillType; @@ -44,7 +44,7 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { this.loseColor = loseColor; this.loseName = loseName; this.loseTypes = loseTypes; - + this.dependencyTypes.add(DependencyType.BecomeCreature); } @@ -87,83 +87,67 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { } for (Permanent permanent : affectedPermanents) { - if (permanent != null) { - switch (layer) { - case TextChangingEffects_3: - if (sublayer == SubLayer.NA) { - if (loseName) { - permanent.setName(token.getName()); - } + if (permanent == null) { + continue; + } + switch (layer) { + case TextChangingEffects_3: + if (loseName) { + permanent.setName(token.getName()); + } + break; + + case TypeChangingEffects_4: + for (CardType t : token.getCardType()) { + permanent.addCardType(t); + } + if (theyAreStillType != null || loseTypes) { + permanent.removeAllCreatureTypes(game); + } + for (SubType t : token.getSubtype(game)) { + permanent.addSubType(game, t); + } + permanent.setIsAllCreatureTypes(token.isAllCreatureTypes()); + + for (SuperType t : token.getSuperType()) { + if (!permanent.getSuperType().contains(t)) { + permanent.addSuperType(t); } - break; + } - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (theyAreStillType != null) { - permanent.getSubtype(game).retainAll(SubType.getLandTypes()); - permanent.getSubtype(game).addAll(token.getSubtype(game)); - } else { - if (loseTypes) { - permanent.getSubtype(game).retainAll(SubType.getLandTypes()); - } + break; - for (SubType t : token.getSubtype(game)) { - if (!permanent.hasSubtype(t, game)) { - permanent.getSubtype(game).add(t); - } - } - } + case ColorChangingEffects_5: + if (this.loseColor) { + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setRed(false); + permanent.getColor(game).setGreen(false); + } + if (token.getColor(game).hasColor()) { + permanent.getColor(game).addColor(token.getColor(game)); + } + break; - for (SuperType t : token.getSuperType()) { - if (!permanent.getSuperType().contains(t)) { - permanent.addSuperType(t); - } - } - - for (CardType t : token.getCardType()) { - if (!permanent.getCardType().contains(t)) { - permanent.addCardType(t); - } - } + case AbilityAddingRemovingEffects_6: + if (!token.getAbilities().isEmpty()) { + for (Ability ability : token.getAbilities()) { + permanent.addAbility(ability, source.getSourceId(), game); } - break; + } + break; - case ColorChangingEffects_5: - if (sublayer == SubLayer.NA) { - if (this.loseColor) { - permanent.getColor(game).setBlack(false); - permanent.getColor(game).setGreen(false); - permanent.getColor(game).setBlue(false); - permanent.getColor(game).setWhite(false); - permanent.getColor(game).setRed(false); - } - if (token.getColor(game).hasColor()) { - permanent.getColor(game).addColor(token.getColor(game)); - } + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + int power = token.getPower().getValue(); + int toughness = token.getToughness().getValue(); + if (power != 0 && toughness != 0) { + permanent.getPower().setValue(power); + permanent.getToughness().setValue(toughness); } - break; - - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - if (!token.getAbilities().isEmpty()) { - for (Ability ability : token.getAbilities()) { - permanent.addAbility(ability, source.getSourceId(), game); - } - } - } - break; - - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - int power = token.getPower().getValue(); - int toughness = token.getToughness().getValue(); - if (power != 0 && toughness != 0) { - permanent.getPower().setValue(power); - permanent.getToughness().setValue(toughness); - } - } - break; - } + } + break; } } return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java index cb9ec1db5b0..5369d5465a3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java @@ -78,19 +78,14 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl { switch (loseType) { case ALL: case ALL_BUT_COLOR: + permanent.removeAllSubTypes(game); + break; case ABILITIES_SUBTYPE: - if (permanent.isLand()) { - permanent.getSubtype(game).retainAll(SubType.getLandTypes()); - } else { - permanent.getSubtype(game).clear(); - } - permanent.setIsAllCreatureTypes(false); + permanent.removeAllCreatureTypes(game); break; } for (SubType t : token.getSubtype(game)) { - if (!permanent.hasSubtype(t, game)) { - permanent.getSubtype(game).add(t); - } + permanent.addSubType(game, t); } } break; @@ -98,11 +93,11 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl { case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { if (loseType == LoseType.ALL || loseType == LoseType.COLOR) { - permanent.getColor(game).setBlack(false); - permanent.getColor(game).setGreen(false); - permanent.getColor(game).setBlue(false); permanent.getColor(game).setWhite(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setBlack(false); permanent.getColor(game).setRed(false); + permanent.getColor(game).setGreen(false); } if (token.getColor(game).hasColor()) { permanent.getColor(game).addColor(token.getColor(game)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect.java index e7727fe2b65..af09a5bbfb2 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect.java @@ -1,42 +1,33 @@ package mage.abilities.effects.common.continuous; -import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.*; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Duration; +import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.Token; +import mage.target.targetpointer.FixedTarget; /** * @author jeffwadsworth */ -public class BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect extends ContinuousEffectImpl { +public class BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect extends OneShotEffect { - public enum LoseType { - NONE, ALL, ALL_BUT_COLOR, ABILITIES, ABILITIES_SUBTYPE_AND_PT - } - - protected Token token; - protected String type; - protected LoseType loseType; // what attributes are lost + private final Token token; + private final Duration duration; public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(Token token, String text, Duration duration) { - this(token, text, duration, LoseType.NONE); - } - - public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(Token token, String text, Duration duration, LoseType loseType) { - super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature); - this.token = token; - this.loseType = loseType; + super(Outcome.BecomeCreature); staticText = text; + this.token = token; + this.duration = duration; } public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(final BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect effect) { super(effect); this.token = effect.token.copy(); - this.type = effect.type; - this.loseType = effect.loseType; + this.duration = effect.duration; } @Override @@ -44,122 +35,19 @@ public class BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect extends Co return new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(this); } - @Override - public void init(Ability source, Game game) { - super.init(source, game); - Permanent attachedPermanent = game.getPermanent(source.getSourceId()); - if (attachedPermanent != null) { - Permanent permanentAttachedTo = game.getPermanent(attachedPermanent.getAttachedTo()); - if (permanentAttachedTo != null) { - affectedObjectList.add(new MageObjectReference(permanentAttachedTo, game)); - } - } - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - boolean attachedExists = false; - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null) { - for (MageObjectReference mageObjectReference : affectedObjectList) { - Permanent permanentAttachedTo = mageObjectReference.getPermanent(game); - if (permanentAttachedTo != null) { - attachedExists = true; - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - for (SuperType superType : token.getSuperType()) { - permanentAttachedTo.addSuperType(superType); - - } - // card type - switch (loseType) { - case ALL: - case ALL_BUT_COLOR: - permanentAttachedTo.getCardType().clear(); - break; - } - for (CardType cardType : token.getCardType()) { - permanentAttachedTo.addCardType(cardType); - } - - // sub type - switch (loseType) { - case ALL: - case ALL_BUT_COLOR: - case ABILITIES_SUBTYPE_AND_PT: - permanentAttachedTo.getSubtype(game).retainAll(SubType.getLandTypes()); - break; - } - for (SubType subType : token.getSubtype(game)) { - if (!permanentAttachedTo.hasSubtype(subType, game)) { - permanentAttachedTo.getSubtype(game).add(subType); - } - } - - } - break; - - case ColorChangingEffects_5: - if (sublayer == SubLayer.NA) { - if (loseType == LoseType.ALL) { - permanentAttachedTo.getColor(game).setBlack(false); - permanentAttachedTo.getColor(game).setGreen(false); - permanentAttachedTo.getColor(game).setBlue(false); - permanentAttachedTo.getColor(game).setWhite(false); - permanentAttachedTo.getColor(game).setRed(false); - } - if (token.getColor(game).hasColor()) { - permanentAttachedTo.getColor(game).setColor(token.getColor(game)); - } - } - break; - - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - switch (loseType) { - case ALL: - case ALL_BUT_COLOR: - case ABILITIES: - case ABILITIES_SUBTYPE_AND_PT: - permanentAttachedTo.removeAllAbilities(source.getSourceId(), game); - break; - } - for (Ability ability : token.getAbilities()) { - permanentAttachedTo.addAbility(ability, source.getSourceId(), game); - } - - } - break; - - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - permanentAttachedTo.getPower().setValue(token.getPower().getValue()); - permanentAttachedTo.getToughness().setValue(token.getToughness().getValue()); - } - break; - } - } - if (!attachedExists) { - discard(); - } - return true; - } - } - return false; - } - @Override public boolean apply(Game game, Ability source) { - return false; + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (permanent == null) { + return false; + } + Permanent enchanted = game.getPermanent(permanent.getAttachedTo()); + if (enchanted == null) { + return false; + } + game.addEffect(new BecomesCreatureTargetEffect( + token, false, true, duration + ).setTargetPointer(new FixedTarget(enchanted, game)), source); + return true; } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.PTChangingEffects_7 - || layer == Layer.AbilityAddingRemovingEffects_6 - || layer == Layer.ColorChangingEffects_5 - || layer == Layer.TypeChangingEffects_4; - } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java index 882d828e6a5..ab88478ab1c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java @@ -82,78 +82,66 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements } else { permanent = game.getPermanent(source.getSourceId()); } - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (losePreviousTypes) { - permanent.getCardType().clear(); - } - for (CardType cardType : token.getCardType()) { - if (permanent.getCardType().contains(cardType)) { - continue; - } - permanent.addCardType(cardType); - } - - if (theyAreStillType != null && theyAreStillType.isEmpty() || theyAreStillType == null && permanent.isLand()) { - permanent.getSubtype(game).retainAll(SubType.getLandTypes()); - } - if (!token.getSubtype(game).isEmpty()) { - for (SubType subType : token.getSubtype(game)) { - if (permanent.hasSubtype(subType, game)) { - continue; - } - permanent.getSubtype(game).add(subType); - } - } - permanent.setIsAllCreatureTypes(token.isAllCreatureTypes()); - } - break; - - case ColorChangingEffects_5: - if (sublayer == SubLayer.NA) { - if (token.getColor(game).hasColor()) { - permanent.getColor(game).setColor(token.getColor(game)); - } - } - break; - - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - if (loseAbilities) { - permanent.removeAllAbilities(source.getSourceId(), game); - } - for (Ability ability : token.getAbilities()) { - permanent.addAbility(ability, source.getSourceId(), game); - } - } - break; - - case PTChangingEffects_7: - if ((sublayer == SubLayer.CharacteristicDefining_7a && isCharacterDefining()) - || (sublayer == SubLayer.SetPT_7b && !isCharacterDefining())) { - if (power != null) { - permanent.getPower().setValue(power.calculate(game, source, this)); // check all other becomes to use calculate? - } else if (token.getPower() != null) { - permanent.getPower().setValue(token.getPower().getValue()); - } - if (toughness != null) { - permanent.getToughness().setValue(toughness.calculate(game, source, this)); - } else if (token.getToughness() != null) { - permanent.getToughness().setValue(token.getToughness().getValue()); - } - } - break; + if (permanent == null) { + if (duration == Duration.Custom) { + this.discard(); } + return false; + } + switch (layer) { + case TypeChangingEffects_4: + if (losePreviousTypes) { + permanent.getCardType().clear(); + } + for (CardType cardType : token.getCardType()) { + permanent.addCardType(cardType); + } - return true; + if (theyAreStillType != null && theyAreStillType.isEmpty() + || theyAreStillType == null && permanent.isLand()) { + permanent.removeAllCreatureTypes(game); + } + if (!token.getSubtype(game).isEmpty()) { + for (SubType subType : token.getSubtype(game)) { + permanent.addSubType(game, subType); + } + } + permanent.setIsAllCreatureTypes(token.isAllCreatureTypes()); + break; - } else if (duration == Duration.Custom) { - this.discard(); + case ColorChangingEffects_5: + if (token.getColor(game).hasColor()) { + permanent.getColor(game).setColor(token.getColor(game)); + } + break; + + case AbilityAddingRemovingEffects_6: + if (loseAbilities) { + permanent.removeAllAbilities(source.getSourceId(), game); + } + for (Ability ability : token.getAbilities()) { + permanent.addAbility(ability, source.getSourceId(), game); + } + break; + + case PTChangingEffects_7: + if ((sublayer == SubLayer.CharacteristicDefining_7a && isCharacterDefining()) + || (sublayer == SubLayer.SetPT_7b && !isCharacterDefining())) { + if (power != null) { + permanent.getPower().setValue(power.calculate(game, source, this)); // check all other becomes to use calculate? + } else if (token.getPower() != null) { + permanent.getPower().setValue(token.getPower().getValue()); + } + if (toughness != null) { + permanent.getToughness().setValue(toughness.calculate(game, source, this)); + } else if (token.getToughness() != null) { + permanent.getToughness().setValue(token.getToughness().getValue()); + } + } + break; } - return false; + return true; } @Override @@ -171,7 +159,10 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements @Override public boolean hasLayer(Layer layer) { - return layer == Layer.PTChangingEffects_7 || layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4; + return layer == Layer.PTChangingEffects_7 + || layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.ColorChangingEffects_5 + || layer == Layer.TypeChangingEffects_4; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java index dfcabb361b7..630d3013015 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java @@ -70,82 +70,68 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { boolean result = false; for (UUID permanentId : targetPointer.getTargets(game, source)) { Permanent permanent = game.getPermanent(permanentId); - if (permanent != null) { - switch (layer) { - case TextChangingEffects_3: - if (sublayer == SubLayer.NA) { - if (loseName) { - permanent.setName(token.getName()); - } - } - break; - - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (loseAllAbilities) { - permanent.getSubtype(game).retainAll(SubType.getLandTypes()); - permanent.getCardType().clear(); // remove all CardTypes - permanent.getSubtype(game).addAll(token.getSubtype(game)); - } else { - if (removeSubtypes) { - permanent.getSubtype(game).clear(); - } - for (SubType t : token.getSubtype(game)) { - if (!permanent.hasSubtype(t, game)) { - permanent.getSubtype(game).add(t); - } - } - } - - for (SuperType t : token.getSuperType()) { - if (!permanent.getSuperType().contains(t)) { - permanent.addSuperType(t); - } - } - - for (CardType t : token.getCardType()) { - if (!permanent.getCardType().contains(t)) { - permanent.addCardType(t); - } - } - } - break; - - case ColorChangingEffects_5: - if (sublayer == SubLayer.NA) { - if (loseAllAbilities) { - permanent.getColor(game).setBlack(false); - permanent.getColor(game).setGreen(false); - permanent.getColor(game).setBlue(false); - permanent.getColor(game).setWhite(false); - permanent.getColor(game).setBlack(false); - } - if (token.getColor(game).hasColor()) { - permanent.getColor(game).setColor(token.getColor(game)); - } - } - break; - - case AbilityAddingRemovingEffects_6: - if (loseAllAbilities && !keepAbilities) { - permanent.removeAllAbilities(source.getSourceId(), game); - } - if (sublayer == SubLayer.NA) { - if (!token.getAbilities().isEmpty()) { - for (Ability ability : token.getAbilities()) { - permanent.addAbility(ability, source.getSourceId(), game); - } - } - } - break; - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { // CDA can only define a characteristic of either the card or token it comes from. - permanent.getToughness().setValue(token.getToughness().getValue()); - permanent.getPower().setValue(token.getPower().getValue()); - } - } - result = true; + if (permanent == null) { + continue; } + switch (layer) { + case TextChangingEffects_3: + if (loseName) { + permanent.setName(token.getName()); + } + break; + + case TypeChangingEffects_4: + for (CardType t : token.getCardType()) { + permanent.addCardType(t); + } + if (loseAllAbilities || removeSubtypes) { + permanent.removeAllCreatureTypes(game); + } + for (SubType t : token.getSubtype(game)) { + permanent.addSubType(game, t); + } + + for (SuperType t : token.getSuperType()) { + if (!permanent.getSuperType().contains(t)) { + permanent.addSuperType(t); + } + } + + break; + + case ColorChangingEffects_5: + if (loseAllAbilities) { + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setRed(false); + permanent.getColor(game).setGreen(false); + } + if (token.getColor(game).hasColor()) { + permanent.getColor(game).setColor(token.getColor(game)); + } + break; + + case AbilityAddingRemovingEffects_6: + if (loseAllAbilities && !keepAbilities) { + permanent.removeAllAbilities(source.getSourceId(), game); + } + if (sublayer == SubLayer.NA) { + if (!token.getAbilities().isEmpty()) { + for (Ability ability : token.getAbilities()) { + permanent.addAbility(ability, source.getSourceId(), game); + } + } + } + break; + + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { // CDA can only define a characteristic of either the card or token it comes from. + permanent.getToughness().setValue(token.getToughness().getValue()); + permanent.getPower().setValue(token.getPower().getValue()); + } + } + result = true; } if (!result && this.duration == Duration.Custom) { this.discard(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTypeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTypeTargetEffect.java index 44ddd08cd9a..68a8b7deead 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTypeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTypeTargetEffect.java @@ -1,8 +1,3 @@ -/* - * 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.abilities.effects.common.continuous; import mage.abilities.Ability; @@ -19,15 +14,15 @@ import java.util.UUID; */ public class BecomesCreatureTypeTargetEffect extends ContinuousEffectImpl { - protected SubTypeList subtypes = new SubTypeList(); - protected boolean loseOther; // loses other creature types + private final SubTypeList subtypes = new SubTypeList(); + private final boolean loseOther; // loses other creature types public BecomesCreatureTypeTargetEffect(Duration duration, SubType subtype) { - this(duration, createArrayList(subtype)); + this(duration, new SubTypeList(subtype)); } public BecomesCreatureTypeTargetEffect(Duration duration, SubType subtype, boolean loseOther) { - this(duration, createArrayList(subtype), loseOther); + this(duration, new SubTypeList(subtype), loseOther); } public BecomesCreatureTypeTargetEffect(Duration duration, SubTypeList subtypes) { @@ -35,67 +30,45 @@ public class BecomesCreatureTypeTargetEffect extends ContinuousEffectImpl { } public BecomesCreatureTypeTargetEffect(Duration duration, SubTypeList subtypes, boolean loseOther) { - super(duration, Outcome.Detriment); - this.subtypes = subtypes; + super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); + this.subtypes.addAll(subtypes); this.staticText = setText(); this.loseOther = loseOther; } - private static SubTypeList createArrayList(SubType subtype) { - SubTypeList subtypes = new SubTypeList(); - subtypes.add(subtype); - return subtypes; - } - public BecomesCreatureTypeTargetEffect(final BecomesCreatureTypeTargetEffect effect) { super(effect); this.subtypes.addAll(effect.subtypes); this.loseOther = effect.loseOther; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public BecomesCreatureTypeTargetEffect copy() { return new BecomesCreatureTypeTargetEffect(this); } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { + boolean flag = false; for (UUID targetPermanent : targetPointer.getTargets(game, source)) { Permanent permanent = game.getPermanent(targetPermanent); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (loseOther) { - permanent.getSubtype(game).retainAll(SubType.getLandTypes()); - permanent.getSubtype(game).addAll(subtypes); - } else { - for (SubType subtype : subtypes) { - if (!permanent.hasSubtype(subtype, game)) { - permanent.getSubtype(game).add(subtype); - } - } - } - break; - } - } else { - if (duration == Duration.Custom) { - discard(); - } + if (permanent == null) { + continue; + } + flag = true; + if (loseOther) { + permanent.removeAllCreatureTypes(game); + } + for (SubType subtype : subtypes) { + permanent.addSubType(game, subtype); } } + if (!flag && duration == Duration.Custom) { + discard(); + } return true; } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } - private String setText() { StringBuilder sb = new StringBuilder(); sb.append("Target creature becomes that type"); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java index 064e11c4a04..35d94fe5e95 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java @@ -1,33 +1,21 @@ -/* - * 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.abilities.effects.common.continuous; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; /** - * * @author jeffwadsworth */ public class BecomesEnchantmentSourceEffect extends ContinuousEffectImpl implements SourceEffect { public BecomesEnchantmentSourceEffect() { - super(Duration.Custom, Outcome.AddAbility); + super(Duration.Custom, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.AddAbility); staticText = "{this} becomes an Enchantment"; dependencyTypes.add(DependencyType.EnchantmentAddingRemoving); - } public BecomesEnchantmentSourceEffect(final BecomesEnchantmentSourceEffect effect) { @@ -45,35 +33,17 @@ public class BecomesEnchantmentSourceEffect extends ContinuousEffectImpl impleme affectedObjectList.add(new MageObjectReference(source.getSourceId(), game)); } - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent permanent = affectedObjectList.get(0).getPermanent(game); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getCardType().clear(); - permanent.getSubtype(game).clear(); - if (!permanent.getCardType().contains(CardType.ENCHANTMENT)) { - permanent.getCardType().add(CardType.ENCHANTMENT); - } - } - break; - } - return true; - } - this.discard(); - return false; - } - @Override public boolean apply(Game game, Ability source) { - return false; + Permanent permanent = affectedObjectList.get(0).getPermanent(game); + if (permanent == null) { + this.discard(); + return false; + } + permanent.getCardType().clear(); + permanent.getCardType().add(CardType.ENCHANTMENT); + permanent.getSubtype(game).retainAll(SubType.getEnchantmentTypes()); + permanent.setIsAllCreatureTypes(false); + return true; } - - @Override - public boolean hasLayer(Layer layer) { - return Layer.TypeChangingEffects_4 == layer; - } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java index 202788360b0..8b01f0fbc1f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java @@ -75,7 +75,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple permanent.getSuperType().clear(); permanent.getCardType().clear(); permanent.addCardType(CardType.CREATURE); - permanent.getSubtype(game).clear(); + permanent.removeAllSubTypes(game); permanent.getManaCost().clear(); break; case ColorChangingEffects_5: diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java index 06e5f1c0120..3f5fd586d1d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java @@ -136,7 +136,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen permanent.getSuperType().clear(); permanent.getCardType().clear(); permanent.addCardType(CardType.CREATURE); - permanent.getSubtype(game).clear(); + permanent.removeAllSubTypes(game); break; case ColorChangingEffects_5: permanent.getColor(game).setColor(new ObjectColor()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesSubtypeAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesSubtypeAllEffect.java index 97f506cbf34..a11f9aeb370 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesSubtypeAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesSubtypeAllEffect.java @@ -8,6 +8,7 @@ package mage.abilities.effects.common.continuous; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -18,33 +19,26 @@ import mage.util.SubTypeList; */ public class BecomesSubtypeAllEffect extends ContinuousEffectImpl { - protected SubTypeList subtypes = new SubTypeList(); - protected boolean loseOther; // loses other subtypes - protected FilterCreaturePermanent filter; + private final SubTypeList subtypes = new SubTypeList(); + private final boolean loseOther; // loses other subtypes + private final FilterCreaturePermanent filter; public BecomesSubtypeAllEffect(Duration duration, SubType subtype) { - this(duration, createArrayList(subtype)); + this(duration, new SubTypeList(subtype)); } public BecomesSubtypeAllEffect(Duration duration, SubTypeList subtypes) { - this(duration, subtypes, new FilterCreaturePermanent("All creatures"), true); + this(duration, subtypes, StaticFilters.FILTER_PERMANENT_CREATURE, true); } - public BecomesSubtypeAllEffect(Duration duration, - SubTypeList subtypes, FilterCreaturePermanent filter, boolean loseOther) { - super(duration, Outcome.Detriment); - this.subtypes = subtypes; + public BecomesSubtypeAllEffect(Duration duration, SubTypeList subtypes, FilterCreaturePermanent filter, boolean loseOther) { + super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); + this.subtypes.addAll(subtypes); this.staticText = setText(); this.loseOther = loseOther; this.filter = filter; } - private static SubTypeList createArrayList(SubType subtype) { - SubTypeList subtypes = new SubTypeList(); - subtypes.add(subtype); - return subtypes; - } - public BecomesSubtypeAllEffect(final BecomesSubtypeAllEffect effect) { super(effect); this.subtypes.addAll(effect.subtypes); @@ -52,47 +46,32 @@ public class BecomesSubtypeAllEffect extends ContinuousEffectImpl { this.filter = effect.filter; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public BecomesSubtypeAllEffect copy() { return new BecomesSubtypeAllEffect(this); } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, - Game game) { + public boolean apply(Game game, Ability source) { + boolean flag = false; for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (loseOther) { - permanent.getSubtype(game).retainAll(SubType.getLandTypes()); - permanent.getSubtype(game).addAll(subtypes); - } else { - for (SubType subtype : subtypes) { - if (!permanent.hasSubtype(subtype, game)) { - permanent.getSubtype(game).add(subtype); - } - } - } - break; - } - } else if (duration == Duration.Custom) { - discard(); + if (permanent == null) { + continue; + } + flag = true; + if (loseOther) { + permanent.removeAllCreatureTypes(game); + } + for (SubType subtype : subtypes) { + permanent.addSubType(game, subtype); } } + if (!flag && duration == Duration.Custom) { + discard(); + } return true; } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } - private String setText() { StringBuilder sb = new StringBuilder(); sb.append("Target creature becomes that type"); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java index 1b2055f1831..03efc93852e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java @@ -9,6 +9,7 @@ import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.AdventureCardSpell; import mage.cards.Card; +import mage.cards.ModalDoubleFacesCardHalf; import mage.cards.SplitCardHalf; import mage.constants.*; import mage.filter.FilterCard; @@ -22,7 +23,7 @@ import java.util.UUID; public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImpl { private final AlternativeCostSourceAbility alternativeCastingCostAbility; - + public CastFromHandWithoutPayingManaCostEffect() { this(StaticFilters.FILTER_CARDS_NON_LAND, true); } @@ -38,7 +39,7 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp condition = new CompoundCondition(SourceIsSpellCondition.instance, IsBeingCastFromHandCondition.instance); } else { condition = SourceIsSpellCondition.instance; - } + } this.alternativeCastingCostAbility = new AlternativeCostSourceAbility(null, condition, null, filter, true); this.staticText = "You may cast " + filter.getMessage() + (fromHand ? " from your hand" : "") @@ -88,9 +89,9 @@ enum IsBeingCastFromHandCondition implements Condition { @Override public boolean apply(Game game, Ability source) { MageObject object = game.getObject(source.getSourceId()); - if (object instanceof SplitCardHalf || object instanceof AdventureCardSpell) { - UUID splitCardId = ((Card) object).getMainCard().getId(); - object = game.getObject(splitCardId); + if (object instanceof SplitCardHalf || object instanceof AdventureCardSpell || object instanceof ModalDoubleFacesCardHalf) { + UUID mainCardId = ((Card) object).getMainCard().getId(); + object = game.getObject(mainCardId); } if (object instanceof Spell) { // needed to check if it can be cast by alternate cost Spell spell = (Spell) object; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java index e2513cac569..01cd5cafab8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java @@ -40,10 +40,8 @@ public class CreaturesBecomeOtherTypeEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { if (layer == Layer.TypeChangingEffects_4) { - for (Permanent object: game.getBattlefield().getActivePermanents(this.filter, source.getControllerId(), game)) { - if (!object.hasSubtype(this.subType, game)) { - object.getSubtype(game).add(this.subType); - } + for (Permanent object : game.getBattlefield().getActivePermanents(this.filter, source.getControllerId(), game)) { + object.addSubType(game, this.subType); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAllCreatureTypesAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAllCreatureTypesAttachedEffect.java new file mode 100644 index 00000000000..a9473a5103f --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAllCreatureTypesAttachedEffect.java @@ -0,0 +1,44 @@ +package mage.abilities.effects.common.continuous; + +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 GainAllCreatureTypesAttachedEffect extends ContinuousEffectImpl { + + public GainAllCreatureTypesAttachedEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); + staticText = "and is every creature type"; + } + + public GainAllCreatureTypesAttachedEffect(final GainAllCreatureTypesAttachedEffect effect) { + super(effect); + } + + @Override + public GainAllCreatureTypesAttachedEffect copy() { + return new GainAllCreatureTypesAttachedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent equipment = game.getPermanent(source.getSourceId()); + if (equipment == null) { + return false; + } + Permanent permanent = game.getPermanent(equipment.getAttachedTo()); + if (permanent == null) { + return false; + } + permanent.setIsAllCreatureTypes(true); + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAllCreatureTypesTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAllCreatureTypesTargetEffect.java new file mode 100644 index 00000000000..da286872276 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAllCreatureTypesTargetEffect.java @@ -0,0 +1,53 @@ +package mage.abilities.effects.common.continuous; + +import mage.abilities.Ability; +import mage.abilities.Mode; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public class GainAllCreatureTypesTargetEffect extends ContinuousEffectImpl { + + public GainAllCreatureTypesTargetEffect(Duration duration) { + super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); + } + + public GainAllCreatureTypesTargetEffect(final GainAllCreatureTypesTargetEffect effect) { + super(effect); + } + + @Override + public GainAllCreatureTypesTargetEffect copy() { + return new GainAllCreatureTypesTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int affectedTargets = 0; + for (UUID permanentId : targetPointer.getTargets(game, source)) { + Permanent target = game.getPermanent(permanentId); + if (target != null) { + target.setIsAllCreatureTypes(true); + affectedTargets++; + } + } + return affectedTargets > 0; + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + return "target " + mode.getTargets().get(0).getTargetName() + " gains all creature types " + duration.toString(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/HasSubtypesSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/HasSubtypesSourceEffect.java new file mode 100644 index 00000000000..1b444035e24 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/HasSubtypesSourceEffect.java @@ -0,0 +1,71 @@ +package mage.abilities.effects.common.continuous; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.*; +import mage.game.Game; +import mage.util.SubTypeList; + +/** + * @author TheElk801 + */ +public class HasSubtypesSourceEffect extends ContinuousEffectImpl { + + private final SubTypeList subtypes = new SubTypeList(); + + public HasSubtypesSourceEffect(SubType... subTypes) { + super(Duration.EndOfGame, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); + subtypes.add(subTypes); + this.staticText = setText(); + } + + public HasSubtypesSourceEffect(final HasSubtypesSourceEffect effect) { + super(effect); + this.subtypes.addAll(effect.subtypes); + } + + @Override + public HasSubtypesSourceEffect copy() { + return new HasSubtypesSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject sourceObject = game.getObject(source.getSourceId()); + if (sourceObject == null) { + return false; + } + for (SubType subType : subtypes) { + sourceObject.addSubType(game, subType); + } + return true; + } + + private String setText() { + String s = "{this} is also "; + switch (subtypes.size()) { + case 0: + throw new UnsupportedOperationException("Can't have zero subtypes"); + case 1: + s += subtypes.get(0).getIndefiniteArticle() + " " + subtypes.get(0); + break; + case 2: + s += subtypes.get(0).getIndefiniteArticle() + " " + subtypes.get(0); + s += " and "; + s += subtypes.get(1).getIndefiniteArticle() + " " + subtypes.get(1); + break; + default: + for (int i = 0; i < subtypes.size(); i++) { + if (i == 0) { + s += subtypes.get(0).getIndefiniteArticle() + " " + subtypes.get(0) + ", "; + } else if (i == subtypes.size() - 1) { + s += "and " + subtypes.get(0); + } else { + s += subtypes.get(0) + ", "; + } + } + } + return s; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java index 7be4c3ef7b5..0424ad4fb95 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java @@ -3,7 +3,10 @@ package mage.abilities.effects.common.continuous; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.*; +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; @@ -29,8 +32,8 @@ public class LoseAllCreatureTypesTargetEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent != null) { - permanent.setIsAllCreatureTypes(false); - return permanent.getSubtype(game).retainAll(SubType.getLandTypes()); + permanent.removeAllCreatureTypes(game); + return true; } return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseCreatureTypeSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseCreatureTypeSourceEffect.java index 02405a9d044..27ce0e2b48f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseCreatureTypeSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseCreatureTypeSourceEffect.java @@ -24,7 +24,7 @@ public class LoseCreatureTypeSourceEffect extends ContinuousEffectImpl implement * @param lessThan */ public LoseCreatureTypeSourceEffect(DynamicValue dynamicValue, int lessThan) { - super(Duration.WhileOnBattlefield, Outcome.Detriment); + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); this.dynamicValue = dynamicValue; this.lessThan = lessThan; setText(); @@ -53,31 +53,22 @@ public class LoseCreatureTypeSourceEffect extends ContinuousEffectImpl implement } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { if (dynamicValue.calculate(game, source, this) >= lessThan) { return false; } Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getCardType().remove(CardType.CREATURE); - permanent.getSubtype(game).retainAll(SubType.getLandTypes()); - if (permanent.isAttacking() || permanent.getBlocking() > 0) { - permanent.removeFromCombat(game); - } - } - break; - } - return true; + if (permanent == null) { + return false; } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; + permanent.getCardType().remove(CardType.CREATURE); + if (!permanent.isTribal()) { + permanent.removeAllCreatureTypes(game); + } + if (permanent.isAttacking() || permanent.getBlocking() > 0) { + permanent.removeFromCombat(game); + } + return true; } private void setText() { @@ -86,10 +77,4 @@ public class LoseCreatureTypeSourceEffect extends ContinuousEffectImpl implement sb.append(CardUtil.numberToText(lessThan)).append(", {this} isn't a creature"); staticText = sb.toString(); } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetCardSubtypeAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetCardSubtypeAttachedEffect.java index 05da896fff3..fe82e4e7b02 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetCardSubtypeAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetCardSubtypeAttachedEffect.java @@ -22,20 +22,6 @@ public class SetCardSubtypeAttachedEffect extends ContinuousEffectImpl { this.setText(); } - /*public SetCardSubtypeAttachedEffect(SubType setSubtype, Duration duration, AttachmentType attachmentType) { - super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); - this.setSubtypes.add(setSubtype); - this.attachmentType = attachmentType; - setText(); - } - - public SetCardSubtypeAttachedEffect(List setSubtypes, Duration duration, AttachmentType attachmentType) { - super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); - this.setSubtypes.addAll(setSubtypes); - this.attachmentType = attachmentType; - setText(); - }*/ - public SetCardSubtypeAttachedEffect(final SetCardSubtypeAttachedEffect effect) { super(effect); this.setSubtypes = effect.setSubtypes; @@ -45,13 +31,15 @@ public class SetCardSubtypeAttachedEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { Permanent equipment = game.getPermanent(source.getSourceId()); - if (equipment != null && equipment.getAttachedTo() != null) { - Permanent target = game.getPermanent(equipment.getAttachedTo()); - if (target != null) { - target.getSubtype(game).retainAll(SubType.getLandTypes()); - target.getSubtype(game).addAll(setSubtypes); - } + if (equipment == null || equipment.getAttachedTo() == null) { + return true; } + Permanent target = game.getPermanent(equipment.getAttachedTo()); + if (target == null) { + return true; + } + target.removeAllCreatureTypes(game); + target.getSubtype(game).addAll(setSubtypes); return true; } 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 d2d29146826..84a0bc07514 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 @@ -9,9 +9,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; /** - * * IMPORTANT: This only adds the chosen subtype while the source permanent is entering the battlefield. * You should also use @link{mage.abilities.effects.common.continuous.AddChosenSubtypeEffect} to make the subtype persist. + * * @author LevelX2 */ public class EnterAttributeAddChosenSubtypeEffect extends OneShotEffect { @@ -35,9 +35,7 @@ public class EnterAttributeAddChosenSubtypeEffect extends OneShotEffect { Permanent permanent = game.getPermanentEntering(source.getSourceId()); SubType subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type"); if (permanent != null && subtype != null) { - if (!permanent.getSubtype(game).contains(subtype)) { - permanent.getSubtype(game).add(subtype); - } + permanent.addSubType(game, subtype); return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/FatesealEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/FatesealEffect.java index f32586c5779..de8cf909944 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/FatesealEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/FatesealEffect.java @@ -76,7 +76,7 @@ public class FatesealEffect extends OneShotEffect { } // move cards to the top of the library controller.putCardsOnTopOfLibrary(cards, game, source, true); - game.fireEvent(new GameEvent(GameEvent.EventType.FATESEAL, opponent.getId(), source.getSourceId(), source.getControllerId())); + game.fireEvent(new GameEvent(GameEvent.EventType.FATESEALED, opponent.getId(), source.getSourceId(), source.getControllerId())); controller.setTopCardRevealed(revealed); return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java b/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java index bb9c97f3ccd..e457dafa1c2 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java @@ -1,24 +1,31 @@ package mage.abilities.keyword; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.*; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; +import mage.cards.ModalDoubleFacesCardHalf; import mage.cards.SplitCardHalf; -import mage.constants.*; +import mage.constants.AsThoughEffectType; +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.stack.Spell; import mage.players.Player; +import java.util.UUID; + /** * Aftermath - * + *

* TODO: Implement once we get details on the comprehensive rules meaning of the * ability - * + *

* Current text is a shell copied from Flashback * * @author stravant @@ -113,9 +120,7 @@ class AftermathCantCastFromHand extends ContinuousRuleModifyingEffectImpl { Card card = game.getCard(event.getSourceId()); if (card != null && card.getId().equals(source.getSourceId())) { Zone zone = game.getState().getZone(card.getId()); - if (zone != null && (zone != Zone.GRAVEYARD)) { - return true; - } + return zone != null && (zone != Zone.GRAVEYARD); } return false; } @@ -155,14 +160,16 @@ class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl { sourceCard = ((SplitCardHalf) sourceCard).getParentCard(); sourceId = sourceCard.getId(); } + if (sourceCard instanceof ModalDoubleFacesCardHalf) { + sourceCard = ((ModalDoubleFacesCardHalf) sourceCard).getParentCard(); + sourceId = sourceCard.getId(); + } if (event.getTargetId().equals(sourceId)) { // Moving this spell from stack to yard Spell spell = game.getStack().getSpell(source.getSourceId()); - if (spell != null && spell.getFromZone() == Zone.GRAVEYARD) { - // And this spell was cast from the graveyard, so we need to exile it - return true; - } + // And this spell was cast from the graveyard, so we need to exile it + return spell != null && spell.getFromZone() == Zone.GRAVEYARD; } } return false; @@ -174,6 +181,9 @@ class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl { if (sourceCard instanceof SplitCardHalf) { sourceCard = ((SplitCardHalf) sourceCard).getParentCard(); } + if (sourceCard instanceof ModalDoubleFacesCardHalf) { + sourceCard = ((ModalDoubleFacesCardHalf) sourceCard).getParentCard(); + } if (sourceCard != null) { Player player = game.getPlayer(sourceCard.getOwnerId()); if (player != null) { diff --git a/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java b/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java index 410b7629792..2bcc5c8af85 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java @@ -5,14 +5,18 @@ import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.ExileZone; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterLandCard; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.players.Player; +import mage.target.common.TargetCardInExile; /** * @author BetaSteward_at_googlemail.com @@ -25,7 +29,8 @@ public class CascadeAbility extends TriggeredAbilityImpl { + "nonland card that costs less." + " You may cast it without paying its mana cost. " + "Put the exiled cards on the bottom in a random order.)"; - private boolean withReminder; + private final boolean withReminder; + private static final FilterCard filter = new FilterLandCard("land card (to put onto the battlefield)"); public CascadeAbility() { this(true); @@ -36,7 +41,7 @@ public class CascadeAbility extends TriggeredAbilityImpl { this.withReminder = withReminder; } - public CascadeAbility(final CascadeAbility ability) { + private CascadeAbility(final CascadeAbility ability) { super(ability); this.withReminder = ability.withReminder; } @@ -71,11 +76,11 @@ public class CascadeAbility extends TriggeredAbilityImpl { class CascadeEffect extends OneShotEffect { - public CascadeEffect() { + CascadeEffect() { super(Outcome.PutCardInPlay); } - public CascadeEffect(CascadeEffect effect) { + private CascadeEffect(CascadeEffect effect) { super(effect); } @@ -86,8 +91,7 @@ class CascadeEffect extends OneShotEffect { if (controller == null) { return false; } - ExileZone exile = game.getExile().createZone(source.getSourceId(), - controller.getName() + " Cascade"); + Cards cards = new CardsImpl(); card = game.getCard(source.getSourceId()); if (card == null) { return false; @@ -98,33 +102,43 @@ class CascadeEffect extends OneShotEffect { if (card == null) { break; } - controller.moveCardsToExile(card, source, game, true, exile.getId(), exile.getName()); + cards.add(card); + controller.moveCards(card, Zone.EXILED, source, game); } while (controller.canRespond() - && (card.isLand() - || !cardThatCostsLess(sourceCost, card, game))); + && (card.isLand() || card.getConvertedManaCost() >= sourceCost)); controller.getLibrary().reset(); // set back empty draw state if that caused an empty draw - if (card != null) { - if (controller.chooseUse(outcome, "Use cascade effect on " + card.getLogName() + '?', source, game)) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - } + GameEvent event = GameEvent.getEvent( + GameEvent.EventType.CASCADE_LAND, source.getSourceId(), + source.getSourceId(), source.getControllerId(), 0 + ); + game.replaceEvent(event); + if (event.getAmount() > 0) { + TargetCardInExile target = new TargetCardInExile( + 0, event.getAmount(), StaticFilters.FILTER_CARD_LAND, null, true + ); + controller.choose(Outcome.PutCardInPlay, cards, target, game); + controller.moveCards( + new CardsImpl(target.getTargets()).getCards(game), Zone.BATTLEFIELD, + source, game, true, false, false, null + ); + } + if (card != null && controller.chooseUse( + outcome, "Use cascade effect on " + card.getLogName() + '?', source, game + )) { + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + controller.cast(controller.chooseAbilityForCast(card, game, true), + game, true, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); } // Move the remaining cards to the buttom of the library in a random order - return controller.putCardsOnBottomOfLibrary(new CardsImpl(exile), game, source, false); + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); + return controller.putCardsOnBottomOfLibrary(cards, game, source, false); } @Override public CascadeEffect copy() { return new CascadeEffect(this); } - - private boolean cardThatCostsLess(int value, Card card, Game game) { - - return card.getConvertedManaCost() < value; - - } } diff --git a/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java b/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java index cd50dd7120e..c96cd120db4 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.keyword; import mage.ObjectColor; @@ -19,7 +18,6 @@ import mage.players.Player; import mage.util.CardUtil; /** - * * @author LevelX2 */ public class EmbalmAbility extends ActivatedAbilityImpl { @@ -89,9 +87,7 @@ class EmbalmEffect extends OneShotEffect { EmptyToken token = new EmptyToken(); CardUtil.copyTo(token).from(card); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) token.getColor(game).setColor(ObjectColor.WHITE); - if (!token.hasSubtype(SubType.ZOMBIE, game)) { - token.getSubtype(game).add(0, SubType.ZOMBIE); - } + token.addSubType(game, SubType.ZOMBIE); token.getManaCost().clear(); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.EMBALMED_CREATURE, token.getId(), source.getSourceId(), controller.getId())); token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId(), false, false, null); diff --git a/Mage/src/main/java/mage/abilities/keyword/EternalizeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EternalizeAbility.java index 494b2cb2c2f..61d185e263a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EternalizeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EternalizeAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.keyword; import mage.ObjectColor; @@ -19,7 +18,6 @@ import mage.players.Player; import mage.util.CardUtil; /** - * * @author igoudt */ public class EternalizeAbility extends ActivatedAbilityImpl { @@ -94,9 +92,7 @@ class EternalizeEffect extends OneShotEffect { EmptyToken token = new EmptyToken(); CardUtil.copyTo(token).from(card); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) token.getColor(game).setColor(ObjectColor.BLACK); - if (!token.hasSubtype(SubType.ZOMBIE, game)) { - token.getSubtype(game).add(0, SubType.ZOMBIE); - } + token.addSubType(game, SubType.ZOMBIE); token.getManaCost().clear(); token.removePTCDA(); token.getPower().modifyBaseValue(4); diff --git a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java index 91143c8978d..968ffeee61d 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java @@ -7,6 +7,7 @@ import mage.abilities.costs.Costs; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; +import mage.cards.ModalDoubleFacesCard; import mage.cards.SplitCard; import mage.constants.*; import mage.game.Game; @@ -68,12 +69,18 @@ public class FlashbackAbility extends SpellAbility { } // Flashback can never cast a split card by Fuse, because Fuse only works from hand // https://tappedout.net/mtg-questions/snapcaster-mage-and-flashback-on-a-fuse-card-one-or-both-halves-legal-targets/ - if (card.isSplitCard()) { + if (card instanceof SplitCard) { if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) { return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game); } else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) { return ((SplitCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game); } + } else if (card instanceof ModalDoubleFacesCard) { + if (((ModalDoubleFacesCard) card).getLeftHalfCard().getName().equals(abilityName)) { + return ((ModalDoubleFacesCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game); + } else if (((ModalDoubleFacesCard) card).getRightHalfCard().getName().equals(abilityName)) { + return ((ModalDoubleFacesCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game); + } } return card.getSpellAbility().canActivate(playerId, game); } @@ -87,12 +94,18 @@ public class FlashbackAbility extends SpellAbility { if (card != null) { if (spellAbilityToResolve == null) { SpellAbility spellAbilityCopy = null; - if (card.isSplitCard()) { + if (card instanceof SplitCard) { if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) { spellAbilityCopy = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy(); } else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) { spellAbilityCopy = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy(); } + } else if (card instanceof ModalDoubleFacesCard) { + if (((ModalDoubleFacesCard) card).getLeftHalfCard().getName().equals(abilityName)) { + spellAbilityCopy = ((ModalDoubleFacesCard) card).getLeftHalfCard().getSpellAbility().copy(); + } else if (((ModalDoubleFacesCard) card).getRightHalfCard().getName().equals(abilityName)) { + spellAbilityCopy = ((ModalDoubleFacesCard) card).getRightHalfCard().getSpellAbility().copy(); + } } else { spellAbilityCopy = card.getSpellAbility().copy(); } diff --git a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java index 6e45eccd8a5..025c6cebcb4 100644 --- a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java @@ -52,9 +52,10 @@ public class TransformAbility extends SimpleStaticAbility { for (CardType type : sourceCard.getCardType()) { permanent.addCardType(type); } - permanent.getSubtype(game).clear(); + permanent.removeAllSubTypes(game); + permanent.setIsAllCreatureTypes(sourceCard.isAllCreatureTypes()); for (SubType type : sourceCard.getSubtype(game)) { - permanent.getSubtype(game).add(type); + permanent.addSubType(game, type); } permanent.getSuperType().clear(); for (SuperType type : sourceCard.getSuperType()) { diff --git a/Mage/src/main/java/mage/cards/AdventureCardSpellImpl.java b/Mage/src/main/java/mage/cards/AdventureCardSpellImpl.java index 82c157401c9..21ec7047bdc 100644 --- a/Mage/src/main/java/mage/cards/AdventureCardSpellImpl.java +++ b/Mage/src/main/java/mage/cards/AdventureCardSpellImpl.java @@ -1,8 +1,3 @@ -/* - * 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; import mage.abilities.Modes; diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index 364415d96d5..e49bf7072fe 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -1,7 +1,5 @@ package mage.cards; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.Mana; import mage.abilities.Abilities; @@ -16,6 +14,9 @@ import mage.game.Game; import mage.game.GameState; import mage.game.permanent.Permanent; +import java.util.List; +import java.util.UUID; + public interface Card extends MageObject { UUID getOwnerId(); @@ -29,8 +30,9 @@ public interface Card extends MageObject { /** * For cards: return all basic and dynamic abilities * For permanents: return all basic and dynamic abilities + * * @param game - * @return + * @return */ Abilities getAbilities(Game game); @@ -62,8 +64,6 @@ public interface Card extends MageObject { String getFlipCardName(); - boolean isSplitCard(); - boolean isTransformable(); void setTransformable(boolean transformable); @@ -76,21 +76,23 @@ public interface Card extends MageObject { void addInfo(String key, String value, Game game); + // WARNING, don't add new move/remove methods (if you add then you must override it in all multi-part cards like Split or MDF) + /** * Moves the card to the specified zone * * @param zone * @param sourceId * @param game - * @param flag If zone - *

    - *
  • LIBRARY:
    • true - put on top
    • false - put on - * bottom
  • - *
  • BATTLEFIELD:
    • true - tapped
    • false - - * untapped
  • - *
  • GRAVEYARD:
    • true - not from Battlefield
    • false - from - * Battlefield
  • - *
+ * @param flag If zone + *
    + *
  • LIBRARY:
    • true - put on top
    • false - put on + * bottom
  • + *
  • BATTLEFIELD:
    • true - tapped
    • false - + * untapped
  • + *
  • GRAVEYARD:
    • true - not from Battlefield
    • false - from + * Battlefield
  • + *
* @return true if card was moved to zone */ boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag); @@ -100,8 +102,8 @@ public interface Card extends MageObject { /** * Moves the card to an exile zone * - * @param exileId set to null for generic exile zone - * @param name used for exile zone with the specified exileId + * @param exileId set to null for generic exile zone + * @param name used for exile zone with the specified exileId * @param sourceId * @param game * @return true if card was moved to zone @@ -112,6 +114,7 @@ public interface Card extends MageObject { boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId); + // WARNING, don't add new move/remove methods (if you add then you must override it in all multi-parts card like Split Half or MDF Half) boolean removeFromZone(Game game, Zone fromZone, UUID sourceId); boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId); diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index e059075a167..8bee7eeb669 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -1,13 +1,10 @@ package mage.cards; -import com.google.common.collect.ImmutableList; import mage.MageObject; import mage.MageObjectImpl; import mage.Mana; import mage.ObjectColor; import mage.abilities.*; -import mage.abilities.hint.Hint; -import mage.abilities.hint.HintUtils; import mage.abilities.keyword.FlashbackAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.repository.PluginClassloaderRegistery; @@ -22,6 +19,7 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.game.stack.StackObject; +import mage.util.CardUtil; import mage.util.GameLog; import mage.util.SubTypeList; import mage.watchers.Watcher; @@ -57,10 +55,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { protected boolean flipCard; protected String flipCardName; protected boolean usesVariousArt = false; - protected boolean splitCard; protected boolean morphCard; - protected boolean modalDFC; // modal double faces card - protected List attachments = new ArrayList<>(); public CardImpl(UUID ownerId, CardSetInfo setInfo, CardType[] cardTypes, String costs) { @@ -139,9 +134,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { flipCard = card.flipCard; flipCardName = card.flipCardName; usesVariousArt = card.usesVariousArt; - splitCard = card.splitCard; morphCard = card.morphCard; - modalDFC = card.modalDFC; this.attachments.addAll(card.attachments); } @@ -223,52 +216,16 @@ public abstract class CardImpl extends MageObjectImpl implements Card { game.getState().getCardState(objectId).addInfo(key, value); } - protected static final List rulesError = ImmutableList.of("Exception occurred in rules generation"); - @Override public List getRules() { - try { - return getAbilities().getRules(this.getName()); - } catch (Exception e) { - logger.info("Exception in rules generation for card: " + this.getName(), e); - } - return rulesError; + Abilities sourceAbilities = this.getAbilities(); + return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(), sourceAbilities, sourceAbilities); } @Override public List getRules(Game game) { - try { - List rules = getAbilities(game).getRules(getName()); - if (game != null) { - // debug state - for (String data : game.getState().getCardState(objectId).getInfo().values()) { - rules.add(data); - } - // ability hints - List abilityHints = new ArrayList<>(); - if (HintUtils.ABILITY_HINTS_ENABLE) { - for (Ability ability : abilities) { - for (Hint hint : ability.getHints()) { - String s = hint.getText(game, ability); - if (s != null && !s.isEmpty()) { - abilityHints.add(s); - } - } - } - } - - // restrict hints only for permanents, not cards - // total hints - if (!abilityHints.isEmpty()) { - rules.add(HintUtils.HINT_START_MARK); - HintUtils.appendHints(rules, abilityHints); - } - } - return rules; - } catch (Exception e) { - logger.error("Exception in rules generation for card: " + this.getName(), e); - } - return rulesError; + Abilities sourceAbilities = this.getAbilities(game); + return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), sourceAbilities, sourceAbilities); } /** @@ -314,7 +271,8 @@ public abstract class CardImpl extends MageObjectImpl implements Card { // workaround to add dynamic flashback ability from main card to all parts (example: Snapcaster Mage gives flashback to split card) if (!this.getId().equals(this.getMainCard().getId())) { CardState mainCardState = game.getState().getCardState(this.getMainCard().getId()); - if (mainCardState != null + if (this.getSpellAbility() != null // lands can't be casted (haven't spell ability), so ignore it + && mainCardState != null && !mainCardState.hasLostAllAbilities() && mainCardState.getAbilities().containsClass(FlashbackAbility.class)) { FlashbackAbility flash = new FlashbackAbility(this.getManaCost(), this.isInstant() ? TimingRule.INSTANT : TimingRule.SORCERY); @@ -563,13 +521,22 @@ public abstract class CardImpl extends MageObjectImpl implements Card { stackObject = game.getStack().getSpell(this.getId(), false); } - if (stackObject == null && (this instanceof SplitCard)) { // handle if half of Split cast is on the stack + // handle half of Split Cards on stack + if (stackObject == null && (this instanceof SplitCard)) { stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId(), false); if (stackObject == null) { stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId(), false); } } + // handle half of Modal Double Faces Cards on stack + if (stackObject == null && (this instanceof ModalDoubleFacesCard)) { + stackObject = game.getStack().getSpell(((ModalDoubleFacesCard) this).getLeftHalfCard().getId(), false); + if (stackObject == null) { + stackObject = game.getStack().getSpell(((ModalDoubleFacesCard) this).getRightHalfCard().getId(), false); + } + } + if (stackObject == null && (this instanceof AdventureCard)) { stackObject = game.getStack().getSpell(((AdventureCard) this).getSpellCard().getId(), false); } @@ -687,10 +654,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { @Override public final Card getSecondCardFace() { - // TODO: remove when MDFCs are implemented - if (modalDFC) { - return null; - } // init second side card on first call if (secondSideCardClazz == null && secondSideCard == null) { return null; @@ -726,11 +689,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { return flipCardName; } - @Override - public boolean isSplitCard() { - return splitCard; - } - @Override public boolean getUsesVariousArt() { return usesVariousArt; diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java new file mode 100644 index 00000000000..5a981ae91a1 --- /dev/null +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java @@ -0,0 +1,281 @@ +package mage.cards; + +import mage.MageInt; +import mage.MageObject; +import mage.ObjectColor; +import mage.abilities.Abilities; +import mage.abilities.AbilitiesImpl; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.ZoneChangeEvent; +import mage.util.CardUtil; +import mage.util.SubTypeList; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; + +/** + * @author JayDi85 + */ +public abstract class ModalDoubleFacesCard extends CardImpl { + + protected Card leftHalfCard; // main card in all zone + protected Card rightHalfCard; // second side card, can be only in stack and battlefield zones + + public ModalDoubleFacesCard(UUID ownerId, CardSetInfo setInfo, + CardType[] typesLeft, SubType[] subTypesLeft, String costsLeft, + String secondSideName, CardType[] typesRight, SubType[] subTypesRight, String costsRight) { + super(ownerId, setInfo, typesLeft, costsLeft + costsRight, SpellAbilityType.MODAL); + // main card name must be same as left side + leftHalfCard = new ModalDoubleFacesCardHalfImpl(this.getOwnerId(), new CardSetInfo(setInfo.getName(), setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()), + typesLeft, subTypesLeft, costsLeft, this, SpellAbilityType.MODAL_LEFT); + rightHalfCard = new ModalDoubleFacesCardHalfImpl(this.getOwnerId(), new CardSetInfo(secondSideName, setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()), + typesRight, subTypesRight, costsRight, this, SpellAbilityType.MODAL_RIGHT); + } + + public ModalDoubleFacesCard(ModalDoubleFacesCard card) { + super(card); + this.leftHalfCard = card.getLeftHalfCard().copy(); + ((ModalDoubleFacesCardHalf) leftHalfCard).setParentCard(this); + this.rightHalfCard = card.rightHalfCard.copy(); + ((ModalDoubleFacesCardHalf) rightHalfCard).setParentCard(this); + } + + public ModalDoubleFacesCardHalf getLeftHalfCard() { + return (ModalDoubleFacesCardHalf) leftHalfCard; + } + + public ModalDoubleFacesCardHalf getRightHalfCard() { + return (ModalDoubleFacesCardHalf) rightHalfCard; + } + + @Override + public void assignNewId() { + super.assignNewId(); + leftHalfCard.assignNewId(); + rightHalfCard.assignNewId(); + } + + @Override + public void setCopy(boolean isCopy, MageObject copiedFrom) { + super.setCopy(isCopy, copiedFrom); + leftHalfCard.setCopy(isCopy, copiedFrom); + rightHalfCard.setCopy(isCopy, copiedFrom); + } + + @Override + public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, List appliedEffects) { + if (super.moveToZone(toZone, sourceId, game, flag, appliedEffects)) { + game.getState().setZone(leftHalfCard.getId(), toZone); + game.getState().setZone(rightHalfCard.getId(), toZone); + return true; + } + return false; + } + + @Override + public void setZone(Zone zone, Game game) { + super.setZone(zone, game); + game.setZone(leftHalfCard.getId(), zone); + game.setZone(rightHalfCard.getId(), zone); + } + + @Override + public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List appliedEffects) { + if (super.moveToExile(exileId, name, sourceId, game, appliedEffects)) { + Zone currentZone = game.getState().getZone(getId()); + game.getState().setZone(leftHalfCard.getId(), currentZone); + game.getState().setZone(rightHalfCard.getId(), currentZone); + return true; + } + return false; + } + + @Override + public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) { + // zone contains only one main card + return super.removeFromZone(game, fromZone, sourceId); + } + + @Override + public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) { + if (isCopy()) { // same as meld cards + super.updateZoneChangeCounter(game, event); + return; + } + super.updateZoneChangeCounter(game, event); + leftHalfCard.updateZoneChangeCounter(game, event); + rightHalfCard.updateZoneChangeCounter(game, event); + } + + @Override + public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) { + switch (ability.getSpellAbilityType()) { + case MODAL_LEFT: + return this.leftHalfCard.cast(game, fromZone, ability, controllerId); + case MODAL_RIGHT: + return this.rightHalfCard.cast(game, fromZone, ability, controllerId); + default: + if (this.leftHalfCard.getSpellAbility() != null) + this.leftHalfCard.getSpellAbility().setControllerId(controllerId); + if (this.rightHalfCard.getSpellAbility() != null) + this.rightHalfCard.getSpellAbility().setControllerId(controllerId); + return super.cast(game, fromZone, ability, controllerId); + } + } + + + @Override + public ArrayList getCardType() { + // CardImpl's constructor can call some code on init, so you must check left/right before + // it's a bad workaround + return leftHalfCard != null ? leftHalfCard.getCardType() : cardType; + } + + @Override + public SubTypeList getSubtype(Game game) { + // rules: While a double-faced card isn’t on the stack or battlefield, consider only the characteristics of its front face. + + // CardImpl's constructor can call some code on init, so you must check left/right before + return leftHalfCard != null ? leftHalfCard.getSubtype(game) : subtype; + } + + @Override + public boolean hasSubtype(SubType subtype, Game game) { + return leftHalfCard.hasSubtype(subtype, game); + } + + @Override + public EnumSet getSuperType() { + return EnumSet.noneOf(SuperType.class); + } + + @Override + public Abilities getAbilities() { + return getInnerAbilities(false); + } + + public Abilities getSharedAbilities(Game game) { + // no shared abilities for mdf cards (e.g. must be left or right only) + return new AbilitiesImpl<>(); + } + + @Override + public Abilities getAbilities(Game game) { + return getInnerAbilities(game, false); + } + + private Abilities getInnerAbilities(Game game, boolean showOnlyMainSide) { + Abilities allAbilites = new AbilitiesImpl<>(); + + // ignore default spell ability from main card (only halfes are actual) + for (Ability ability : super.getAbilities(game)) { + if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.MODAL) { + continue; + } + allAbilites.add(ability); + } + + allAbilites.addAll(leftHalfCard.getAbilities(game)); + if (!showOnlyMainSide) { + allAbilites.addAll(rightHalfCard.getAbilities(game)); + } + + return allAbilites; + } + + private Abilities getInnerAbilities(boolean showOnlyMainSide) { + Abilities allAbilites = new AbilitiesImpl<>(); + + // ignore default spell ability from main card (only halfes are actual) + for (Ability ability : super.getAbilities()) { + if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.MODAL) { + continue; + } + allAbilites.add(ability); + } + + allAbilites.addAll(leftHalfCard.getAbilities()); + if (!showOnlyMainSide) { + allAbilites.addAll(rightHalfCard.getAbilities()); + } + + return allAbilites; + } + + @Override + public List getRules() { + // rules must show only main side (another side visible by toggle/transform button in GUI) + // card hints from both sides + return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(), + this.getInnerAbilities(true), this.getInnerAbilities(false) + ); + } + + @Override + public List getRules(Game game) { + // rules must show only main side (another side visible by toggle/transform button in GUI) + // card hints from both sides + return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), + this.getInnerAbilities(game, true), this.getInnerAbilities(game, false) + ); + } + + @Override + public boolean hasAbility(Ability ability, Game game) { + return super.hasAbility(ability, game); + } + + @Override + public ObjectColor getColor(Game game) { + return leftHalfCard.getColor(game); + } + + @Override + public ObjectColor getFrameColor(Game game) { + return leftHalfCard.getFrameColor(game); + } + + @Override + public void setOwnerId(UUID ownerId) { + super.setOwnerId(ownerId); + abilities.setControllerId(ownerId); + leftHalfCard.getAbilities().setControllerId(ownerId); + leftHalfCard.setOwnerId(ownerId); + rightHalfCard.getAbilities().setControllerId(ownerId); + rightHalfCard.setOwnerId(ownerId); + } + + @Override + public ManaCosts getManaCost() { + return leftHalfCard.getManaCost(); + } + + @Override + public int getConvertedManaCost() { + // Rules: + // The converted mana cost of a modal double-faced card is based on the characteristics of the + // face that’s being considered. On the stack and battlefield, consider whichever face is up. + // In all other zones, consider only the front face. This is different than how the converted + // mana cost of a transforming double-faced card is determined. + + // on stack or battlefield it must be half card with own cost + return leftHalfCard.getConvertedManaCost(); + } + + @Override + public MageInt getPower() { + return leftHalfCard.getPower(); + } + + @Override + public MageInt getToughness() { + return leftHalfCard.getToughness(); + } +} diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java new file mode 100644 index 00000000000..bb2c1364cf9 --- /dev/null +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java @@ -0,0 +1,18 @@ +package mage.cards; + +import mage.MageInt; + +/** + * @author JayDi85 + */ +public interface ModalDoubleFacesCardHalf extends Card { + + @Override + ModalDoubleFacesCardHalf copy(); + + void setParentCard(ModalDoubleFacesCard card); + + ModalDoubleFacesCard getParentCard(); + + void setPT(MageInt power, MageInt toughtness); +} diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java new file mode 100644 index 00000000000..475841d928f --- /dev/null +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java @@ -0,0 +1,109 @@ +package mage.cards; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SpellAbilityType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +/** + * @author JayDi85 + */ +public class ModalDoubleFacesCardHalfImpl extends CardImpl implements ModalDoubleFacesCardHalf { + + ModalDoubleFacesCard parentCard; + + public ModalDoubleFacesCardHalfImpl(UUID ownerId, CardSetInfo setInfo, CardType[] cardTypes, SubType[] cardSubTypes, + String costs, ModalDoubleFacesCard parentCard, SpellAbilityType spellAbilityType) { + super(ownerId, setInfo, cardTypes, costs, spellAbilityType); + this.subtype.addAll(Arrays.asList(cardSubTypes)); + this.parentCard = parentCard; + } + + public ModalDoubleFacesCardHalfImpl(final ModalDoubleFacesCardHalfImpl card) { + super(card); + this.parentCard = card.parentCard; + } + + @Override + public UUID getOwnerId() { + return parentCard.getOwnerId(); + } + + @Override + public String getImageName() { + // TODO: own name? + return parentCard.getImageName(); + } + + @Override + public String getExpansionSetCode() { + // TODO: own set code? + return parentCard.getExpansionSetCode(); + } + + @Override + public String getCardNumber() { + // TODO: own card number? + return parentCard.getCardNumber(); + } + + @Override + public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, List appliedEffects) { + return parentCard.moveToZone(toZone, sourceId, game, flag, appliedEffects); + } + + @Override + public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List appliedEffects) { + return parentCard.moveToExile(exileId, name, sourceId, game, appliedEffects); + } + + @Override + public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) { + return parentCard.removeFromZone(game, fromZone, sourceId); + } + + @Override + public ModalDoubleFacesCard getMainCard() { + return parentCard; + } + + @Override + public void setZone(Zone zone, Game game) { + game.setZone(parentCard.getId(), zone); + game.setZone(parentCard.getLeftHalfCard().getId(), zone); + game.setZone(parentCard.getRightHalfCard().getId(), zone); + } + + @Override + public ModalDoubleFacesCardHalfImpl copy() { + return new ModalDoubleFacesCardHalfImpl(this); + } + + @Override + public void setParentCard(ModalDoubleFacesCard card) { + this.parentCard = card; + } + + @Override + public ModalDoubleFacesCard getParentCard() { + return this.parentCard; + } + + @Override + public void setPT(MageInt power, MageInt toughness) { + this.power = power; + this.toughness = toughness; + } + + @Override + public String getIdName() { + // id must send to main card (popup card hint in game logs) + return getName() + " [" + parentCard.getId().toString().substring(0, 3) + ']'; + } +} diff --git a/Mage/src/main/java/mage/cards/SplitCard.java b/Mage/src/main/java/mage/cards/SplitCard.java index 936339bf640..75298907b4f 100644 --- a/Mage/src/main/java/mage/cards/SplitCard.java +++ b/Mage/src/main/java/mage/cards/SplitCard.java @@ -32,7 +32,6 @@ public abstract class SplitCard extends CardImpl { String[] names = setInfo.getName().split(" // "); leftHalfCard = new SplitCardHalfImpl(this.getOwnerId(), new CardSetInfo(names[0], setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()), typesLeft, costsLeft, this, SpellAbilityType.SPLIT_LEFT); rightHalfCard = new SplitCardHalfImpl(this.getOwnerId(), new CardSetInfo(names[1], setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()), typesRight, costsRight, this, SpellAbilityType.SPLIT_RIGHT); - this.splitCard = true; } public SplitCard(SplitCard card) { @@ -187,4 +186,15 @@ public abstract class SplitCard extends CardImpl { rightHalfCard.getAbilities().setControllerId(ownerId); rightHalfCard.setOwnerId(ownerId); } + + @Override + public int getConvertedManaCost() { + // 202.3d The converted mana cost of a split card not on the stack or of a fused split spell on the + // stack is determined from the combined mana costs of its halves. Otherwise, while a split card is + // on the stack, the converted mana cost of the spell is determined by the mana cost of the half + // that was chosen to be cast. See rule 708, “Split Cards.” + + // split card and it's halfes contains own mana costs, so no need to rewrite logic + return super.getConvertedManaCost(); + } } diff --git a/Mage/src/main/java/mage/cards/SplitCardHalf.java b/Mage/src/main/java/mage/cards/SplitCardHalf.java index 6baee1c2949..76db56ce603 100644 --- a/Mage/src/main/java/mage/cards/SplitCardHalf.java +++ b/Mage/src/main/java/mage/cards/SplitCardHalf.java @@ -1,12 +1,6 @@ -/* - * 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; /** - * * @author LevelX2 */ public interface SplitCardHalf extends Card { diff --git a/Mage/src/main/java/mage/cards/SplitCardHalfImpl.java b/Mage/src/main/java/mage/cards/SplitCardHalfImpl.java index 4649864452e..a1adb62bb46 100644 --- a/Mage/src/main/java/mage/cards/SplitCardHalfImpl.java +++ b/Mage/src/main/java/mage/cards/SplitCardHalfImpl.java @@ -1,8 +1,3 @@ -/* - * 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; import mage.constants.CardType; @@ -14,7 +9,6 @@ import java.util.List; import java.util.UUID; /** - * * @author LevelX2 */ public class SplitCardHalfImpl extends CardImpl implements SplitCardHalf { @@ -61,6 +55,11 @@ public class SplitCardHalfImpl extends CardImpl implements SplitCardHalf { return splitCardParent.moveToExile(exileId, name, sourceId, game, appliedEffects); } + @Override + public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) { + return splitCardParent.removeFromZone(game, fromZone, sourceId); + } + @Override public SplitCard getMainCard() { return splitCardParent; diff --git a/Mage/src/main/java/mage/cards/mock/MockCard.java b/Mage/src/main/java/mage/cards/mock/MockCard.java index 8598688a8d0..3221ffda4f0 100644 --- a/Mage/src/main/java/mage/cards/mock/MockCard.java +++ b/Mage/src/main/java/mage/cards/mock/MockCard.java @@ -6,6 +6,7 @@ import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.cards.CardImpl; +import mage.cards.ModalDoubleFacesCard; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import org.apache.log4j.Logger; @@ -20,6 +21,7 @@ import java.util.List; public class MockCard extends CardImpl { static public String ADVENTURE_NAME_SEPARATOR = " // "; + static public String MODAL_DOUBLE_FACES_NAME_SEPARATOR = " // "; // Needs to be here, as it is normally calculated from the // PlaneswalkerEntersWithLoyaltyAbility of the card... but the MockCard @@ -30,6 +32,7 @@ public class MockCard extends CardImpl { protected ManaCosts manaCostLeft; protected ManaCosts manaCostRight; protected String adventureSpellName; + protected boolean isModalDoubleFacesCard; public MockCard(CardInfo card) { super(null, card.getName()); @@ -53,7 +56,6 @@ public class MockCard extends CardImpl { this.frameColor = card.getFrameColor(); this.frameStyle = card.getFrameStyle(); - this.splitCard = card.isSplitCard(); this.flipCard = card.isFlipCard(); this.transformable = card.isDoubleFaced(); @@ -66,10 +68,16 @@ public class MockCard extends CardImpl { this.adventureSpellName = card.getAdventureSpellName(); } + if (card.isModalDoubleFacesCard()) { + ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card.getCard(); + CardInfo mdfSecondSide = new CardInfo(mdfCard.getRightHalfCard()); + this.secondSideCard = new MockCard(mdfSecondSide); + this.isModalDoubleFacesCard = true; + } + if (this.isPlaneswalker()) { String startingLoyaltyString = card.getStartingLoyalty(); if (startingLoyaltyString.isEmpty()) { - //Logger.getLogger(MockCard.class).warn("Planeswalker `" + this.name + "` has empty starting loyalty."); } else { try { this.startingLoyalty = Integer.parseInt(startingLoyaltyString); @@ -117,8 +125,14 @@ public class MockCard extends CardImpl { } public String getFullName(boolean showSecondName) { + if (!showSecondName) { + return getName(); + } + if (adventureSpellName != null) { return getName() + ADVENTURE_NAME_SEPARATOR + adventureSpellName; + } else if (isModalDoubleFacesCard) { + return getName() + MODAL_DOUBLE_FACES_NAME_SEPARATOR + this.secondSideCard.getName(); } else { return getName(); } @@ -145,4 +159,10 @@ public class MockCard extends CardImpl { private Ability textAbilityFromString(final String text) { return new MockAbility(text); } + + @Override + public boolean isTransformable() { + // must enable toggle mode in deck editor (switch between card sides); + return super.isTransformable() || this.isModalDoubleFacesCard; + } } diff --git a/Mage/src/main/java/mage/cards/mock/MockSplitCard.java b/Mage/src/main/java/mage/cards/mock/MockSplitCard.java index 7858ba1e656..d0b3b861131 100644 --- a/Mage/src/main/java/mage/cards/mock/MockSplitCard.java +++ b/Mage/src/main/java/mage/cards/mock/MockSplitCard.java @@ -35,7 +35,6 @@ public class MockSplitCard extends SplitCard { this.usesVariousArt = card.usesVariousArt(); this.color = card.getColor(); - this.splitCard = card.isSplitCard(); this.flipCard = card.isFlipCard(); this.transformable = card.isDoubleFaced(); diff --git a/Mage/src/main/java/mage/cards/repository/CardInfo.java b/Mage/src/main/java/mage/cards/repository/CardInfo.java index d369bac186c..0d674bfbc83 100644 --- a/Mage/src/main/java/mage/cards/repository/CardInfo.java +++ b/Mage/src/main/java/mage/cards/repository/CardInfo.java @@ -3,8 +3,6 @@ package mage.cards.repository; import com.j256.ormlite.field.DataType; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; -import java.util.*; -import java.util.stream.Collectors; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -17,6 +15,9 @@ import mage.util.CardUtil; import mage.util.SubTypeList; import org.apache.log4j.Logger; +import java.util.*; +import java.util.stream.Collectors; + /** * @author North */ @@ -103,6 +104,12 @@ public class CardInfo { protected boolean adventureCard; @DatabaseField protected String adventureSpellName; + @DatabaseField + protected boolean modalDoubleFacesCard; + @DatabaseField + protected String modalDoubleFacesSecondSideName; + + // if you add new field with card side name then update CardRepository.addNewNames too public enum ManaCostSide { LEFT, RIGHT, ALL @@ -121,7 +128,7 @@ public class CardInfo { this.toughness = card.getToughness().toString(); this.convertedManaCost = card.getConvertedManaCost(); this.rarity = card.getRarity(); - this.splitCard = card.isSplitCard(); + this.splitCard = card instanceof SplitCard; this.splitCardFuse = card.getSpellAbility() != null && card.getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED; this.splitCardAftermath = card.getSpellAbility() != null && card.getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_AFTERMATH; @@ -140,6 +147,11 @@ public class CardInfo { this.adventureSpellName = ((AdventureCard) card).getSpellCard().getName(); } + if (card instanceof ModalDoubleFacesCard) { + this.modalDoubleFacesCard = true; + this.modalDoubleFacesSecondSideName = ((ModalDoubleFacesCard) card).getRightHalfCard().getName(); + } + this.frameStyle = card.getFrameStyle().toString(); this.frameColor = card.getFrameColor(null).toString(); this.variousArt = card.getUsesVariousArt(); @@ -153,13 +165,17 @@ public class CardInfo { this.setSubtypes(card.getSubtype(null).stream().map(SubType::toString).collect(Collectors.toList())); this.setSuperTypes(card.getSuperType()); - // mana cost can contains multiple cards (split left/right, card/adventure) + // mana cost can contains multiple cards (split left/right, modal double faces, card/adventure) if (card instanceof SplitCard) { List manaCostLeft = ((SplitCard) card).getLeftHalfCard().getManaCost().getSymbols(); List manaCostRight = ((SplitCard) card).getRightHalfCard().getManaCost().getSymbols(); this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight)); + } else if (card instanceof ModalDoubleFacesCard) { + List manaCostLeft = ((ModalDoubleFacesCard) card).getLeftHalfCard().getManaCost().getSymbols(); + List manaCostRight = ((ModalDoubleFacesCard) card).getRightHalfCard().getManaCost().getSymbols(); + this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight)); } else if (card instanceof AdventureCard) { - List manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols(); // Spell from left like MTGA + List manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols(); List manaCostRight = card.getManaCost().getSymbols(); this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight)); } else { @@ -181,6 +197,12 @@ public class CardInfo { length += rule.length(); rulesList.add(rule); } + } else if (card instanceof ModalDoubleFacesCard) { + // mdf card return main side's rules only (GUI can toggle it to another side) + for (String rule : card.getRules()) { + length += rule.length(); + rulesList.add(rule); + } } else { for (String rule : card.getRules()) { length += rule.length(); @@ -222,7 +244,6 @@ public class CardInfo { } } if (this.startingLoyalty == null) { - //Logger.getLogger(CardInfo.class).warn("Planeswalker `" + card.getName() + "` missing starting loyalty"); this.startingLoyalty = ""; } } else { @@ -447,4 +468,12 @@ public class CardInfo { public String getAdventureSpellName() { return adventureSpellName; } + + public boolean isModalDoubleFacesCard() { + return modalDoubleFacesCard; + } + + public String getModalDoubleFacesSecondSideName() { + return modalDoubleFacesSecondSideName; + } } diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java index 9df069865e0..1be847a9f1f 100644 --- a/Mage/src/main/java/mage/cards/repository/CardRepository.java +++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java @@ -34,9 +34,9 @@ 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 = 52; + private static final long CARD_DB_VERSION = 53; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 232; + private static final long CARD_CONTENT_VERSION = 233; private Dao cardDao; private Set classNames; private final RepositoryEventSource eventSource = new RepositoryEventSource(); @@ -136,16 +136,10 @@ public enum CardRepository { Set names = new TreeSet<>(); try { QueryBuilder qb = cardDao.queryBuilder(); - qb.distinct().selectColumns("name"); + qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName"); List results = cardDao.query(qb.prepare()); for (CardInfo card : results) { - int result = card.getName().indexOf(" // "); - if (result > 0) { - names.add(card.getName().substring(0, result)); - names.add(card.getName().substring(result + 4)); - } else { - names.add(card.getName()); - } + addNewNames(card, names); } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting names from DB : " + ex); @@ -153,21 +147,39 @@ public enum CardRepository { return names; } + private void addNewNames(CardInfo card, Set namesList) { + // require before call: qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName"...); + + // normal names + int result = card.getName().indexOf(" // "); + if (result > 0) { + namesList.add(card.getName().substring(0, result)); + namesList.add(card.getName().substring(result + 4)); + } else { + namesList.add(card.getName()); + } + + // additional names from double side cards + if (card.getSecondSideName() != null && !card.getSecondSideName().isEmpty()) { + namesList.add(card.getSecondSideName()); + } + if (card.getModalDoubleFacesSecondSideName() != null && !card.getModalDoubleFacesSecondSideName().isEmpty()) { + namesList.add(card.getModalDoubleFacesSecondSideName()); + } + if (card.getFlipCardName() != null && !card.getFlipCardName().isEmpty()) { + namesList.add(card.getFlipCardName()); + } + } + public Set getNonLandNames() { Set names = new TreeSet<>(); try { QueryBuilder qb = cardDao.queryBuilder(); - qb.distinct().selectColumns("name"); + qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName"); qb.where().not().like("types", new SelectArg('%' + CardType.LAND.name() + '%')); List results = cardDao.query(qb.prepare()); for (CardInfo card : results) { - int result = card.getName().indexOf(" // "); - if (result > 0) { - names.add(card.getName().substring(0, result)); - names.add(card.getName().substring(result + 4)); - } else { - names.add(card.getName()); - } + addNewNames(card, names); } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting non-land names from DB : " + ex); @@ -188,7 +200,7 @@ public enum CardRepository { Set names = new TreeSet<>(); try { QueryBuilder qb = cardDao.queryBuilder(); - qb.distinct().selectColumns("name"); + qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName"); Where where = qb.where(); where.and( where.not().like("supertypes", '%' + SuperType.BASIC.name() + '%'), @@ -196,13 +208,7 @@ public enum CardRepository { ); List results = cardDao.query(qb.prepare()); for (CardInfo card : results) { - int result = card.getName().indexOf(" // "); - if (result > 0) { - names.add(card.getName().substring(0, result)); - names.add(card.getName().substring(result + 4)); - } else { - names.add(card.getName()); - } + addNewNames(card, names); } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting non-land names from DB : " + ex); @@ -215,17 +221,11 @@ public enum CardRepository { Set names = new TreeSet<>(); try { QueryBuilder qb = cardDao.queryBuilder(); - qb.distinct().selectColumns("name"); + qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName"); qb.where().not().like("supertypes", new SelectArg('%' + SuperType.BASIC.name() + '%')); List results = cardDao.query(qb.prepare()); for (CardInfo card : results) { - int result = card.getName().indexOf(" // "); - if (result > 0) { - names.add(card.getName().substring(0, result)); - names.add(card.getName().substring(result + 4)); - } else { - names.add(card.getName()); - } + addNewNames(card, names); } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting non-land names from DB : " + ex); @@ -238,17 +238,11 @@ public enum CardRepository { Set names = new TreeSet<>(); try { QueryBuilder qb = cardDao.queryBuilder(); - qb.distinct().selectColumns("name"); + qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName"); qb.where().like("types", new SelectArg('%' + CardType.CREATURE.name() + '%')); List results = cardDao.query(qb.prepare()); for (CardInfo card : results) { - int result = card.getName().indexOf(" // "); - if (result > 0) { - names.add(card.getName().substring(0, result)); - names.add(card.getName().substring(result + 4)); - } else { - names.add(card.getName()); - } + addNewNames(card, names); } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting creature names from DB : " + ex); @@ -261,17 +255,11 @@ public enum CardRepository { Set names = new TreeSet<>(); try { QueryBuilder qb = cardDao.queryBuilder(); - qb.distinct().selectColumns("name"); + qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName"); qb.where().like("types", new SelectArg('%' + CardType.ARTIFACT.name() + '%')); List results = cardDao.query(qb.prepare()); for (CardInfo card : results) { - int result = card.getName().indexOf(" // "); - if (result > 0) { - names.add(card.getName().substring(0, result)); - names.add(card.getName().substring(result + 4)); - } else { - names.add(card.getName()); - } + addNewNames(card, names); } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting artifact names from DB : " + ex); @@ -284,7 +272,7 @@ public enum CardRepository { Set names = new TreeSet<>(); try { QueryBuilder qb = cardDao.queryBuilder(); - qb.distinct().selectColumns("name"); + qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName"); Where where = qb.where(); where.and( where.not().like("types", '%' + CardType.CREATURE.name() + '%'), @@ -292,13 +280,7 @@ public enum CardRepository { ); List results = cardDao.query(qb.prepare()); for (CardInfo card : results) { - int result = card.getName().indexOf(" // "); - if (result > 0) { - names.add(card.getName().substring(0, result)); - names.add(card.getName().substring(result + 4)); - } else { - names.add(card.getName()); - } + addNewNames(card, names); } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting non-land and non-creature names from DB : " + ex); @@ -310,7 +292,7 @@ public enum CardRepository { Set names = new TreeSet<>(); try { QueryBuilder qb = cardDao.queryBuilder(); - qb.distinct().selectColumns("name"); + qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName"); Where where = qb.where(); where.and( where.not().like("types", '%' + CardType.ARTIFACT.name() + '%'), @@ -318,13 +300,7 @@ public enum CardRepository { ); List results = cardDao.query(qb.prepare()); for (CardInfo card : results) { - int result = card.getName().indexOf(" // "); - if (result > 0) { - names.add(card.getName().substring(0, result)); - names.add(card.getName().substring(result + 4)); - } else { - names.add(card.getName()); - } + addNewNames(card, names); } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting non-artifact non-land names from DB : " + ex); diff --git a/Mage/src/main/java/mage/cards/repository/CardScanner.java b/Mage/src/main/java/mage/cards/repository/CardScanner.java index 6660c6da847..c6a224b2320 100644 --- a/Mage/src/main/java/mage/cards/repository/CardScanner.java +++ b/Mage/src/main/java/mage/cards/repository/CardScanner.java @@ -59,7 +59,7 @@ public final class CardScanner { new CardSetInfo(setInfo.getName(), set.getCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()), errorsList); if (card != null) { - cardsToAdd.add(new CardInfo(card)); + cardsToAdd.add(new CardInfo(card)); // normal, transformed, adventure, modal double faces -- all must have single face in db if (card instanceof SplitCard) { SplitCard splitCard = (SplitCard) card; cardsToAdd.add(new CardInfo(splitCard.getLeftHalfCard())); diff --git a/Mage/src/main/java/mage/constants/SpellAbilityType.java b/Mage/src/main/java/mage/constants/SpellAbilityType.java index 22427d77be4..f32c86fa516 100644 --- a/Mage/src/main/java/mage/constants/SpellAbilityType.java +++ b/Mage/src/main/java/mage/constants/SpellAbilityType.java @@ -1,7 +1,6 @@ package mage.constants; /** - * * @author North */ public enum SpellAbilityType { @@ -13,7 +12,9 @@ public enum SpellAbilityType { SPLIT_FUSED("Split SpellAbility"), SPLIT_LEFT("LeftSplit SpellAbility"), SPLIT_RIGHT("RightSplit SpellAbility"), - MODE("Mode SpellAbility"), + MODAL("Modal SpellAbility"), // used for modal double faces cards + MODAL_LEFT("LeftModal SpellAbility"), + MODAL_RIGHT("RightModal SpellAbility"), SPLICE("Spliced SpellAbility"), ADVENTURE_SPELL("Adventure SpellAbility"); diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 154fc8292a2..48b90a1cf35 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -3,11 +3,8 @@ package mage.constants; import mage.MageObject; import mage.filter.predicate.Predicate; import mage.game.Game; -import mage.util.SubTypeList; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; public enum SubType { @@ -468,6 +465,22 @@ public enum SubType { } } + private static final Set landTypes = new HashSet<>(); + private static final Map> subTypeSetMap = new HashMap<>(); + + static { + for (SubTypeSet subTypeSet : SubTypeSet.values()) { + subTypeSetMap.put( + subTypeSet, + Arrays.stream(values()) + .filter(subType -> subType.getSubTypeSet() == subTypeSet) + .collect(Collectors.toSet()) + ); + } + landTypes.addAll(subTypeSetMap.get(SubTypeSet.BasicLandType)); + landTypes.addAll(subTypeSetMap.get(SubTypeSet.NonBasicLandType)); + } + private final SubTypeSet subTypeSet; private final String description; private final boolean customSet; @@ -534,49 +547,46 @@ public enum SubType { return subTypeSet; } - public static Set getArtifactTypes() { - Set subTypes = EnumSet.noneOf(SubType.class); - for (SubType subType : values()) { - if (subType.getSubTypeSet() == SubTypeSet.ArtifactType) { - subTypes.add(subType); - } + public boolean canGain(MageObject mageObject) { + switch (subTypeSet) { + case CreatureType: + return mageObject.isCreature() || mageObject.isTribal(); + case BasicLandType: + case NonBasicLandType: + return mageObject.isLand(); + case EnchantmentType: + return mageObject.isEnchantment(); + case ArtifactType: + return mageObject.isArtifact(); + case PlaneswalkerType: + return mageObject.isPlaneswalker(); } - return subTypes; + return false; + } + + public static Set getArtifactTypes() { + return subTypeSetMap.get(SubTypeSet.ArtifactType); + } + + public static Set getEnchantmentTypes() { + return subTypeSetMap.get(SubTypeSet.EnchantmentType); } public static Set getPlaneswalkerTypes() { - Set subTypes = EnumSet.noneOf(SubType.class); - for (SubType subType : values()) { - if (subType.getSubTypeSet() == SubTypeSet.PlaneswalkerType) { - subTypes.add(subType); - } - } - return subTypes; + return subTypeSetMap.get(SubTypeSet.PlaneswalkerType); + } public static Set getCreatureTypes() { - Set subTypes = EnumSet.noneOf(SubType.class); - for (SubType subType : values()) { - if (subType.getSubTypeSet() == SubTypeSet.CreatureType) { - subTypes.add(subType); - } - } - return subTypes; + return subTypeSetMap.get(SubTypeSet.CreatureType); + } public static Set getBasicLands() { - return Arrays.stream(values()) - .filter(p -> p.getSubTypeSet() == SubTypeSet.BasicLandType) - .collect(Collectors.toSet()); + return subTypeSetMap.get(SubTypeSet.BasicLandType); } - public static SubTypeList getLandTypes() { - SubTypeList landTypes = new SubTypeList(); - for (SubType subType : values()) { - if (subType.getSubTypeSet() == SubTypeSet.BasicLandType || subType.getSubTypeSet() == SubTypeSet.NonBasicLandType) { - landTypes.add(subType); - } - } + public static Set getLandTypes() { return landTypes; } } diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index 9492c1d1eb6..93c9c571640 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -12,6 +12,7 @@ import mage.game.Game; */ public enum CounterType { + AEGIS("aegis"), AGE("age"), AIM("aim"), ARROW("arrow"), diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/MulticoloredPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/MulticoloredPredicate.java index 58a5937d744..59ab086a664 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/MulticoloredPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/MulticoloredPredicate.java @@ -1,7 +1,7 @@ - package mage.filter.predicate.mageobject; import mage.MageObject; +import mage.cards.ModalDoubleFacesCardHalf; import mage.cards.SplitCardHalf; import mage.constants.Zone; import mage.filter.predicate.Predicate; @@ -18,8 +18,13 @@ public enum MulticoloredPredicate implements Predicate { // 708.3. Each split card that consists of two halves with different colored mana symbols in their mana costs // is a multicolored card while it's not a spell on the stack. While it's a spell on the stack, it's only the // color or colors of the half or halves being cast. # - if (input instanceof SplitCardHalf && game.getState().getZone(input.getId()) != Zone.STACK) { + if (input instanceof SplitCardHalf + && game.getState().getZone(input.getId()) != Zone.STACK) { return 1 < ((SplitCardHalf) input).getMainCard().getColor(game).getColorCount(); + } else if (input instanceof ModalDoubleFacesCardHalf + && (game.getState().getZone(input.getId()) != Zone.STACK && game.getState().getZone(input.getId()) != Zone.BATTLEFIELD)) { + // While a double-faced card isn’t on the stack or battlefield, consider only the characteristics of its front face. + return 1 < ((ModalDoubleFacesCardHalf) input).getMainCard().getColor(game).getColorCount(); } else { return 1 < input.getColor(game).getColorCount(); } diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java index 294f2d18af1..cb89353be36 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java @@ -1,6 +1,7 @@ package mage.filter.predicate.mageobject; import mage.MageObject; +import mage.cards.ModalDoubleFacesCard; import mage.cards.SplitCard; import mage.constants.SpellAbilityType; import mage.filter.predicate.Predicate; @@ -32,10 +33,15 @@ public class NamePredicate implements Predicate { } // If a player names a card, the player may name either half of a split card, but not both. // A split card has the chosen name if one of its two names matches the chosen name. + // Same for modal double faces cards if (input instanceof SplitCard) { return CardUtil.haveSameNames(name, ((SplitCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || CardUtil.haveSameNames(name, ((SplitCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames); + } else if (input instanceof ModalDoubleFacesCard) { + return CardUtil.haveSameNames(name, ((ModalDoubleFacesCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || + CardUtil.haveSameNames(name, ((ModalDoubleFacesCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || + CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames); } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { SplitCard card = (SplitCard) ((Spell) input).getCard(); return CardUtil.haveSameNames(name, card.getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || diff --git a/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java index dc284f62002..33b78bcb933 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java @@ -2,6 +2,7 @@ package mage.filter.predicate.other; import mage.cards.AdventureCard; import mage.cards.Card; +import mage.cards.ModalDoubleFacesCard; import mage.cards.SplitCard; import mage.cards.mock.MockCard; import mage.constants.SubType; @@ -52,6 +53,8 @@ public class CardTextPredicate implements Predicate { String fullName = input.getName(); if (input instanceof MockCard) { fullName = ((MockCard) input).getFullName(true); + } else if (input instanceof ModalDoubleFacesCard) { + fullName = input.getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + ((ModalDoubleFacesCard) input).getRightHalfCard().getName(); } else if (input instanceof AdventureCard) { fullName = input.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + ((AdventureCard) input).getSpellCard().getName(); } @@ -67,14 +70,14 @@ public class CardTextPredicate implements Predicate { } } - //separate by spaces + // separate by spaces String[] tokens = text.toLowerCase(Locale.ENGLISH).split(" "); for (String token : tokens) { boolean found = false; if (!token.isEmpty()) { // then try to find in rules if (inRules) { - if (input.isSplitCard()) { + if (input instanceof SplitCard) { for (String rule : ((SplitCard) input).getLeftHalfCard().getRules(game)) { if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { found = true; @@ -88,6 +91,22 @@ public class CardTextPredicate implements Predicate { } } } + + if (input instanceof ModalDoubleFacesCard) { + for (String rule : ((ModalDoubleFacesCard) input).getLeftHalfCard().getRules(game)) { + if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { + found = true; + break; + } + } + for (String rule : ((ModalDoubleFacesCard) input).getRightHalfCard().getRules(game)) { + if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { + found = true; + break; + } + } + } + if (input instanceof AdventureCard) { for (String rule : ((AdventureCard) input).getSpellCard().getRules(game)) { if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { @@ -96,6 +115,7 @@ public class CardTextPredicate implements Predicate { } } } + for (String rule : input.getRules(game)) { if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { found = true; diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java index a833490e3b0..7ba0e16ec3a 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java @@ -1,21 +1,20 @@ package mage.filter.predicate.permanent; +import mage.MageObject; import mage.filter.predicate.Predicate; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; /** * @author LevelX2 */ -public enum CommanderPredicate implements Predicate { +public enum CommanderPredicate implements Predicate { instance; @Override - public boolean apply(Permanent input, Game game) { - Player owner = game.getPlayer(input.getOwnerId()); - return owner != null - && game.getCommandersIds(owner).contains(input.getId()); + public boolean apply(MageObject input, Game game) { + Player owner = game.getPlayer(game.getOwnerId(input.getId())); + return owner != null && game.getCommandersIds(owner).contains(input.getId()); } @Override diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/EnteredThisTurnPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/EnteredThisTurnPredicate.java index 797af90a259..07b0e6ac778 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/EnteredThisTurnPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/EnteredThisTurnPredicate.java @@ -1,8 +1,3 @@ -/* - * 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.filter.predicate.permanent; import mage.filter.predicate.Predicate; @@ -10,10 +5,10 @@ import mage.game.Game; import mage.game.permanent.Permanent; /** - * * @author LevelX2 */ -public class EnteredThisTurnPredicate implements Predicate { +public enum EnteredThisTurnPredicate implements Predicate { + instance; @Override public boolean apply(Permanent input, Game game) { diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 2486fcfa803..4a558c07282 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -244,17 +244,29 @@ public abstract class GameImpl implements Game, Serializable { card.setOwnerId(ownerId); gameCards.put(card.getId(), card); state.addCard(card); - if (card.isSplitCard()) { + if (card instanceof SplitCard) { + // left Card leftCard = ((SplitCard) card).getLeftHalfCard(); leftCard.setOwnerId(ownerId); gameCards.put(leftCard.getId(), leftCard); state.addCard(leftCard); + // right Card rightCard = ((SplitCard) card).getRightHalfCard(); rightCard.setOwnerId(ownerId); gameCards.put(rightCard.getId(), rightCard); state.addCard(rightCard); - } - if (card instanceof AdventureCard) { + } else if (card instanceof ModalDoubleFacesCard) { + // left + Card leftCard = ((ModalDoubleFacesCard) card).getLeftHalfCard(); + leftCard.setOwnerId(ownerId); + gameCards.put(leftCard.getId(), leftCard); + state.addCard(leftCard); + // right + Card rightCard = ((ModalDoubleFacesCard) card).getRightHalfCard(); + rightCard.setOwnerId(ownerId); + gameCards.put(rightCard.getId(), rightCard); + state.addCard(rightCard); + } else if (card instanceof AdventureCard) { Card spellCard = ((AdventureCard) card).getSpellCard(); spellCard.setOwnerId(ownerId); gameCards.put(spellCard.getId(), spellCard); @@ -1911,7 +1923,7 @@ public abstract class GameImpl implements Game, Serializable { Iterator copiedCards = this.getState().getCopiedCards().iterator(); while (copiedCards.hasNext()) { Card card = copiedCards.next(); - if (card instanceof SplitCardHalf || card instanceof AdventureCardSpell) { + if (card instanceof SplitCardHalf || card instanceof AdventureCardSpell || card instanceof ModalDoubleFacesCardHalf) { continue; // only the main card is moves, not the halves (cause halfes is not copied - it uses original card -- TODO: need to fix (bugs with same card copy)? } Zone zone = state.getZone(card.getId()); diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 0496002adc9..6f406d28f70 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -1,9 +1,5 @@ package mage.game; -import java.io.Serializable; -import java.util.*; -import static java.util.Collections.emptyList; -import java.util.stream.Collectors; import mage.MageObject; import mage.MageObjectReference; import mage.abilities.*; @@ -12,6 +8,7 @@ import mage.abilities.effects.ContinuousEffects; import mage.abilities.effects.Effect; import mage.cards.AdventureCard; import mage.cards.Card; +import mage.cards.ModalDoubleFacesCard; import mage.cards.SplitCard; import mage.constants.Zone; import mage.designations.Designation; @@ -40,6 +37,12 @@ import mage.util.ThreadLocalStringBuilder; import mage.watchers.Watcher; import mage.watchers.Watchers; +import java.io.Serializable; +import java.util.*; +import java.util.stream.Collectors; + +import static java.util.Collections.emptyList; + /** * @author BetaSteward_at_googlemail.com *

@@ -626,6 +629,7 @@ public class GameState implements Serializable, Copyable { // public void addMessage(String message) { // this.messages.add(message); // } + /** * Returns a list of all players of the game ignoring range or if a player * has lost or left the game. @@ -799,7 +803,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(); @@ -835,10 +839,14 @@ public class GameState implements Serializable, Copyable { } // TODO Watchers? // TODO Abilities? - if (card.isSplitCard()) { + if (card instanceof SplitCard) { removeCopiedCard(((SplitCard) card).getLeftHalfCard()); removeCopiedCard(((SplitCard) card).getRightHalfCard()); } + if (card instanceof ModalDoubleFacesCard) { + removeCopiedCard(((ModalDoubleFacesCard) card).getLeftHalfCard()); + removeCopiedCard(((ModalDoubleFacesCard) card).getRightHalfCard()); + } if (card instanceof AdventureCard) { removeCopiedCard(((AdventureCard) card).getSpellCard()); } @@ -1194,21 +1202,34 @@ public class GameState implements Serializable, Copyable { } public Card copyCard(Card cardToCopy, Ability source, Game game) { + // main card Card copiedCard = cardToCopy.copy(); copiedCard.assignNewId(); copiedCard.setOwnerId(source.getControllerId()); copiedCard.setCopy(true, cardToCopy); copiedCards.put(copiedCard.getId(), copiedCard); addCard(copiedCard); - if (copiedCard.isSplitCard()) { + + // other faces + if (copiedCard instanceof SplitCard) { + // left Card leftCard = ((SplitCard) copiedCard).getLeftHalfCard(); // TODO: must be new ID (bugs with same card copy)? copiedCards.put(leftCard.getId(), leftCard); addCard(leftCard); + // right Card rightCard = ((SplitCard) copiedCard).getRightHalfCard(); copiedCards.put(rightCard.getId(), rightCard); addCard(rightCard); - } - if (copiedCard instanceof AdventureCard) { + } else if (copiedCard instanceof ModalDoubleFacesCard) { + // left + Card leftCard = ((ModalDoubleFacesCard) copiedCard).getLeftHalfCard(); // TODO: must be new ID (bugs with same card copy)? + copiedCards.put(leftCard.getId(), leftCard); + addCard(leftCard); + // right + Card rightCard = ((ModalDoubleFacesCard) copiedCard).getRightHalfCard(); + copiedCards.put(rightCard.getId(), rightCard); + addCard(rightCard); + } else if (copiedCard instanceof AdventureCard) { Card spellCard = ((AdventureCard) copiedCard).getSpellCard(); copiedCards.put(spellCard.getId(), spellCard); addCard(spellCard); diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java index 9c3b8775c87..f658f68dbb1 100644 --- a/Mage/src/main/java/mage/game/ZonesHandler.java +++ b/Mage/src/main/java/mage/game/ZonesHandler.java @@ -1,9 +1,7 @@ package mage.game; -import mage.cards.Card; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.cards.MeldCard; +import mage.abilities.keyword.TransformAbility; +import mage.cards.*; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; @@ -19,7 +17,6 @@ import mage.players.Player; import mage.target.TargetCard; import java.util.*; -import mage.abilities.keyword.TransformAbility; /** * Created by samuelsandeen on 9/6/16. @@ -54,7 +51,7 @@ public final class ZonesHandler { public static List moveCards(List zoneChangeInfos, Game game) { // Handle Unmelded Meld Cards - for (ListIterator itr = zoneChangeInfos.listIterator(); itr.hasNext();) { + for (ListIterator itr = zoneChangeInfos.listIterator(); itr.hasNext(); ) { ZoneChangeInfo info = itr.next(); MeldCard card = game.getMeldCard(info.event.getTargetId()); // Copies should be handled as normal cards. @@ -101,42 +98,57 @@ public final class ZonesHandler { ZoneChangeEvent event = info.event; Zone toZone = event.getToZone(); Card targetCard = getTargetCard(game, event.getTargetId()); - Cards cards = null; - // If we're moving a token it shouldn't be put into any zone as an object. + + Cards cardsToMove = null; // moving real cards + Cards cardsToUpdate = null; // updating all card's parts + // if we're moving a token it shouldn't be put into any zone as an object. if (!(targetCard instanceof Permanent) && targetCard != null) { if (targetCard instanceof MeldCard) { - cards = ((MeldCard) targetCard).getHalves(); + // meld/group cards must be independent (use can choose order) + cardsToMove = ((MeldCard) targetCard).getHalves(); + cardsToUpdate = cardsToMove; + } else if (targetCard instanceof ModalDoubleFacesCard + || targetCard instanceof ModalDoubleFacesCardHalf) { + // mdf cards must be moved as single object, but each half must be updated separetly + ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) targetCard.getMainCard(); + cardsToMove = new CardsImpl(mdfCard); + cardsToUpdate = new CardsImpl(mdfCard); + cardsToUpdate.add(mdfCard.getLeftHalfCard()); + cardsToUpdate.add(mdfCard.getRightHalfCard()); } else { - cards = new CardsImpl(targetCard); + cardsToMove = new CardsImpl(targetCard); + cardsToUpdate = cardsToMove; } Player owner = game.getPlayer(targetCard.getOwnerId()); switch (toZone) { case HAND: - for (Card card : cards.getCards(game)) { + for (Card card : cardsToMove.getCards(game)) { game.getPlayer(card.getOwnerId()).getHand().add(card); } break; case GRAVEYARD: for (Card card : chooseOrder( - "order to put in graveyard (last chosen will be on top)", cards, owner, game)) { + "order to put in graveyard (last chosen will be on top)", cardsToMove, owner, game)) { game.getPlayer(card.getOwnerId()).getGraveyard().add(card); } break; case LIBRARY: if (info instanceof ZoneChangeInfo.Library && ((ZoneChangeInfo.Library) info).top) { + // on top for (Card card : chooseOrder( - "order to put on top of library (last chosen will be topmost)", cards, owner, game)) { + "order to put on top of library (last chosen will be topmost)", cardsToMove, owner, game)) { game.getPlayer(card.getOwnerId()).getLibrary().putOnTop(card, game); } - } else { // buttom + } else { + // on bottom for (Card card : chooseOrder( - "order to put on bottom of library (last chosen will be bottommost)", cards, owner, game)) { + "order to put on bottom of library (last chosen will be bottommost)", cardsToMove, owner, game)) { game.getPlayer(card.getOwnerId()).getLibrary().putOnBottom(card, game); } } break; case EXILED: - for (Card card : cards.getCards(game)) { + for (Card card : cardsToMove.getCards(game)) { if (info instanceof ZoneChangeInfo.Exile && ((ZoneChangeInfo.Exile) info).id != null) { ZoneChangeInfo.Exile exileInfo = (ZoneChangeInfo.Exile) info; game.getExile().createZone(exileInfo.id, exileInfo.name).add(card); @@ -147,13 +159,13 @@ public final class ZonesHandler { break; case COMMAND: // There should never be more than one card here. - for (Card card : cards.getCards(game)) { + for (Card card : cardsToMove.getCards(game)) { game.addCommander(new Commander(card)); } break; case STACK: // There should never be more than one card here. - for (Card card : cards.getCards(game)) { + for (Card card : cardsToMove.getCards(game)) { Spell spell; if (info instanceof ZoneChangeInfo.Stack && ((ZoneChangeInfo.Stack) info).spell != null) { spell = ((ZoneChangeInfo.Stack) info).spell; @@ -174,28 +186,39 @@ public final class ZonesHandler { throw new UnsupportedOperationException("to Zone " + toZone.toString() + " not supported yet"); } } + + // update zone in main game.setZone(event.getTargetId(), event.getToZone()); - if (targetCard instanceof MeldCard && cards != null) { + + // update zone in other parts (meld cards, mdf half cards) + if (cardsToUpdate != null) { + for (Card card : cardsToUpdate.getCards(game)) { + if (!card.getId().equals(event.getTargetId())) { + game.setZone(card.getId(), event.getToZone()); + } + } + } + + // reset meld status + if (targetCard instanceof MeldCard) { if (event.getToZone() != Zone.BATTLEFIELD) { ((MeldCard) targetCard).setMelded(false, game); } - for (Card card : cards.getCards(game)) { - game.setZone(card.getId(), event.getToZone()); - } } } public static Card getTargetCard(Game game, UUID targetId) { - if (game.getCard(targetId) != null) { - return game.getCard(targetId); + Card card = game.getCard(targetId); + if (card != null) { + return card; } - if (game.getMeldCard(targetId) != null) { - return game.getMeldCard(targetId); + + card = game.getMeldCard(targetId); + if (card != null) { + return card; } - if (game.getPermanent(targetId) != null) { - return game.getPermanent(targetId); - } - return null; + + return game.getPermanent(targetId); } private static boolean maybeRemoveFromSourceZone(ZoneChangeInfo info, Game game) { @@ -203,7 +226,7 @@ public final class ZonesHandler { if (info instanceof ZoneChangeInfo.Unmelded) { ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info; MeldCard meld = game.getMeldCard(info.event.getTargetId()); - for (Iterator itr = unmelded.subInfo.iterator(); itr.hasNext();) { + for (Iterator itr = unmelded.subInfo.iterator(); itr.hasNext(); ) { ZoneChangeInfo subInfo = itr.next(); if (!maybeRemoveFromSourceZone(subInfo, game)) { itr.remove(); @@ -232,10 +255,10 @@ public final class ZonesHandler { if (info.faceDown) { card.setFaceDown(true, game); } else if (info.event.getToZone().equals(Zone.BATTLEFIELD)) { - if (!card.isPermanent() + if (!card.isPermanent() && (!card.isTransformable() || Boolean.FALSE.equals(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId())))) { // Non permanents (Instants, Sorceries, ... stay in the zone they are if an abilty/effect tries to move it to the battlefield - return false; + return false; } } if (!game.replaceEvent(event)) { @@ -248,9 +271,10 @@ public final class ZonesHandler { Permanent permanent; if (card instanceof MeldCard) { permanent = new PermanentMeld(card, event.getPlayerId(), game); + } else if (card instanceof ModalDoubleFacesCard) { + throw new IllegalStateException("Unexpected trying of move mdf card to battlefield instead half"); } else if (card instanceof Permanent) { - // This should never happen. - permanent = (Permanent) card; + throw new IllegalStateException("Unexpected trying of move permanent to battlefield instead card"); } else { permanent = new PermanentCard(card, event.getPlayerId(), game); } diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 188696f9adc..158da6ffada 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -91,6 +91,7 @@ public class GameEvent implements Serializable { CLASH, CLASHED, DAMAGE_PLAYER, MILL_CARDS, + MILLED_CARD, /* DAMAGED_PLAYER targetId the id of the damaged player sourceId sourceId of the ability which caused the damage @@ -249,13 +250,17 @@ public class GameEvent implements Serializable { SHUFFLE_LIBRARY, LIBRARY_SHUFFLED, ENCHANT_PLAYER, ENCHANTED_PLAYER, CAN_TAKE_MULLIGAN, - FLIP_COIN, COIN_FLIPPED, SCRY, SURVEIL, SURVEILED, FATESEAL, + SCRY, SCRIED, + SURVEIL, SURVEILED, + FATESEALED, + FLIP_COIN, COIN_FLIPPED, ROLL_DICE, DICE_ROLLED, ROLL_PLANAR_DIE, PLANAR_DIE_ROLLED, PLANESWALK, PLANESWALKED, PAID_CUMULATIVE_UPKEEP, DIDNT_PAY_CUMULATIVE_UPKEEP, LIFE_PAID, + CASCADE_LAND, //permanent events ENTERS_THE_BATTLEFIELD_SELF, /* 616.1a If any of the replacement and/or prevention effects are self-replacement effects (see rule 614.15), one of them must be chosen. If not, proceed to rule 616.1b. */ diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 829e75df529..f60c34f77e7 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -264,7 +264,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { // ability hints List abilityHints = new ArrayList<>(); if (HintUtils.ABILITY_HINTS_ENABLE) { - for (Ability ability : abilities) { + for (Ability ability : getAbilities(game)) { for (Hint hint : ability.getHints()) { String s = hint.getText(game, ability); if (s != null && !s.isEmpty()) { @@ -341,7 +341,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return rules; } catch (Exception e) { - return rulesError; + return CardUtil.RULES_ERROR_INFO; } } diff --git a/Mage/src/main/java/mage/game/permanent/token/CribSwapShapeshifterWhiteToken.java b/Mage/src/main/java/mage/game/permanent/token/CribSwapShapeshifterWhiteToken.java index a5314fa3465..5aa82f27423 100644 --- a/Mage/src/main/java/mage/game/permanent/token/CribSwapShapeshifterWhiteToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/CribSwapShapeshifterWhiteToken.java @@ -1,13 +1,11 @@ - - package mage.game.permanent.token; -import mage.constants.CardType; -import mage.constants.SubType; + import mage.MageInt; import mage.abilities.keyword.ChangelingAbility; +import mage.constants.CardType; +import mage.constants.SubType; /** - * * @author spjspj */ public final class CribSwapShapeshifterWhiteToken extends TokenImpl { @@ -19,6 +17,7 @@ public final class CribSwapShapeshifterWhiteToken extends TokenImpl { subtype.add(SubType.SHAPESHIFTER); power = new MageInt(1); toughness = new MageInt(1); + setIsAllCreatureTypes(true); addAbility(ChangelingAbility.getInstance()); } diff --git a/Mage/src/main/java/mage/game/permanent/token/SalamnderWarriorToken.java b/Mage/src/main/java/mage/game/permanent/token/SalamnderWarriorToken.java new file mode 100644 index 00000000000..c03d2fa9ed5 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/SalamnderWarriorToken.java @@ -0,0 +1,29 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class SalamnderWarriorToken extends TokenImpl { + + public SalamnderWarriorToken() { + super("Salamander Warrior", "4/3 blue Salamander Warrior creature token"); + cardType.add(CardType.CREATURE); + color.setBlue(true); + subtype.add(SubType.SALAMANDER); + subtype.add(SubType.WARRIOR); + power = new MageInt(4); + toughness = new MageInt(3); + } + + public SalamnderWarriorToken(final SalamnderWarriorToken token) { + super(token); + } + + public SalamnderWarriorToken copy() { + return new SalamnderWarriorToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/ShapeshifterToken.java b/Mage/src/main/java/mage/game/permanent/token/ShapeshifterToken.java index 7f5d05ce522..dc9edaa5f10 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ShapeshifterToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ShapeshifterToken.java @@ -27,6 +27,7 @@ public final class ShapeshifterToken extends TokenImpl { subtype.add(SubType.SHAPESHIFTER); power = new MageInt(2); toughness = new MageInt(2); + setIsAllCreatureTypes(true); addAbility(ChangelingAbility.getInstance()); } diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index d8342f952c0..a30e8cec7e5 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -178,6 +178,12 @@ public class Spell extends StackObjImpl implements Card { + " as Adventure spell of " + GameLog.getColoredObjectIdName(adventureCard); } + if (card instanceof ModalDoubleFacesCardHalf) { + ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card.getMainCard(); + return GameLog.replaceNameByColoredName(card, getSpellAbility().toString(), mdfCard) + + " as mdf side of " + GameLog.getColoredObjectIdName(mdfCard); + } + return GameLog.replaceNameByColoredName(card, getSpellAbility().toString()); } @@ -247,7 +253,7 @@ public class Spell extends StackObjImpl implements Card { // Otherwise effects like evolve trigger from creature comes into play event card.getCardType().remove(CardType.CREATURE); if (!card.getSubtype(game).contains(SubType.AURA)) { - card.getSubtype(game).add(SubType.AURA); + card.addSubType(game, SubType.AURA); } } UUID permId = null; @@ -271,9 +277,7 @@ public class Spell extends StackObjImpl implements Card { Permanent permanent = game.getPermanent(permId); if (permanent instanceof PermanentCard) { permanent.setSpellAbility(ability); // otherwise spell ability without bestow will be set - if (!card.getCardType().contains(CardType.CREATURE)) { - card.addCardType(CardType.CREATURE); - } + card.addCardType(CardType.CREATURE); card.getSubtype(game).remove(SubType.AURA); } } @@ -714,11 +718,6 @@ public class Spell extends StackObjImpl implements Card { return null; } - @Override - public boolean isSplitCard() { - return false; - } - @Override public boolean isTransformable() { return false; diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index c1faf4b7f91..efed0eb9528 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1309,7 +1309,14 @@ public abstract class PlayerImpl implements Player, Serializable { card.getId(), card.getId(), playerId, activationStatus.getApprovingObject()); landEventAfter.setZone(cardZoneBefore); game.fireEvent(landEventAfter); - game.fireInformEvent(getLogName() + " plays " + card.getLogName()); + + String playText = getLogName() + " plays " + card.getLogName(); + if (card instanceof ModalDoubleFacesCardHalf) { + ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card.getMainCard(); + playText = getLogName() + " plays " + GameLog.replaceNameByColoredName(card, card.getName(), mdfCard) + + " as MDF side of " + GameLog.getColoredObjectIdName(mdfCard); + } + game.fireInformEvent(playText); // game.removeBookmark(bookmark); resetStoredBookmark(game); // prevent undo after playing a land return true; @@ -1602,6 +1609,10 @@ public abstract class PlayerImpl implements Player, Serializable { needId1 = object.getId(); needId2 = ((SplitCard) object).getLeftHalfCard().getId(); needId3 = ((SplitCard) object).getRightHalfCard().getId(); + } else if (object instanceof ModalDoubleFacesCard) { + needId1 = object.getId(); + needId2 = ((ModalDoubleFacesCard) object).getLeftHalfCard().getId(); + needId3 = ((ModalDoubleFacesCard) object).getRightHalfCard().getId(); } else if (object instanceof AdventureCard) { needId1 = object.getId(); needId2 = ((AdventureCard) object).getMainCard().getId(); @@ -3402,10 +3413,16 @@ public abstract class PlayerImpl implements Player, Serializable { // BASIC abilities if (object instanceof SplitCard) { - SplitCard splitCard = (SplitCard) object; - getPlayableFromObjectSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(game), availableMana, output); - getPlayableFromObjectSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(game), availableMana, output); - getPlayableFromObjectSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output); + SplitCard mainCard = (SplitCard) object; + getPlayableFromObjectSingle(game, fromZone, mainCard.getLeftHalfCard(), mainCard.getLeftHalfCard().getAbilities(game), availableMana, output); + getPlayableFromObjectSingle(game, fromZone, mainCard.getRightHalfCard(), mainCard.getRightHalfCard().getAbilities(game), availableMana, output); + getPlayableFromObjectSingle(game, fromZone, mainCard, mainCard.getSharedAbilities(game), availableMana, output); + } + if (object instanceof ModalDoubleFacesCard) { + ModalDoubleFacesCard mainCard = (ModalDoubleFacesCard) object; + getPlayableFromObjectSingle(game, fromZone, mainCard.getLeftHalfCard(), mainCard.getLeftHalfCard().getAbilities(game), availableMana, output); + getPlayableFromObjectSingle(game, fromZone, mainCard.getRightHalfCard(), mainCard.getRightHalfCard().getAbilities(game), availableMana, output); + getPlayableFromObjectSingle(game, fromZone, mainCard, mainCard.getSharedAbilities(game), availableMana, output); } else if (object instanceof AdventureCard) { // adventure must use different card characteristics for different spells (main or adventure) AdventureCard adventureCard = (AdventureCard) object; @@ -4414,6 +4431,9 @@ public abstract class PlayerImpl implements Player, Serializable { } Cards cards = new CardsImpl(this.getLibrary().getTopCards(game, event.getAmount())); this.moveCards(cards, Zone.GRAVEYARD, source, game); + for (Card card : cards.getCards(game)) { + game.fireEvent(GameEvent.getEvent(EventType.MILLED_CARD, card.getId(), source.getSourceId(), getId())); + } return cards; } @@ -4525,9 +4545,14 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean scry(int value, Ability source, Game game) { - game.informPlayers(getLogName() + " scries " + value); + GameEvent event = new GameEvent(EventType.SCRY, getId(), source == null + ? null : source.getSourceId(), getId(), value, true); + if (game.replaceEvent(event)) { + return false; + } + game.informPlayers(getLogName() + " scries " + event.getAmount()); Cards cards = new CardsImpl(); - cards.addAll(getLibrary().getTopCards(game, value)); + cards.addAll(getLibrary().getTopCards(game, event.getAmount())); if (!cards.isEmpty()) { TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("card" + (cards.size() == 1 ? "" : "s") @@ -4537,8 +4562,8 @@ public abstract class PlayerImpl implements Player, Serializable { cards.removeAll(target.getTargets()); putCardsOnTopOfLibrary(cards, game, source, true); } - game.fireEvent(new GameEvent(GameEvent.EventType.SCRY, getId(), source == null - ? null : source.getSourceId(), getId(), value, true)); + game.fireEvent(new GameEvent(GameEvent.EventType.SCRIED, getId(), source == null + ? null : source.getSourceId(), getId(), event.getAmount(), true)); return true; } diff --git a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java index 855cb085166..921dea995d9 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java @@ -1,8 +1,8 @@ package mage.target.common; import mage.abilities.dynamicvalue.DynamicValue; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; /** * @author North @@ -17,11 +17,11 @@ public class TargetCreaturePermanentAmount extends TargetPermanentAmount { this(amount, StaticFilters.FILTER_PERMANENT_CREATURE); } - public TargetCreaturePermanentAmount(int amount, FilterCreaturePermanent filter) { + public TargetCreaturePermanentAmount(int amount, FilterPermanent filter) { super(amount, filter); } - public TargetCreaturePermanentAmount(DynamicValue amount, FilterCreaturePermanent filter) { + public TargetCreaturePermanentAmount(DynamicValue amount, FilterPermanent filter) { super(amount, filter); } diff --git a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java index 7807cb601c3..feedd0a4db8 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java +++ b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java @@ -37,7 +37,7 @@ public class TargetCreaturePermanentWithDifferentTypes extends TargetCreaturePer Permanent selectedCreature = game.getPermanent(targetId); if (selectedCreature != null && !creature.getId().equals(selectedCreature.getId())) { - if (creature.shareSubtypes(selectedCreature, game)) { + if (creature.shareCreatureTypes(selectedCreature, game)) { return false; } } diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index ae1289cee1e..bae18c8d378 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1,11 +1,6 @@ package mage.util; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.stream.Collectors; +import com.google.common.collect.ImmutableList; import mage.MageObject; import mage.Mana; import mage.abilities.Abilities; @@ -15,8 +10,12 @@ import mage.abilities.SpellAbility; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.*; import mage.abilities.effects.ContinuousEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.HintUtils; import mage.cards.Card; import mage.cards.MeldCard; +import mage.cards.ModalDoubleFacesCard; +import mage.cards.SplitCard; import mage.constants.*; import mage.filter.Filter; import mage.filter.predicate.mageobject.NamePredicate; @@ -30,19 +29,31 @@ import mage.game.stack.Spell; import mage.players.Player; import mage.target.Target; import mage.util.functions.CopyTokenFunction; +import org.apache.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; /** * @author nantuko */ public final class CardUtil { + private static final Logger logger = Logger.getLogger(CardUtil.class); + + public static final List RULES_ERROR_INFO = ImmutableList.of("Exception occurred in rules generation"); + private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone"; static final String[] numberStrings = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", - "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"}; + "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"}; static final String[] ordinalStrings = {"first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eightth", "ninth", - "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth"}; + "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth"}; public static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); @@ -877,4 +888,71 @@ public final class CardUtil { } } } + + /** + * Return card name for same name searching + * + * @param card + * @return + */ + public static String getCardNameForSameNameSearch(Card card) { + // it's ok to return one name only cause NamePredicate can find same card by first name + if (card instanceof SplitCard) { + return ((SplitCard) card).getLeftHalfCard().getName(); + } else if (card instanceof ModalDoubleFacesCard) { + return ((ModalDoubleFacesCard) card).getLeftHalfCard().getName(); + } else { + return card.getName(); + } + } + + public static List getCardRulesWithAdditionalInfo(UUID cardId, String cardName, + Abilities rulesSource, Abilities hintAbilities) { + return getCardRulesWithAdditionalInfo(null, cardId, cardName, rulesSource, hintAbilities); + } + + /** + * Prepare rules list from abilities + * + * @param rulesSource abilities list to show as rules + * @param hintsSource abilities list to show as card hints only (you can add additional hints here; exameple: from second or transformed side) + */ + public static List getCardRulesWithAdditionalInfo(Game game, UUID cardId, String cardName, + Abilities rulesSource, Abilities hintsSource) { + try { + List rules = rulesSource.getRules(cardName); + + if (game != null) { + + // debug state + for (String data : game.getState().getCardState(cardId).getInfo().values()) { + rules.add(data); + } + + // ability hints + List abilityHints = new ArrayList<>(); + if (HintUtils.ABILITY_HINTS_ENABLE) { + for (Ability ability : hintsSource) { + for (Hint hint : ability.getHints()) { + String s = hint.getText(game, ability); + if (s != null && !s.isEmpty()) { + abilityHints.add(s); + } + } + } + } + + // restrict hints only for permanents, not cards + // total hints + if (!abilityHints.isEmpty()) { + rules.add(HintUtils.HINT_START_MARK); + HintUtils.appendHints(rules, abilityHints); + } + } + return rules; + } catch (Exception e) { + logger.error("Exception in rules generation for card: " + cardName, e); + } + return RULES_ERROR_INFO; + } } diff --git a/Mage/src/main/java/mage/util/functions/AddSubtypeApplier.java b/Mage/src/main/java/mage/util/functions/AddSubtypeApplier.java index 6f5c35e93dd..0d762ff8657 100644 --- a/Mage/src/main/java/mage/util/functions/AddSubtypeApplier.java +++ b/Mage/src/main/java/mage/util/functions/AddSubtypeApplier.java @@ -28,7 +28,7 @@ public class AddSubtypeApplier extends ApplyToPermanent { @Override public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { if (!permanent.hasSubtype(subtype, game)) { - permanent.getSubtype(game).add(subtype); + permanent.addSubType(game, subtype); } return true; } @@ -36,7 +36,7 @@ public class AddSubtypeApplier extends ApplyToPermanent { @Override public boolean apply(Game game, MageObject mageObject, Ability source, UUID copyToObjectId) { if (!mageObject.hasSubtype(subtype, game)) { - mageObject.getSubtype(game).add(subtype); + mageObject.addSubType(game, subtype); } return true; } diff --git a/Mage/src/main/java/mage/watchers/common/ProwlWatcher.java b/Mage/src/main/java/mage/watchers/common/ProwlWatcher.java index d83b3ed0dc2..0f89823500b 100644 --- a/Mage/src/main/java/mage/watchers/common/ProwlWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/ProwlWatcher.java @@ -1,7 +1,5 @@ - package mage.watchers.common; -import mage.abilities.keyword.ChangelingAbility; import mage.constants.SubType; import mage.constants.WatcherScope; import mage.game.Game; @@ -30,22 +28,24 @@ public class ProwlWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == EventType.DAMAGED_PLAYER) { - DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event; - if (dEvent.isCombatDamage()) { - Permanent creature = game.getPermanent(dEvent.getSourceId()); - if (creature != null && !allSubtypes.contains(creature.getControllerId())) { - if (creature.getAbilities().containsKey(ChangelingAbility.getInstance().getId()) || creature.isAllCreatureTypes()) { - allSubtypes.add(creature.getControllerId()); - } else { - Set subtypes = damagingSubtypes.getOrDefault(creature.getControllerId(), new LinkedHashSet<>()); - - subtypes.addAll(creature.getSubtype(game)); - damagingSubtypes.put(creature.getControllerId(), subtypes); - } - } - } + if (event.getType() != EventType.DAMAGED_PLAYER) { + return; } + DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event; + if (!dEvent.isCombatDamage()) { + return; + } + Permanent creature = game.getPermanent(dEvent.getSourceId()); + if (creature == null || allSubtypes.contains(creature.getControllerId())) { + return; + } + if (creature.isAllCreatureTypes()) { + allSubtypes.add(creature.getControllerId()); + return; + } + damagingSubtypes + .computeIfAbsent(creature.getControllerId(), m -> new LinkedHashSet<>()) + .addAll(creature.getSubtype(game)); } @Override diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 6fa584b5c07..6abeed78efc 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -39300,47 +39300,196 @@ Roil Eruption|Zendikar Rising|389|C|{1}{R}|Sorcery|||Kicker {5}$Roil Eruption de Roiling Regrowth|Zendikar Rising|390|U|{2}{G}|Instant|||Sacrifice a land. Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library.| Kargan Warleader|Zendikar Rising|391|U|{1}{R}{W}|Creature - Human Warrior|3|3|Other Warriors you control get +1/+1.| The Prismatic Piper|Commander Legends|1|C|{5}|Legendary Creature - Shapeshifter|3|3|If The Prismatic Piper is your commander, choose a color before the game begins. The Prismatic Piper is the chosen color.$Partner| +Ancestral Blade|Commander Legends|5|C|{1}{W}|Artifact - Equipment|||When Ancestral Blade enters the battlefield, create a 1/1 white Soldier creature token, then attach Ancestral Blade to it.$Equipped creature gets +1/+1.$Equip {1}| +Archon of Coronation|Commander Legends|9|M|{4}{W}{W}|Creature - Archon|5|5|Flying$When Archon of Coronation enters the battlefield, you become the monarch.$As long as you're the monarch, damage doesn't cause you to lose life.| +Ardenn, Intrepid Archaeologist|Commander Legends|10|U|{2}{W}|Legendary Creature - Kor Scout|2|2|At the beginning of combat on your turn, you may attach any number of Auras and Equipment you control to target permanent or player.$Partner| +Austere Command|Commander Legends|12|R|{4}{W}{W}|Sorcery|||Choose two —$• Destroy all artifacts.$• Destroy all enchantments.$• Destroy all creatures with converted mana cost 3 or less.$• Destroy all creatures with converted mana cost 4 or greater.| +Cage of Hands|Commander Legends|14|C|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature can't attack or block.${1}{W}: Return Cage of Hands to its owner's hand.| +Captain's Call|Commander Legends|15|C|{3}{W}|Sorcery|||Create three 1/1 white Soldier creature tokens.| +Court of Grace|Commander Legends|16|R|{2}{W}{W}|Enchantment|||When Court of Grace enters the battlefield, you become the monarch.$At the beginning of your upkeep, create a 1/1 white Spirit creature token with flying. If you're the monarch, create a 4/4 white Angel creature token with flying instead.| +Court Street Denizen|Commander Legends|17|C|{2}{W}|Creature - Human Soldier|2|2|Whenever another white creature enters the battlefield under your control, tap target creature an opponent controls.| +Doomed Traveler|Commander Legends|19|C|{W}|Creature - Human Soldier|1|1|When Doomed Traveler dies, create a 1/1 white Spirit creature token with flying.| +First Response|Commander Legends|22|U|{3}{W}|Enchantment|||At the beginning of each upkeep, if you lost life last turn, create a 1/1 white Soldier creature token.| +Inspiring Roar|Commander Legends|23|C|{3}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.| +Intangible Virtue|Commander Legends|24|U|{1}{W}|Enchantment|||Creature tokens you control get +1/+1 and have vigilance.| Keeper of the Accord|Commander Legends|27|R|{3}{W}|Creature - Human Soldier|3|4|At the beginning of each opponent's end step, if that player controls more creatures than you, create a 1/1 white Soldier creature token.$At the beginning of each opponent's end step, if that player controls more lands than you, you may search your library for a basic Plains card, put it onto the battlefield tapped, then shuffle your library.| +Keleth, Sunmane Familiar|Commander Legends|28|U|{1}{W}|Legendary Creature - Horse|1|1|Whenever a commander you control attacks, put a +1/+1 counter on it.$Partner| +Kinsbaile Courier|Commander Legends|29|C|{2}{W}|Creature - Kithkin Soldier|2|1|When Kinsbaile Courier enters the battlefield, put a +1/+1 counter on target creature.$Encore {2}{W}| +Kor Cartographer|Commander Legends|30|C|{3}{W}|Creature - Kor Scout|2|2|When Kor Cartographer enters the battlefield, you may search your library for a Plains card, put it onto the battlefield tapped, then shuffle your library.| +Livio, Oathsworn Sentinel|Commander Legends|31|R|{1}{W}|Legendary Creature - Human Knight|2|2|{1}{W}: Choose another target creature. Its controller may exile it with an aegis counter on it.${2}{W}, {T}: Return all exiled cards with aegis counters on them to the battlefield under their owners' control.$Partner| +Ninth Bridge Patrol|Commander Legends|33|C|{1}{W}|Creature - Dwarf Soldier|1|1|Whenever another creature you control leaves the battlefield, put a +1/+1 counter on Ninth Bridge Patrol.| +Patron of the Valiant|Commander Legends|37|U|{3}{W}{W}|Creature - Angel|4|4|Flying$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.| +Prava of the Steel Legion|Commander Legends|38|U|{2}{W}|Legendary Creature - Cat Soldier|1|4|As long as it's your turn, creature tokens you control get +1/+4.${3}{W}: Create a 1/1 white Soldier creature token.$Partner| +Promise of Tomorrow|Commander Legends|39|R|{2}{W}|Enchantment|||Whenever a creature you control dies, exile it.$At the beginning of each end step, if you control no creatures, sacrifice Promise of Tomorrow and return all cards exiled with it to the battlefield under your control.| +Radiant, Serra Archangel|Commander Legends|40|U|{6}{W}|Legendary Creature - Angel|6|4|Flying$Tap another untapped creature you control with flying: Radiant, Serra Archangel gains protection from the color of your choice until end of turn.$Partner| Raise the Alarm|Commander Legends|41|C|{1}{W}|Instant|||Create two 1/1 white Soldier creature tokens.| Rebbec, Architect of Ascension|Commander Legends|42|U|{3}{W}|Legendary Creature - Human Artificer|3|4|Artifacts you control have protection from each converted mana cost among artifacts you control.$Partner| +Return to Dust|Commander Legends|43|U|{2}{W}{W}|Instant|||Exile target artifact or enchantment. If you cast this spell during your main phase, you may exile up to one other target artifact or enchantment.| Seraphic Greatsword|Commander Legends|45|M|{1}{W}|Artifact - Equipment|||Equipped creature gets +2/+2.$Whenever equipped creature attacks the player with the most life or tied for most life, create a 4/4 white Angel creature token with flying that's tapped and attacking that player.$Equip {4}| +Slash the Ranks|Commander Legends|47|R|{3}{W}{W}|Sorcery|||Destroy all creatures and planeswalkers except for commanders.| +Vow of Duty|Commander Legends|54|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2, has vigilance, and can't attack you or a planeswalker you control.| +Amphin Mutineer|Commander Legends|55|R|{3}{U}|Creature - Salamander Pirate|3|3|When Amphin Mutineer enters the battlefield, exile up to one target non-Salamander creature. That creature's controller creates a 4/3 blue Salamander Warrior creature token.$Encore {4}{U}{U}| Brinelin, the Moon Kraken|Commander Legends|60|U|{6}{U}{U}|Legendary Creature - Kraken|6|8|When Brinelin, the Moon Kraken enters the battlefield or whenever you cast a spell with converted mana cost 6 or greater, you may return target nonland permanent to its owner's hand.$Partner| +Flood of Recollection|Commander Legends|61|U|{U}{U}|Sorcery|||Return target instant or sorcery card from your graveyard to your hand. Exile Flood of Recollection.| Confiscate|Commander Legends|62|U|{4}{U}{U}|Enchantment - Aura|||Enchant permanent$You control enchanted permanent.| +Court of Cunning|Commander Legends|63|R|{1}{U}{U}|Enchantment|||When Court of Cunning enters the battlefield, you become the monarch.$At the beginning of your upkeep, any number of target players each mill two cards. If you're the monarch, each of those players mills ten cards instead.| +Eligeth, Crossroads Augur|Commander Legends|66|R|{4}{U}{U}|Legendary Creature - Sphinx|5|6|Flying$If you would scry a number of cards, draw that many cards instead.$Partner| +Fall from Favor|Commander Legends|68|C|{2}{U}|Enchantment - Aura|||Enchant creature$When Fall from Favor enters the battlefield, tap enchanted creature and you become the monarch.$Enchanted creature doesn't untap during its controller's untap step unless that player is the monarch.| +Forceful Denial|Commander Legends|69|C|{3}{U}{U}|Instant|||Cascade$Counter target spell.| +Ghost of Ramirez DePietro|Commander Legends|71|U|{2}{U}|Legendary Creature - Pirate Spirit|2|3|Ghost of Ramirez DePietro can't be blocked by creatures with toughness 3 or greater.$Whenever Ghost of Ramirez DePietro deals combat damage to a player, choose up to one target card in a graveyard that was discarded or put there from a library this turn. Put that card into its owner's hand.$Partner| Horizon Scholar|Commander Legends|73|U|{5}{U}|Creature - Sphinx|4|4|Flying$When Horizon Scholar enters the battlefield, scry 2.| Interpret the Signs|Commander Legends|75|U|{5}{U}|Sorcery|||Scry 3, then reveal the top card of your library. Draw cards equal to that card's converted mana cost.| +Kitesail Corsair|Commander Legends|76|C|{1}{U}|Creature - Human Pirate|2|1|Kitesail Corsair has flying as long as it's attacking.| +Kitesail Skirmisher|Commander Legends|77|C|{3}{U}|Creature - Human Pirate|3|1|Flying$Whenever Kitesail Skirmisher attacks, another target creature attacking the same player or planeswalker gains flying until end of turn.$Encore {4}{U}| +Laboratory Drudge|Commander Legends|78|R|{3}{U}|Creature - Zombie Horror|3|4|At the beginning of each end step, draw a card if you've cast a spell from a graveyard or activated an ability of a card in a graveyard this turn.| +Malcolm, Keen-Eyed Navigator|Commander Legends|79|U|{2}{U}|Legendary Creature - Siren Pirate|2|2|Flying$Whenever one or more Pirates you control deal damage to your opponents, you create a Treasure token for each opponent dealt damage.$Partner| +Merchant Raiders|Commander Legends|81|U|{3}{U}|Creature - Human Pirate|2|4|Whenever Merchant Raiders or another Pirate enters the battlefield under your control, tap up to one target creature. That creature doesn't untap during its controller's untap step for as long as you control Merchant Raiders.| Mnemonic Deluge|Commander Legends|82|M|{6}{U}{U}{U}|Sorcery|||Exile target instant or sorcery card from a graveyard. Copy that card three times. You may cast the copies without paying their mana costs. Exile Mnemonic Deluge.| +Omenspeaker|Commander Legends|83|C|{1}{U}|Creature - Human Wizard|1|3|When Omenspeaker enters the battlefield, scry 2.| +Preordain|Commander Legends|84|C|{U}|Sorcery|||Scry 2, then draw a card.| Prying Eyes|Commander Legends|86|C|{4}{U}{U}|Instant|||Draw four cards, then discard two cards.| +Run Away Together|Commander Legends|87|C|{1}{U}|Instant|||Choose two target creatures controlled by different players. Return those creatures to their owners' hands.| Siani, Eye of the Storm|Commander Legends|95|U|{3}{U}|Legendary Creature - Djinn Monk|3|2|Flying$Whenever Siani, Eye of the Storm attacks, scry X, where X is the number of attacking creatures with flying.$Partner| Siren Stormtamer|Commander Legends|96|U|{U}|Creature - Siren Pirate Wizard|1|1|Flying${U}, Sacrifice Siren Stormtamer: Counter target spell or ability that targets you or a creature you control.| Sphinx of the Second Sun|Commander Legends|99|M|{6}{U}{U}|Creature - Sphinx|6|6|Flying$At the beginning of your postcombat main phase, you get an additional beginning phase after this phase.| Supreme Will|Commander Legends|102|U|{2}{U}|Instant|||Choose one —$• Counter target spell unless its controller pays {3}.$• Look at the top four cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order.| +Vow of Flight|Commander Legends|105|U|{2}{U}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2, has flying, and can't attack you or a planeswalker you control.| Warden of Evos Isle|Commander Legends|106|U|{2}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.| +Wrong Turn|Commander Legends|107|R|{2}{U}|Instant|||Target opponent gains control of target creature.| +Armix, Filigree Thrasher|Commander Legends|108|U|{2}{B}|Legendary Artifact Creature - Golem|3|2|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.$Partner| +Bitter Revelation|Commander Legends|109|C|{3}{B}|Sorcery|||Look at the top four cards of your library. Put two of them into your hand and the rest into your graveyard. You lose 2 life.| Briarblade Adept|Commander Legends|111|C|{4}{B}|Creature - Elf Assassin|3|4|Whenever Briarblade Adept attacks, target creature an opponent controls get -1/-1 until end of turn.$Encore {3}{B}| +Cast Down|Commander Legends|112|U|{1}{B}|Instant|||Destroy target nonlegendary creature.| +Court of Ambition|Commander Legends|114|R|{2}{B}{B}|Enchantment|||When Court of Ambition enters the battlefield, you become the monarch.$At the beginning of your upkeep, each opponent loses 3 life unless they discard a card. If you're the monarch, instead each opponent loses 6 life unless they discard two cards.| +Cuombajj Witches|Commander Legends|116|U|{B}{B}|Creature - Human Wizard|1|3|{T}: Cuombajj Witches deals 1 damage to any target and 1 damage to any target of an opponent's choice.| +Demonic Lore|Commander Legends|118|U|{2}{B}|Enchantment|||When Demonic Lore enters the battlefield, draw three cards.$At the beginning of your end step, you lose 2 life for each card in your hand.| +Exquisite Huntmaster|Commander Legends|122|C|{3}{B}|Creature - Elf Warrior|4|2|When Exquisite Huntmaster dies, create a 1/1 green Elf Warrior creature token.$Encore {4}{B}| +Eyeblight Cullers|Commander Legends|124|C|{4}{B}|Creature - Elf Warrior|3|3|When Eyeblight Cullers dies, create three 1/1 green Elf Warrior creature tokens, then mill three cards.| +Fleshbag Marauder|Commander Legends|128|C|{2}{B}|Creature - Zombie Warrior|3|1|When Fleshbag Marauder enters the battlefield, each player sacrifices a creature.| +Keskit, the Flesh Sculptor|Commander Legends|131|U|{2}{B}|Legendary Creature - Human Artificer|1|3|{T}, Sacrifice three other artifacts and/or creatures: Look at the top three cards of your library. Put two of them into your hand and the other into your graveyard.$Partner| +Null Caller|Commander Legends|140|U|{3}{B}|Creature - Vampire Shaman|2|4|{3}{B}, Exile a creature card from your graveyard: Create a tapped 2/2 black Zombie creature token.| +Opposition Agent|Commander Legends|141|R|{2}{B}|Creature - Human Rogue|3|2|Flash$You control your opponents while they're searching their libraries.$While an opponent is searching their library, they exile each card they find. You may play those cards for as long as they remain exiled, and you may spend mana as though it were mana of any color to cast them.| +Phyrexian Rager|Commander Legends|142|C|{2}{B}|Creature - Horror|2|2|When Phyrexian Rager enters the battlefield, you draw a card and you lose 1 life.| +Plague Reaver|Commander Legends|143|R|{2}{B}|Creature - Beast|6|5|At the beginning of your end step, sacrifice each other creature you control.$Discard two cards, Sacrifice Plague Reaver: Choose target opponent. Return Plague Reaver to the battlefield under that player's control at the beginning of their next upkeep.| +Profane Transfusion|Commander Legends|145|M|{6}{B}{B}{B}|Sorcery|||Two target players exchange life totals. You create an X/X colorless Horror artifact creature token, where X is the difference between those players' life totals.| Sengir, the Dark Baron|Commander Legends|149|R|{4}{B}{B}|Legendary Creature - Vampire Noble|4|4|Flying$Whenever another creature dies, put two +1/+1 counters on Sengir, the Dark Baron.$Whenever another player loses the game, you gain life equal to that player's life total as the turn began.$Partner| Szat's Will|Commander Legends|152|R|{4}{B}|Instant|||Choose one. If you control a commander as you cast this spell, you may choose both.$• Each opponent sacrifices a creature they control with the greatest power.$• Exile all cards from all opponents' graveyards, then create X 0/1 black Thrull creature tokens, where X is the greatest power among creature cards exiled this way.| -Tevesh Szat, Doom of Fools|Commander Legends|153|M|{4}{B}|Legendary Planeswalker - Szat|4|+2: Create two 0/1 black Thrull creature tokens.$+1: You may sacrifice another creature or planeswalker. If you do, draw two cards, then draw another card if the sacrificed permanent was a commander.$−10: Gain control of all commanders. Put all commanders from the commander zone onto the battlefield under your control.$Tevesh Szat, Doom of Fools can be your commander.$Partner| +Tevesh Szat, Doom of Fools|Commander Legends|153|M|{4}{B}|Legendary Planeswalker - Szat|4|+2: Create two 0/1 black Thrull creature tokens.$+1: You may sacrifice another creature or planeswalker. If you do, draw two cards, then draw another card if the sacrificed permanent was a commander.$−10: Gain control of all commanders. Put all commanders from the command zone onto the battlefield under your control.$Tevesh Szat, Doom of Fools can be your commander.$Partner| Thorn of the Black Rose|Commander Legends|154|C|{3}{B}|Creature - Human Assassin|1|3|Deathtouch$When Thorn of the Black Rose enters the battlefield, you become the monarch.| Vampiric Tutor|Commander Legends|156|M|{B}|Instant|||Search your library for a card, then shuffle your library and put that card on top of it. You lose 2 life.| +Vow of Torment|Commander Legends|159|U|{2}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2, has menace, and can't attack you or a planeswalker you control.| Alena, Kessig Trapper|Commander Legends|160|U|{4}{R}|Legendary Creature - Human Scout|4|3|First strike${T}: Add an amount of {R} equal to the greatest power among creatures you control that entered the battlefield this turn.$Partner| +Aurora Phoenix|Commander Legends|161|R|{4}{R}{R}|Creature - Phoenix|5|3|Flying$Cascade$Whenever you cast a spell with cascade, return Aurora Phoenix from your graveyard to your hand.| +Blasphemous Act|Commander Legends|162|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.| +Brazen Freebooter|Commander Legends|164|C|{3}{R}|Creature - Human Pirate|3|3|When Brazen Freebooter enters the battlefield, create a Treasure token.| +Breeches, Brazen Plunderer|Commander Legends|165|U|{3}{R}|Legendary Creature - Goblin Pirate|3|3|Menace$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.$Partner| +Coastline Marauders|Commander Legends|168|U|{2}{R}|Creature - Human Pirate|0|3|Trample$Whenever Coastline Marauders attacks, it gets +1/+0 until end of turn for each land defending player controls.$Encore {4}{R}{R}| +Dargo, the Shipwrecker|Commander Legends|172|U|{6}{R}|Legendary Creature - Giant Pirate|7|5|As an additional cost to cast this spell, you may sacrifice any number of artifacts and/or creatures. This spell costs {2} less to cast for each permanent sacrificed this way and {2} less to cast for each other artifact or creature you've sacrificed this turn.$Trample$Partner| +Dragon Mantle|Commander Legends|174|C|{R}|Enchantment - Aura|||Enchant creature$When Dragon Mantle enters the battlefield, draw a card.$Enchanted creature has "{R}: This creature gets +1/+0 until end of turn."| +Emberwilde Captain|Commander Legends|175|R|{3}{R}|Creature - Djinn Pirate|4|2|When Emberwilde Captain enters the battlefield, you become the monarch.$Whenever an opponent attacks you while you're the monarch, Emberwilde Captain deals damage to that player equal to the number of cards in their hand.| +Furnace Celebration|Commander Legends|181|U|{1}{R}{R}|Enchantment|||Whenever you sacrifice another permanent, you may pay {2}. If you do, Furnace Celebration deals 2 damage to any target.| +Jeska, Thrice Reborn|Commander Legends|186|M|{2}{R}|Legendary Planeswalker - Jeska|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.$+0: Choose target creature. Until your next turn, if that creature would deal combat damage to one of your opponents, it deals triple that damage to that player instead.$−X: Jeska, Thrice Reborn deals X damage to each of up to three targets.$Jeska, Thrice Reborn can be your commander.$Partner| +Jeska's Will|Commander Legends|187|R|{2}{R}|Sorcery|||Choose one. If you control a commander as you cast this spell, you may choose both.$• Add {R} for each card in target opponent's hand.$• Exile the top three cards of your library. You may play them this turn.| +Kediss, Emberclaw Familiar|Commander Legends|188|U|{1}{R}|Legendary Creature - Elemental Lizard|1|1|Whenever a commander you control deals combat damage to an opponent, it deals that much damage to each other opponent.$Partner| Krark, the Thumbless|Commander Legends|189|R|{1}{R}|Legendary Creature - Goblin Wizard|2|2|Whenever you cast an instant or sorcery spell, flip a coin. If you lose the flip, return that spell to its owner's hand. If you win the flip, copy that spell, and you may choose new targets for the copy.$Partner| +Makeshift Munitions|Commander Legends|191|C|{1}{R}|Enchantment|||{1}, Sacrifice an artifact or creature: Makeshift Munitions deals 1 damage to any target.| +Meteoric Mace|Commander Legends|192|U|{4}{R}{R}|Artifact - Equipment|||Equipped creature gets +4/+0 and has trample.$Equip {4}$Cascade| +Port Razer|Commander Legends|193|M|{3}{R}{R}|Creature - Orc Pirate|4|4|Whenever Port Razer deals combat damage to a player, untap each creature you control. After this combat phase, there is an additional combat phase.$Port Razer can't attack a player it has already attacked this turn.| +Rograkh, Son of Rohgahh|Commander Legends|197|U|{0}|Legendary Creature - Kobold Warrior|0|1|First strike, menace, trample$Partner| +Soul's Fire|Commander Legends|200|C|{2}{R}|Instant|||Target creature you control deals damage equal to its power to any target.| +Soulfire Eruption|Commander Legends|201|M|{6}{R}{R}{R}|Sorcery|||Choose any amount of target creatures, planeswalkers, or players. For each one, exile the top card of your library, then Soulfire Eruption deals damage to that target equal to the exiled card's converted mana cost. You may play cards exiled this way until the end of the your next turn.| +Valakut Invoker|Commander Legends|206|C|{2}{R}|Creature - Human Shaman|2|3|{8}: Valakut Invoker deals 3 damage to any target.| +Vow of Lightning|Commander Legends|209|U|{2}{R}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2, has first strike, and can't attack you or a planeswalker you control.| +Wild Celebrants|Commander Legends|212|C|{3}{R}{R}|Creature - Satyr|5|3|When Wild Celebrants enters the battlefield, you may destroy target artifact.| +Ancient Animus|Commander Legends|215|C|{1}{G}|Instant|||Put a +1/+1 counter on target creature you control if it's legendary. Then it fights target creature an opponent controls.| +Apex Devastator|Commander Legends|217|M|{8}{G}{G}|Creature - Hydra Chimera|10|10|Cascade, cascade, cascade, cascade.| +Court of Bounty|Commander Legends|220|R|{2}{G}{G}|Enchantment|||When Court of Bounty enters the battlefield, you become the monarch.$At the beginning of your upkeep, you may put a land card from your hand onto the battlefield. If you're the monarch, instead you may put a creature or land card from your hand onto the battlefield.| +Entourage of Trest|Commander Legends|224|C|{4}{G}|Creature - Elf Soldier|4|4|When Entourage of Trest enters the battlefield, you become the monarch.$Entourage of Trest can block an additional creature each combat as long as you're the monarch.| +Farhaven Elf|Commander Legends|225|C|{2}{G}|Creature - Elf Druid|1|1|When Farhaven Elf enters the battlefield, you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.| +Fyndhorn Elves|Commander Legends|228|C|{G}|Creature - Elf Druid|1|1|{T}: Add {G}.| +Gift of Paradise|Commander Legends|229|C|{2}{G}|Enchantment - Aura|||Enchant land$When Gift of Paradise enters the battlefield, you gain 3 life.$Enchanted land has "{T}: Add two mana of any one color."| Halana, Kessig Ranger|Commander Legends|231|U|{3}{G}|Legendary Creature - Human Archer|3|4|Reach$Whenever another creature enters the battlefield under your control, you may pay {2}. When you do, that creature deals damage equal to its power to target creature.$Partner| +Hunter's Insight|Commander Legends|232|U|{2}{G}|Instant|||Choose target creature you control. Whenever that creature deals combat damage to a player or planeswalker this turn, draw that many cards.| +Immaculate Magistrate|Commander Legends|234|R|{3}{G}|Creature - Elf Shaman|2|2|{T}: Put a +1/+1 counter on target creature for each Elf you control.| +Imperious Perfect|Commander Legends|235|U|{2}{G}|Creature - Elf Warrior|2|2|Other Elf creatures you control get +1/+1.${G}, {T}: Create a 1/1 green Elf Warrior creature token.| +Kamahl, Heart of Krosa|Commander Legends|237|M|{6}{G}{G}|Legendary Creature - Human Druid|5|5|At the beginning of combat on your turn, creatures you control get +3/+3 and gain trample until end of turn.${1}{G}: Until end of turn, target land you control becomes a 1/1 Elemental creature with vigilance, indestructible, and haste. It's still a land.$Partner| +Kamahl's Will|Commander Legends|238|R|{3}{G}|Instant|||Choose one. If you control a commander as you cast this spell, you may choose both.$• Until end of turn, any number of target lands you control become 1/1 Elemental creatures with vigilance, indestructible, and haste. They're still lands.$• Choose target creature you don't control. Each creature you control deals damage equal to its power to that creature.| +Kodama of the East Tree|Commander Legends|239|R|{4}{G}{G}|Legendary Creature - Spirit|6|6|Reach$Whenever another permanent enters the battlefield under your control, if it wasn't put onto the battlefield with this ability, you may put a permanent card with equal or lesser converted mana cost from your hand onto the battlefield.$Partner| +Magus of the Order|Commander Legends|242|R|{2}{G}{G}|Creature - Human Wizard|3|3|{G}, {T}, Sacrifice Magus of the Order and another green creature: Search your library for a green creature card and put it onto the battlefield. Then shuffle your library.| +Natural Reclamation|Commander Legends|245|C|{4}{G}|Instant|||Cascade$Destroy target artifact or enchantment.| +Numa, Joraga Chieftain|Commander Legends|246|U|{2}{G}|Legendary Creature - Elf Warrior|2|2|At the beginning of combat on your turn, you may pay {X}{X}. When you do, distribute X +1/+1 counters among any number of target Elves.$Partner| +Rootweaver Druid|Commander Legends|250|R|{2}{G}|Creature - Elf Druid|2|1|When Rootweaver Druid etners the battlefield, each opponent may search their library for up to three basic land cards. They each put one of those cards onto the battlefield tapped under your control and the rest onto the battlefield tapped under their control. Then each player who searched their library this way shuffles it.| +Soul's Might|Commander Legends|257|C|{4}{G}|Sorcery|||Put X +1/+1 counters on target creature, where X is that creature's power.| +Sweet-Gum Recluse|Commander Legends|260|R|{4}{G}{G}|Creature - Spider|0|3|Flash$Cascade$Reach$When Sweet-Gum Recluse enters the battlefield, put three +1/+1 counters on each of any number of target creatures that entered the battlefield this turn.| +Three Visits|Commander Legends|261|U|{1}{G}|Sorcery|||Search your library for a Forest card and put that card onto the battlefield. Then shuffle your library.| +Vow of Wildness|Commander Legends|262|U|{2}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3, has trample, and can't attack you or a planeswalker you control.| +Amareth, the Lustrous|Commander Legends|266|R|{3}{G}{U}{W}|Legendary Creature - Dragon|6|6|Flying$Whenever another permanent enters the battlefield under your control, look at the top card of your library. If it shares a card type with that permanent, you may reveal that card and put it into your hand.| +Archelos, Lagoon Mystic|Commander Legends|268|R|{1}{B}{G}{U}|Legendary Creature - Turtle Shaman|2|4|As long as Archelos, Lagoon Mystic is tapped, other permanents enter the battlefield tapped.$As long as Archelos, Lagoon Mystic is untapped, other permanents enter the battlefield untapped.| +Averna, the Chaos Bloom|Commander Legends|269|R|{G}{U}{R}|Legendary Creature - Elemental Shaman|4|2|As you cascade, you may put a land card from among the exiled cards onto the battlefield tapped.| +Belbe, Corrupted Observer|Commander Legends|270|R|{B}{G}|Legendary Creature - Zombie Elf|2|2|At the beginning of each player's postcombat main phase, that player adds {C}{C} for each of your opponents who lost life this turn.| +Blim, Comedic Genius|Commander Legends|272|R|{2}{B}{R}|Legendary Creature - Imp|4|3|Flying$Whenever Blim, Comedic Genius deals combat damage to a player, that player gains control of target permanent you control. Then each player loses life and discards cards equal to the number of permanents they control but don't own.| +Colfenor, the Last Yew|Commander Legends|274|R|{3}{W}{B}{G}|Legendary Creature - Treefolk Shaman|3|7|Vigilance, reach$Whenever Colfenor, the Last Yew or another creature you control dies, return up to one other target creature card with lesser toughness from your graveyard to your hand.| +Ghen, Arcanum Weaver|Commander Legends|275|R|{R}{W}{B}|Legendary Creature - Human Wizard|2|3|{R}{W}{B}, {T}, Sacrifice an enchantment: Return target enchantment card from your graveyard to the battlefield.| +Gnostro, Voice of the Crags|Commander Legends|276|R|{1}{U}{R}{W}|Legendary Creature - Chimera|3|3|{T}: Choose one. X is the number of spells you've cast this turn.$• Scry X.$• Gnostro, Voice of the Crags deals X damage to target creature.$• You gain X life.| +Gor Muldrak, Amphinologist|Commander Legends|277|R|{1}{G}{U}|Legendary Creature - Human Scout|3|2|You and permanents you control have protection from Salamanders.$At the beginning of your end step, each player who controls the fewest creatures creates a 4/3 blue Salamander Warrior creature token.| +Hans Eriksson|Commander Legends|279|R|{2}{R}{G}|Legendary Creature - Human Scout|1|4|Whenever Hans Eriksson attacks, reveal the top card of your library. If it's a creature card, put it onto the battlefield tapped and attacking defending player or a planeswalker they control. Otherwise, put that card into your hand. When you put a creature card onto the battlefield this way, it fights Hans Eriksson.| Liesa, Shroud of Dusk|Commander Legends|286|R|{2}{W}{W}{B}|Legendary Creature - Angel|5|5|Rather than pay {2} for each previous time you've cast this spell from the command zone this game, pay 2 life that many times.$Flying, lifelink$Whenever a player casts a spell, they lose 2 life.| Nymris, Oona's Trickster|Commander Legends|288|R|{3}{U}{B}|Legendary Creature - Faerie Knight|1|6|Flash$Flying$Whenever you cast your first spell during each opponent's turn, look at the top two cards of your library. Put one of those cards into your hand and the other into your graveyard.| +Obeka, Brute Chronologist|Commander Legends|289|R|{1}{U}{B}{R}|Legendary Creature - Ogre Wizard|3|4|{T}: The player whose turn it is may end the turn.| +Thalisse, Reverent Medium|Commander Legends|291|U|{3}{W}{B}|Legendary Creature - Human Cleric|3|4|At the beginning of each end step, create X 1/1 white Spirit creature tokens with flying, where X is the number of tokens you created this turn.| +Yurlok of Scorch Thrash|Commander Legends|293|R|{1}{B}{R}{G}|Legendary Creature - Viashino Shaman|4|4|Vigilance$A player losing unspent mana causes that player to lose that much life.${1}, {T}: Each player adds {B}{R}{G}.| +Zara, Renegade Recruiter|Commander Legends|294|R|{3}{U}{R}|Legendary Creature - Human Pirate|4|3|Flying$Whenever Zara, Renegade Recruiter attacks, look at defending player's hand. You may put a creature card from it onto the battlefield under your control tapped and attacking that palyer or a planeswalker they control. Return that creature to its owner's hand at the beginning of the next end step.| +Arcane Signet|Commander Legends|297|U|{2}|Artifact|||{T}: Add one mana of any color in your commander's color identity.| +Armillary Sphere|Commander Legends|298|C|{2}|Artifact|||{2}, {T}, Sacrifice Armillary Sphere: Search your library for up to two basic land cards, reveal them, put them into your hand, then shuffle your library.| Bladegriff Prototype|Commander Legends|300|R|{5}|Artifact Creature - Griffin|3|2|Flying$Whenever Bladegriff Prototype deals combat damage to a player, destroy target nonland permanent of that player's choice that one of your opponents controls.| +Charcoal Diamond|Commander Legends|303|C|{2}|Artifact|||Charcoal Diamond enters the battlefield tapped.${T}: Add {B}.| Commander's Sphere|Commander Legends|306|C|{3}|Artifact|||{T}: Add one mana of any color in your commander's color identity.$Sacrifice Commander's Sphere: Draw a card.| +Horizon Stone|Commander Legends|315|R|{5}|Artifact|||If you would lose unspent mana, that mana becomes colorless instead.| +Jeweled Lotus|Commander Legends|319|M|{0}|Artifact|||{T}, Sacrifice Jeweled Lotus: Add three mana of any one color. Spend this mana only to cast your commander.| Maelstrom Colossus|Commander Legends|322|C|{8}|Artifact Creature - Golem|7|7|Cascade| Marble Diamond|Commander Legends|323|C|{2}|Artifact|||Marble Diamond enters the battlefield tapped.${T}: Add {W}.| Mask of Memory|Commander Legends|324|U|{2}|Artifact - Equipment|||Whenever equipped creature deals combat damage to a player, you may draw two cards. If you do, discard a card.$Equip {1}| Mindless Automaton|Commander Legends|326|U|{4}|Artifact Creature - Construct|0|0|Mindless Automaton enters the battlefield with two +1/+1 counters on it.${1}, Discard a card: Put a +1/+1 counter on Mindless Automaton.$Remove two +1/+1 counters from Mindless Automaton: Draw a card.| +Perilous Myr|Commander Legends|330|C|{2}|Artifact Creature - Myr|1|1|When Perilous Myr dies, it deals 2 damage to any target.| Phyrexian Triniform|Commander Legends|331|M|{9}|Artifact Creature - Golem|9|9|When Phyrexian Triniform dies, create three 3/3 colorless Golem artifact creature tokens.$Encore {12}| +Prophetic Prism|Commander Legends|334|C|{2}|Artifact|||When Prophetic Prism enters the battlefield, draw a card.${1}, {T}: Add one mana of any color.| +Rings of Brighthearth|Commander Legends|335|R|{3}|Artifact|||Whenever you activate an ability, if it isn't a mana ability, you may pay {2}. If you do, copy that ability. You may choose new targets for the copy.| +Sandstone Oracle|Commander Legends|336|U|{7}|Artifact Creature - Sphinx|4|4|Flying$When Sandstone Oracle enters the battlefield, choose an opponent. If that player has more cards in hand than you, draw cards equal to the difference.| +Scroll Rack|Commander Legends|337|M|{2}|Artifact|||{1}, {T}: Exile any number of cards from your hand face down. Put that many cards from the top of your library into your hand. Then look at the exiled cards and put them on top of your library in any order.| +Sky Diamond|Commander Legends|341|C|{2}|Artifact|||Sky Diamond enters the battlefield tapped.${T}: Add {U}.| +Staff of Domination|Commander Legends|343|R|{3}|Artifact|||{1}: Untap Staff of Domination.${2}, {T}: You gain 1 life.${3}, {T}: Untap target creature.${4}, {T}: Tap target creature.${5}, {T}: Draw a card.| +Staunch Throneguard|Commander Legends|344|C|{5}|Artifact Creature - Construct|2|5|Vigilance$When Staunch Throneguard enters the battlefield, you become the monarch.| +Command Beacon|Commander Legends|349|R||Land|||{T}: Add {C}.${T}, Sacrifice Command Beacon: Put your commander into your hand from the command zone.| Command Tower|Commander Legends|350|C||Land|||{T}: Add one mana of any color in your commander's color identity.| +Path of Ancestry|Commander Legends|353|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.| Rejuvenating Springs|Commander Legends|354|R||Land|||Rejuvenating Springs enters the battlefield tapped unless you have two or more opponents.${T}: Add {G} or {U}.| Spectator Seating|Commander Legends|356|R||Land|||Spectator Seating enters the battlefield tapped unless you have two or more opponents.${T}: Add {R} or {W}.| Training Center|Commander Legends|358|R||Land|||Training Center enters the battlefield tapped unless you have two or more opponents.${T}: Add {U} or {R}.| Undergrowth Stadium|Commander Legends|359|R||Land|||Undergrowth Stadium enters the battlefield tapped unless you have two or more opponents.${T}: Add {B} or {G}.| Vault of Champions|Commander Legends|360|R||Land|||Vault of Champions enters the battlefield tapped unless you have two or more opponents.${T}: Add {W} or {B}.| +War Room|Commander Legends|361|R||Land|||{T}: Add {C}.${3}, {T}, Pay life equal to the number of colors in your commanders' color identity: Draw a card.| +Mulldrifter|Commander Legends|400|U|{4}{U}|Creature - Elemental|2|2|Flying$When Mulldrifter enters the battlefield, draw two cards.$Evoke {2}{U}| +Acidic Slime|Commander Legends|421|U|{3}{G}{G}|Creature - Ooze|2|2|Deathtouch$When Acidic Slime enters the battlefield, destroy target artifact, enchantment, or land.| +Myriad Landscape|Commander Legends|487|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 your library.| +Reliquary Tower|Commander Legends|488|U||Land|||You have no maximum hand size.${T}: Add {C}.| +Najeela, the Blade-Blossom|Commander Legends|514|M|{2}{R}|Legendary Creature - Human Warrior|3|2|Whenever a Warrior attacks, you may have its controller create a 1/1 white Warrior creature token that's tapped and attacking.${W}{U}{B}{R}{G}: Untap all attacking creatures. They gain trample, lifelink, and haste until end of turn. After this phase, there is an additional combat phase. Activate this ability only during combat.| +Brago, King Eternal|Commander Legends|516|M|{2}{W}{U}|Legendary Creature - Spirit Noble|2|4|Flying$Whenever Brago, King Eternal deals combat damage to a player, exile any number of target nonland permanents you control, then return those cards to the battlefield under their owner's control.| +Ikra Shidiqi, the Usurper|Commander Legends|519|M|{3}{B}{G}|Legendary Creature - Naga Wizard|3|7|Menace$Whenever a creature you control deals combat damage to a player, you gain life equal to that creature's toughness.$Partner| +Kydele, Chosen of Kruphix|Commander Legends|524|M|{2}{G}{U}|Legendary Creature - Human Wizard|2|3|{T}: Add {C} for each card you've drawn this turn.$Partner| +Ludevic, Necro-Alchemist|Commander Legends|525|M|{1}{U}{R}|Legendary Creature - Human Wizard|1|4|At the beginning of each player's end step, that player may draw a card if a player other than you lost life this turn.$Partner| +Maelstrom Wanderer|Commander Legends|526|M|{5}{G}{U}{R}|Legendary Creature - Elemental|7|5|Creatures you control have haste.$Cascade, cascade| +Nekusar, the Mindrazer|Commander Legends|529|M|{2}{U}{B}{R}|Legendary Creature - Zombie Wizard|2|4|At the beginning of each player's draw step, that player draws an additional card.$Whenever an opponent draws a card, Nekusar, the Mindrazer deals 1 damage to that player.| Prossh, Skyraider of Kher|Commander Legends|530|M|{3}{B}{R}{G}|Legendary Creature - Dragon|5|5|When you cast this spell, create X 0/1 red Kobold creature tokens named Kobolds of Kher Keep, where X is the amount of mana spent to cast Prossh.$Flying$Sacrifice another creature: Prossh, Skyraider of Kher gets +1/+0 until end of turn.| -Radiant, Serra Archangel|Commander Legends|533|U|{6}{W}|Legendary Creature - Angel|6|4|Flying$Tap another untapped creature you control with flying: Radiant, Serra Archangel gains protection from the color of your choice until end of turn.$Partner| +Queen Marchesa|Commander Legends|531|M|{1}{R}{W}{B}|Legendary Creature - Human Assassin|3|3|Deathtouch, haste$When Queen Marchesa enters the battlefield, you become the monarch.$At the beginning of your upkeep, if an opponent is the monarch, create a 1/1 black Assassin creature token with deathtouch and haste.| +Rakdos, Lord of Riots|Commander Legends|532|M|{B}{B}{R}{R}|Legendary Creature - Demon|6|6|You can't cast this spell unless an opponent lost life this turn.$Flying, trample$Creature spells you cast cost {1} less to cast for each 1 life your opponents have lost this turn.| +Ravos, Soultender|Commander Legends|533|M|{3}{W}{B}|Legendary Creature - Human Cleric|2|2|Flying$Other creatures you control get +1/+1.$At the beginning of your upkeep, you may return target creature card from your graveyard to your hand.$Partner| +Sidar Kondo of Jamuraa|Commander Legends|535|M|{2}{G}{W}|Legendary Creature - Human Knight|2|5|Flanking$Creatures your opponents control without flying or reach can't block creatures with power 2 or less.$Partner| +Silas Renn, Seeker Adept|Commander Legends|536|M|{1}{U}{B}|Legendary Artifact Creature - Human|2|2|Deathtouch$Whenever Silas Renn, Seeker Adept deals combat damage to a player, choose target artifact card in your graveyard. You may cast that card this turn.$Partner| +Thrasios, Triton Hero|Commander Legends|538|M|{G}{U}|Legendary Creature - Merfolk Wizard|1|3|{4}: Scry 1, then reveal the top card of your library. If it's a land card, put it onto the battlefield tapped. Otherwise, draw a card.$Partner| +Tymna the Weaver|Commander Legends|539|M|{1}{W}{B}|Legendary Creature - Human Cleric|2|2|Lifelink$At the beginning of your postcombat main phase, you may pay X life, where X is the number of opponents that were dealt combat damage this turn. If you do, draw X cards.$Partner| Vial Smasher the Fierce|Commander Legends|540|M|{1}{B}{R}|Legendary Creature - Goblin Berserker|2|3|Whenever you cast your first spell each turn, choose an opponent at random. Vial Smasher the Fierce deals damage equal to that spell's converted mana cost to that player or a planeswalker that player controls.$Partner| Xenagos, God of Revels|Commander Legends|541|M|{3}{R}{G}|Legendary Enchantment Creature - God|6|5|Indestructible$As long as your devotion to red and green is less than seven, Xenagos isn't a creature.$At the beginning of combat on your turn, another target creature you control gains haste and gets +X/+X until end of turn, where X is that creature's power.| +Zur the Enchanter|Commander Legends|544|M|{1}{W}{U}{B}|Legendary Creature - Human Wizard|1|4|Flying$Whenever Zur the Enchanter attacks, you may search your library for an enchantment card with converted mana cost 3 or less and put it onto the battlefield. If you do, shuffle your library.| +Ramos, Dragon Engine|Commander Legends|545|M|{6}|Legendary Artifact Creature - Dragon|4|4|Flying$Whenever you cast a spell, put a +1/+1 counter on Ramos, Dragon Engine for each of that spell's colors.$Remove five +1/+1 counters from Ramos: Add {W}{W}{U}{U}{B}{B}{R}{R}{G}{G}. Activate this ability only once each turn.| +Armored Skyhunter|Commander Legends|617|R|{3}{W}|Creature - Cat Knight|3|3|Flying$Whenever Armored Skyhunter attacks, look at the top six cards of your library. You may put an Aura or Equipment card from among them onto the battlefield. If an Equipment is put onto the battlefield this way, you may attach it to a creature you control. Put the rest of those cards on the bottom of your library in a random order.| Mana Confluence|Commander Legends|721|M||Land|||{T}, Pay 1 life: Add one mana of any color.|