Added modal double faces cards implementation (MDF cards, #7012)

This commit is contained in:
Oleg Agafonov 2020-09-30 04:02:33 +04:00
parent bbed5a16b8
commit 8ac78b4b9e
60 changed files with 1128 additions and 764 deletions

View file

@ -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
*/
@ -95,6 +96,8 @@ public class CardView extends SimpleCardView {
protected List<String> rightSplitRules;
protected String rightSplitTypeLine;
protected boolean isModalDoubleFacesCard;
protected ArtRect artRect = ArtRect.NORMAL;
protected List<UUID> 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());
@ -455,6 +470,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

View file

@ -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<Integer> 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;
});
}
/**

View file

@ -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);
}
}

View file

@ -1,32 +1,38 @@
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}");
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;
super(ownerId, setInfo,
new CardType[]{CardType.CREATURE}, new SubType[]{SubType.MINOTAUR, SubType.WARRIOR}, "{5}{R}",
"Akoum Teeth", new CardType[]{CardType.LAND}, new SubType[]{}, ""
);
// Akoum Warrior
// Creature Minotaur Warrior
this.getLeftHalfCard().setPT(new MageInt(4), new MageInt(5));
// Trample
this.addAbility(TrampleAbility.getInstance());
this.getLeftHalfCard().addAbility(TrampleAbility.getInstance());
// Akoum Teeth
// Akoum Teeth enters the battlefield tapped.
this.getRightHalfCard().addAbility(new EntersBattlefieldTappedAbility());
// {T}: Add {R}.
this.getRightHalfCard().addAbility(new RedManaAbility());
}
private AkoumWarrior(final AkoumWarrior card) {

View file

@ -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);

View file

@ -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;
}

View file

@ -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<MageObject> {
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);

View file

@ -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<MageObject> {
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;

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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<StackObject> iterator = game.getStack().iterator(); iterator.hasNext();) {
for (Iterator<StackObject> 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.");
}
}
}

View file

@ -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);

View file

@ -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 {

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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<MageObject> {
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

View file

@ -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));

View file

@ -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) {

View file

@ -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<NamePredicate> 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) {

View file

@ -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;
}

View file

@ -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()) {

View file

@ -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));

View file

@ -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);
}
}
}

View file

@ -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;
}

View file

@ -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));

View file

@ -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);
}
}

View file

@ -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<MageObject> {
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;

View file

@ -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<String> 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<String> 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,30 +31,20 @@ 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));
@ -144,48 +55,32 @@ public final class ZendikarRising extends ExpansionSet {
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("Bloodchief's Thirst", 94, Rarity.UNCOMMON, mage.cards.b.BloodchiefsThirst.class));
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("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,71 +89,47 @@ 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("Grakmaw, Skyclave Ravager", 223, Rarity.RARE, mage.cards.g.GrakmawSkyclaveRavager.class));
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("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));
@ -266,19 +137,13 @@ public final class ZendikarRising extends ExpansionSet {
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));
@ -287,39 +152,26 @@ public final class ZendikarRising extends ExpansionSet {
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("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 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("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 +179,41 @@ 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("Phylath, World Sculptor", 234, Rarity.RARE, mage.cards.p.PhylathWorldSculptor.class));
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("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 +221,116 @@ 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 Stormcaller", 77, Rarity.MYTHIC, mage.cards.s.SeaGateStormcaller.class));
cards.add(new SetCardInfo("Sea Gate, Reborn", 76, Rarity.MYTHIC, mage.cards.s.SeaGateReborn.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 Apparition", 39, Rarity.RARE, mage.cards.s.SkyclaveApparition.class));
cards.add(new SetCardInfo("Skyclave Basilica", 40, Rarity.UNCOMMON, mage.cards.s.SkyclaveBasilica.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 Exploration", 175, Rarity.RARE, mage.cards.v.ValakutExploration.class));
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("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("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 Bloodbog", 132, Rarity.UNCOMMON, mage.cards.z.ZofBloodbog.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);
}
}

View file

@ -0,0 +1,162 @@
package org.mage.test.cards.cost.modaldoublefaces;
import mage.constants.PhaseStep;
import mage.constants.Zone;
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();
}
}

View file

@ -257,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);
}
@ -1351,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);

View file

@ -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());

View file

@ -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<ReplacementEffect, Set<Ability>> getApplicableReplacementEffects(GameEvent event, Game game) {
Map<ReplacementEffect, Set<Ability>> 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<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext();) {
for (Iterator<ReplacementEffect> 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<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext();) {
for (Iterator<PreventionEffect> 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<AsThoughEffect> 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<String, String> 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<ReplacementEffect, Set<Ability>> rEffects = getApplicableReplacementEffects(event, game);
// Remove all consumed effects (ability dependant)
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext();) {
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext(); ) {
ReplacementEffect entry = it1.next();
if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9.
Set<UUID> 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<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) {
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<ContinuousEffect, Set<UUID>> entry = iterator.next();
if (!appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
continue;

View file

@ -8,6 +8,7 @@ import mage.abilities.condition.common.SourceIsSpellCondition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.cards.Card;
import mage.cards.ModalDoubleFacesCardHalf;
import mage.cards.SplitCardHalf;
import mage.constants.*;
import mage.filter.FilterCard;
@ -21,7 +22,7 @@ import java.util.UUID;
public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImpl {
private final AlternativeCostSourceAbility alternativeCastingCostAbility;
public CastFromHandWithoutPayingManaCostEffect() {
this(StaticFilters.FILTER_CARDS_NON_LAND, true);
}
@ -37,7 +38,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" : "")
@ -87,9 +88,9 @@ enum IsBeingCastFromHandCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
MageObject object = game.getObject(source.getSourceId());
if (object instanceof SplitCardHalf) {
UUID splitCardId = ((Card) object).getMainCard().getId();
object = game.getObject(splitCardId);
if (object instanceof SplitCardHalf || 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;

View file

@ -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
*
* <p>
* TODO: Implement once we get details on the comprehensive rules meaning of the
* ability
*
* <p>
* 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) {

View file

@ -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();
}

View file

@ -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;

View file

@ -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<Ability> 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
* <ul>
* <li>LIBRARY: <ul><li>true - put on top</li><li>false - put on
* bottom</li></ul></li>
* <li>BATTLEFIELD: <ul><li>true - tapped</li><li>false -
* untapped</li></ul></li>
* <li>GRAVEYARD: <ul><li>true - not from Battlefield</li><li>false - from
* Battlefield</li></ul></li>
* </ul>
* @param flag If zone
* <ul>
* <li>LIBRARY: <ul><li>true - put on top</li><li>false - put on
* bottom</li></ul></li>
* <li>BATTLEFIELD: <ul><li>true - tapped</li><li>false -
* untapped</li></ul></li>
* <li>GRAVEYARD: <ul><li>true - not from Battlefield</li><li>false - from
* Battlefield</li></ul></li>
* </ul>
* @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);

View file

@ -57,7 +57,6 @@ 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
@ -139,7 +138,6 @@ 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;
@ -563,13 +561,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 +694,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 +729,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
return flipCardName;
}
@Override
public boolean isSplitCard() {
return splitCard;
}
@Override
public boolean getUsesVariousArt() {
return usesVariousArt;

View file

@ -0,0 +1,184 @@
package mage.cards;
import mage.MageObject;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.constants.CardType;
import mage.constants.SpellAbilityType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @author JayDi85
*/
public abstract class ModalDoubleFacesCard extends CardImpl {
protected Card leftHalfCard;
protected Card rightHalfCard;
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);
this.modalDFC = true;
}
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<UUID> appliedEffects) {
if (super.moveToZone(toZone, sourceId, game, flag, appliedEffects)) {
game.getState().setZone(getLeftHalfCard().getId(), toZone);
game.getState().setZone(getRightHalfCard().getId(), toZone);
return true;
}
return false;
}
@Override
public void setZone(Zone zone, Game game) {
super.setZone(zone, game);
game.setZone(getLeftHalfCard().getId(), zone);
game.setZone(getRightHalfCard().getId(), zone);
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
if (super.moveToExile(exileId, name, sourceId, game, appliedEffects)) {
Zone currentZone = game.getState().getZone(getId());
game.getState().setZone(getLeftHalfCard().getId(), currentZone);
game.getState().setZone(getRightHalfCard().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);
getLeftHalfCard().updateZoneChangeCounter(game, event);
getRightHalfCard().updateZoneChangeCounter(game, event);
}
@Override
public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
switch (ability.getSpellAbilityType()) {
case MODAL_LEFT:
return this.getLeftHalfCard().cast(game, fromZone, ability, controllerId);
case MODAL_RIGHT:
return this.getRightHalfCard().cast(game, fromZone, ability, controllerId);
default:
this.getLeftHalfCard().getSpellAbility().setControllerId(controllerId);
this.getRightHalfCard().getSpellAbility().setControllerId(controllerId);
return super.cast(game, fromZone, ability, controllerId);
}
}
@Override
public Abilities<Ability> getAbilities() {
Abilities<Ability> allAbilites = new AbilitiesImpl<>();
allAbilites.addAll(super.getAbilities());
allAbilites.addAll(leftHalfCard.getAbilities());
allAbilites.addAll(rightHalfCard.getAbilities());
return allAbilites;
}
public Abilities<Ability> getSharedAbilities(Game game) {
return super.getAbilities(game);
}
@Override
public Abilities<Ability> getAbilities(Game game) {
Abilities<Ability> 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));
allAbilites.addAll(rightHalfCard.getAbilities(game));
return allAbilites;
}
@Override
public List<String> getRules() {
return new ArrayList<>();
}
@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 int getConvertedManaCost() {
// Rules:
// The converted mana cost of a modal double-faced card is based on the characteristics of the
// face thats 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 getLeftHalfCard().getConvertedManaCost();
}
}

View file

@ -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);
}

View file

@ -0,0 +1,101 @@
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.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.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<UUID> appliedEffects) {
return parentCard.moveToZone(toZone, sourceId, game, flag, appliedEffects);
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> 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;
}
}

View file

@ -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();
}
}

View file

@ -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 {

View file

@ -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;

View file

@ -20,6 +20,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 +31,7 @@ public class MockCard extends CardImpl {
protected ManaCosts<ManaCost> manaCostLeft;
protected ManaCosts<ManaCost> manaCostRight;
protected String adventureSpellName;
protected String modalDoubleFacesSecondSideName;
public MockCard(CardInfo card) {
super(null, card.getName());
@ -53,7 +55,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,6 +67,10 @@ public class MockCard extends CardImpl {
this.adventureSpellName = card.getAdventureSpellName();
}
if (card.isModalDoubleFacesCard()) {
this.modalDoubleFacesSecondSideName = card.getModalDoubleFacesSecondSideName();
}
if (this.isPlaneswalker()) {
String startingLoyaltyString = card.getStartingLoyalty();
if (startingLoyaltyString.isEmpty()) {
@ -117,8 +122,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 (modalDoubleFacesSecondSideName != null) {
return getName() + MODAL_DOUBLE_FACES_NAME_SEPARATOR + modalDoubleFacesSecondSideName;
} else {
return getName();
}

View file

@ -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();

View file

@ -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,10 @@ public class CardInfo {
protected boolean adventureCard;
@DatabaseField
protected String adventureSpellName;
@DatabaseField
protected boolean modalDoubleFacesCard;
@DatabaseField
protected String modalDoubleFacesSecondSideName;
public enum ManaCostSide {
LEFT, RIGHT, ALL
@ -121,7 +126,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 +145,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 +163,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<String> manaCostLeft = ((SplitCard) card).getLeftHalfCard().getManaCost().getSymbols();
List<String> manaCostRight = ((SplitCard) card).getRightHalfCard().getManaCost().getSymbols();
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
} else if (card instanceof ModalDoubleFacesCard) {
List<String> manaCostLeft = ((ModalDoubleFacesCard) card).getLeftHalfCard().getManaCost().getSymbols();
List<String> manaCostRight = ((ModalDoubleFacesCard) card).getRightHalfCard().getManaCost().getSymbols();
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
} else if (card instanceof AdventureCard) {
List<String> manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols(); // Spell from left like MTGA
List<String> manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols();
List<String> manaCostRight = card.getManaCost().getSymbols();
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
} else {
@ -181,6 +195,19 @@ public class CardInfo {
length += rule.length();
rulesList.add(rule);
}
} else if (card instanceof ModalDoubleFacesCard) {
for (String rule : ((ModalDoubleFacesCard) card).getLeftHalfCard().getRules()) {
length += rule.length();
rulesList.add(rule);
}
for (String rule : ((ModalDoubleFacesCard) card).getRightHalfCard().getRules()) {
length += rule.length();
rulesList.add(rule);
}
for (String rule : card.getRules()) {
length += rule.length();
rulesList.add(rule);
}
} else {
for (String rule : card.getRules()) {
length += rule.length();
@ -222,7 +249,6 @@ public class CardInfo {
}
}
if (this.startingLoyalty == null) {
//Logger.getLogger(CardInfo.class).warn("Planeswalker `" + card.getName() + "` missing starting loyalty");
this.startingLoyalty = "";
}
} else {
@ -447,4 +473,12 @@ public class CardInfo {
public String getAdventureSpellName() {
return adventureSpellName;
}
public boolean isModalDoubleFacesCard() {
return modalDoubleFacesCard;
}
public String getModalDoubleFacesSecondSideName() {
return modalDoubleFacesSecondSideName;
}
}

View file

@ -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<CardInfo, Object> cardDao;
private Set<String> classNames;
private final RepositoryEventSource eventSource = new RepositoryEventSource();

View file

@ -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()));

View file

@ -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");

View file

@ -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<MageObject> {
// 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 isnt 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();
}

View file

@ -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<MageObject> {
}
// 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) ||

View file

@ -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<Card> {
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<Card> {
}
}
//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<Card> {
}
}
}
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<Card> {
}
}
}
for (String rule : input.getRules(game)) {
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
found = true;

View file

@ -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<Card> 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());

View file

@ -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
* <p>
@ -626,6 +629,7 @@ public class GameState implements Serializable, Copyable<GameState> {
// 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<GameState> {
for (Map.Entry<ZoneChangeData, List<GameEvent>> entry : eventsByKey.entrySet()) {
Set<Card> movedCards = new LinkedHashSet<>();
Set<PermanentToken> movedTokens = new LinkedHashSet<>();
for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext();) {
for (Iterator<GameEvent> 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<GameState> {
}
// 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<GameState> {
}
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);

View file

@ -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<ZoneChangeInfo> moveCards(List<ZoneChangeInfo> zoneChangeInfos, Game game) {
// Handle Unmelded Meld Cards
for (ListIterator<ZoneChangeInfo> itr = zoneChangeInfos.listIterator(); itr.hasNext();) {
for (ListIterator<ZoneChangeInfo> itr = zoneChangeInfos.listIterator(); itr.hasNext(); ) {
ZoneChangeInfo info = itr.next();
MeldCard card = game.getMeldCard(info.event.getTargetId());
// Copies should be handled as normal cards.
@ -106,6 +103,10 @@ public final class ZonesHandler {
if (!(targetCard instanceof Permanent) && targetCard != null) {
if (targetCard instanceof MeldCard) {
cards = ((MeldCard) targetCard).getHalves();
} else if (targetCard instanceof ModalDoubleFacesCard) {
cards = new CardsImpl(targetCard);
cards.add(((ModalDoubleFacesCard) targetCard).getLeftHalfCard());
cards.add(((ModalDoubleFacesCard) targetCard).getRightHalfCard());
} else {
cards = new CardsImpl(targetCard);
}
@ -174,14 +175,19 @@ public final class ZonesHandler {
throw new UnsupportedOperationException("to Zone " + toZone.toString() + " not supported yet");
}
}
game.setZone(event.getTargetId(), event.getToZone());
if (targetCard instanceof MeldCard && cards != null) {
if (event.getToZone() != Zone.BATTLEFIELD) {
((MeldCard) targetCard).setMelded(false, game);
}
if (cards != null && (targetCard instanceof MeldCard || targetCard instanceof ModalDoubleFacesCard)) {
// update other parts too
for (Card card : cards.getCards(game)) {
game.setZone(card.getId(), event.getToZone());
}
// reset meld status
if (targetCard instanceof MeldCard) {
if (event.getToZone() != Zone.BATTLEFIELD) {
((MeldCard) targetCard).setMelded(false, game);
}
}
}
}
@ -203,7 +209,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<ZoneChangeInfo> itr = unmelded.subInfo.iterator(); itr.hasNext();) {
for (Iterator<ZoneChangeInfo> itr = unmelded.subInfo.iterator(); itr.hasNext(); ) {
ZoneChangeInfo subInfo = itr.next();
if (!maybeRemoveFromSourceZone(subInfo, game)) {
itr.remove();
@ -232,10 +238,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 +254,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("Try to move mdf card to battlefield instead half");
} else if (card instanceof Permanent) {
// This should never happen.
permanent = (Permanent) card;
throw new IllegalStateException("Try to move permanent card to battlefield");
} else {
permanent = new PermanentCard(card, event.getPlayerId(), game);
}

View file

@ -712,11 +712,6 @@ public class Spell extends StackObjImpl implements Card {
return null;
}
@Override
public boolean isSplitCard() {
return false;
}
@Override
public boolean isTransformable() {
return false;

View file

@ -1602,6 +1602,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 +3406,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;

View file

@ -1,11 +1,5 @@
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 mage.MageObject;
import mage.Mana;
import mage.abilities.Abilities;
@ -17,6 +11,8 @@ import mage.abilities.costs.mana.*;
import mage.abilities.effects.ContinuousEffect;
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;
@ -31,6 +27,13 @@ import mage.players.Player;
import mage.target.Target;
import mage.util.functions.CopyTokenFunction;
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
*/
@ -39,10 +42,10 @@ public final class CardUtil {
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 +880,21 @@ 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();
}
}
}