diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt index 664b4d79c31..05988555120 100644 --- a/Mage.Client/src/main/resources/card-pictures-tok.txt +++ b/Mage.Client/src/main/resources/card-pictures-tok.txt @@ -357,6 +357,7 @@ |Generate|TOK:C17|Rat|||DeathtouchRatToken| |Generate|TOK:C17|Vampire|||EdgarMarkovToken| |Generate|TOK:C17|Zombie|| +|Generate|TOK:C18|Myr|||BrudicladTelchorMyrToken| |Generate|TOK:CHK|Dragon Spirit|||TatsumaDragonToken| |Generate|TOK:CHK|Elemental|||SeedGuardianToken| |Generate|TOK:CHK|Illusion|||MelokuTheCloudedMirrorToken| diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java index 81e4030d252..439e7bf928e 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java @@ -1,4 +1,3 @@ - package mage.deck; import java.util.*; @@ -23,6 +22,7 @@ import mage.filter.FilterMana; public class Commander extends Constructed { protected List bannedCommander = new ArrayList<>(); + protected boolean partnerAllowed = true; public Commander() { this("Commander"); @@ -105,6 +105,9 @@ public class Commander extends Constructed { } if (deck.getSideboard().size() < 1 || deck.getSideboard().size() > 2) { + if ((deck.getSideboard().size() > 1 && !partnerAllowed)) { + invalid.put("Commander", "You may only have one commander"); + } invalid.put("Commander", "Sideboard must contain only the commander(s)"); valid = false; } else { diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/MTGO1v1Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/MTGO1v1Commander.java index 2d1f9ffb910..511d0d3f0e0 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/MTGO1v1Commander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/MTGO1v1Commander.java @@ -1,4 +1,3 @@ - package mage.deck; /** @@ -9,70 +8,49 @@ public class MTGO1v1Commander extends Commander { public MTGO1v1Commander() { super("MTGO 1v1 Commander"); + partnerAllowed = false; + banned.add("Ancestral Recall"); - banned.add("Ancient Tomb"); banned.add("Back to Basics"); banned.add("Balance"); banned.add("Baral, Chief of Compliance"); banned.add("Black Lotus"); banned.add("Braids, Cabal Minion"); - banned.add("Brainstorm"); banned.add("Channel"); - banned.add("Chrome Mox"); banned.add("Derevi, Empyrial Tactician"); - banned.add("Demonic Tutor"); - banned.add("Dig Through Time"); + banned.add("Doomsday"); banned.add("Edgar Markov"); banned.add("Edric, Spymaster of Trest"); banned.add("Emrakul, the Aeons Torn"); - banned.add("Enlightened Tutor"); - banned.add("Entomb"); + banned.add("Erayo, Soratami Ascendant"); banned.add("Fastbond"); banned.add("Food Chain"); - banned.add("Gaea's Cradle"); banned.add("Gifts Ungiven"); banned.add("Hermit Druid"); banned.add("Humility"); - banned.add("Imperial Seal"); banned.add("Karakas"); banned.add("Library of Alexandria"); - banned.add("Mana Crypt"); - banned.add("Mana Drain"); - banned.add("Mana Vault"); banned.add("Mind Twist"); banned.add("Moat"); - banned.add("Mox Diamond"); banned.add("Mox Emerald"); banned.add("Mox Jet"); banned.add("Mox Pearl"); banned.add("Mox Ruby"); banned.add("Mox Sapphire"); - banned.add("Mystical Tutor"); banned.add("Natural Order"); - banned.add("Necropotence"); banned.add("Oath of Druids"); - banned.add("Ponder"); - banned.add("Preordain"); banned.add("Rofellos, Llanowar Emissary"); + banned.add("Strip Mine"); banned.add("Sensei's Divining Top"); banned.add("Serra Ascendant"); - banned.add("Sol Ring"); - banned.add("Strip Mine"); banned.add("Survival of the Fittest"); banned.add("Sylvan Library"); - banned.add("Sylvan Tutor"); banned.add("The Tabernacle at Pendrell Vale"); banned.add("Time Vault"); banned.add("Time Walk"); banned.add("Tinker"); banned.add("Tolarian Academy"); - banned.add("Treachery"); - banned.add("Treasure Cruise"); - banned.add("Vial Smasher the Fierce"); - banned.add("Vampiric Tutor"); banned.add("Winter Orb"); - banned.add("Wordly Tutor"); - banned.add("Yamgmoth's Bargain"); banned.add("Zur the Enchanter"); } } diff --git a/Mage.Sets/src/mage/cards/a/AdaptiveAutomaton.java b/Mage.Sets/src/mage/cards/a/AdaptiveAutomaton.java index cb47c035047..1e1341a51e9 100644 --- a/Mage.Sets/src/mage/cards/a/AdaptiveAutomaton.java +++ b/Mage.Sets/src/mage/cards/a/AdaptiveAutomaton.java @@ -3,10 +3,9 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; +import mage.abilities.effects.common.continuous.AddChosenSubtypeEffect; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.ChooseCreatureTypeEffect; import mage.abilities.effects.common.continuous.BoostAllOfChosenSubtypeEffect; import mage.abilities.effects.common.enterAttribute.EnterAttributeAddChosenSubtypeEffect; @@ -15,8 +14,6 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; /** * @author nantuko @@ -42,7 +39,7 @@ public final class AdaptiveAutomaton extends CardImpl { ability.addEffect(new EnterAttributeAddChosenSubtypeEffect()); this.addAbility(ability); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AdaptiveAutomatonAddSubtypeEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AddChosenSubtypeEffect())); // Other creatures you control of the chosen type get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllOfChosenSubtypeEffect(1, 1, Duration.WhileOnBattlefield, filter, true))); } @@ -57,31 +54,3 @@ public final class AdaptiveAutomaton extends CardImpl { } } -class AdaptiveAutomatonAddSubtypeEffect extends ContinuousEffectImpl { - - public AdaptiveAutomatonAddSubtypeEffect() { - super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); - staticText = "{this} is the chosen type in addition to its other types"; - } - - public AdaptiveAutomatonAddSubtypeEffect(final AdaptiveAutomatonAddSubtypeEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - SubType subType = ChooseCreatureTypeEffect.getChoosenCreatureType(permanent.getId(), game); - if (subType != null && !permanent.hasSubtype(subType, game)) { - permanent.getSubtype(game).add(subType); - } - } - return true; - } - - @Override - public AdaptiveAutomatonAddSubtypeEffect copy() { - return new AdaptiveAutomatonAddSubtypeEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AminatouTheFateShifter.java b/Mage.Sets/src/mage/cards/a/AminatouTheFateShifter.java new file mode 100644 index 00000000000..64533667f53 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AminatouTheFateShifter.java @@ -0,0 +1,183 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlanswalkerEntersWithLoyalityCountersAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetForSourceEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderYourControlTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceLeftOrRight; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.other.OwnerPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.players.PlayerList; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInHand; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +public class AminatouTheFateShifter extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("permanent you own"); + + static { + filter.add(new OwnerPredicate(TargetController.YOU)); + filter.add(new AnotherPredicate()); + } + + public AminatouTheFateShifter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{W}{U}{B}"); + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.AMINATOU); + + this.addAbility(new PlanswalkerEntersWithLoyalityCountersAbility(3)); + + // +1: Draw a card, then put a card from your hand on top of your library. + Ability ability = new LoyaltyAbility(new AminatouPlusEffect(), +1); + this.addAbility(ability); + + // -1: Exile another target permanent you own, then return it to the battlefield under your control. + ability = new LoyaltyAbility(new ExileTargetForSourceEffect(), -1); + ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // -6: Choose left or right. Each player gains control of all nonland permanents other than Aminatou, the + // Fateshifter controlled by the next player in the chosen direction. + ability = new LoyaltyAbility(new AminatouUltimateEffect(), -6); + this.addAbility(ability); + } + public AminatouTheFateShifter(final AminatouTheFateShifter card) { + super(card); + } + + @Override + public AminatouTheFateShifter copy() { + return new AminatouTheFateShifter(this); + } +} + +class AminatouPlusEffect extends OneShotEffect { + public AminatouPlusEffect() { + super(Outcome.DrawCard); + staticText = "draw a card, then put a card from your hand on top of your library"; + } + + public AminatouPlusEffect(final AminatouPlusEffect effect) { + super(effect); + } + + @Override + public AminatouPlusEffect copy() { + return new AminatouPlusEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.drawCards(1, game); + putOnLibrary(player, source, game); + return true; + } + return false; + } + + private boolean putOnLibrary(Player player, Ability source, Game game) { + TargetCardInHand target = new TargetCardInHand(); + if (target.canChoose(source.getSourceId(), player.getId(), game)) { + player.chooseTarget(Outcome.ReturnToHand, target, source, game); + Card card = player.getHand().get(target.getFirstTarget(), game); + if (card != null) { + return player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.HAND, true, false); + } + } + return false; + } +} + +class AminatouUltimateEffect extends OneShotEffect { + public AminatouUltimateEffect (){ + super(Outcome.Benefit); + staticText = "Choose left or right. Each player gains control of all nonland permanents other than Aminatou," + + " the Fateshifter controlled by the next player in the chosen direction."; + } + + public AminatouUltimateEffect(final AminatouUltimateEffect effect) { + super(effect); + } + + @Override + public AminatouUltimateEffect copy(){return new AminatouUltimateEffect(this);} + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Choice choice = new ChoiceLeftOrRight(); + if (!controller.choose(Outcome.Neutral, choice, game)) { + return false; + } + boolean left = choice.getChoice().equals("Left"); + PlayerList playerList = game.getState().getPlayerList().copy(); + // set playerlist to controller + while (!playerList.get().equals(source.getControllerId())) { + playerList.getNext(); + } + UUID currentPlayer = playerList.get(); + UUID nextPlayer; + UUID firstNextPlayer = null; + while (!getNextPlayerInDirection(left, playerList, game).equals(firstNextPlayer)) { + nextPlayer = playerList.get(); + if (nextPlayer == null) { + return false; + } + // skip players out of range + if (!game.getState().getPlayersInRange(controller.getId(), game).contains(nextPlayer)){ + continue; + } + // save first next player to check for iteration stop + if (firstNextPlayer == null) { + firstNextPlayer = nextPlayer; + } + FilterNonlandPermanent nextPlayerNonlandPermanentsFilter = new FilterNonlandPermanent(); + nextPlayerNonlandPermanentsFilter.add(new ControllerIdPredicate(nextPlayer)); + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(nextPlayerNonlandPermanentsFilter, game)) { + if (permanent.getId().equals(source.getSourceId())){ + continue; + } + ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, currentPlayer); + effect.setTargetPointer(new FixedTarget(permanent.getId())); + game.addEffect(effect, source); + } + currentPlayer = nextPlayer; + } + return true; + } + return false; + } + + private UUID getNextPlayerInDirection(boolean left, PlayerList playerList, Game game) { + UUID nextPlayerId; + if (left) { + nextPlayerId = playerList.getNext(); + } else { + nextPlayerId = playerList.getPrevious(); + } + return nextPlayerId; + } +} + diff --git a/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java b/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java new file mode 100644 index 00000000000..c9935eb2c3c --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java @@ -0,0 +1,99 @@ +package mage.cards.a; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.constants.SubType; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.game.Game; +import mage.game.permanent.token.StoneTrapIdolToken; +import mage.util.CardUtil; + +/** + * + * @author TheElk801 + */ +public final class AncientStoneIdol extends CardImpl { + + public AncientStoneIdol(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{10}"); + + this.subtype.add(SubType.GOLEM); + this.power = new MageInt(12); + this.toughness = new MageInt(12); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // This spell costs {1} less to cast for each attacking creature. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new AncientStoneIdolCostReductionEffect())); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Ancient Stone Idol dies, create a 6/12 colorless Construct artifact creature token with trample. + this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new StoneTrapIdolToken()))); + } + + public AncientStoneIdol(final AncientStoneIdol card) { + super(card); + } + + @Override + public AncientStoneIdol copy() { + return new AncientStoneIdol(this); + } +} + +class AncientStoneIdolCostReductionEffect extends CostModificationEffectImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(new AttackingPredicate()); + } + + public AncientStoneIdolCostReductionEffect() { + super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "This spell costs {1} less to cast for each attacking creature"; + } + + protected AncientStoneIdolCostReductionEffect(AncientStoneIdolCostReductionEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + int reductionAmount = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + CardUtil.reduceCost(abilityToModify, reductionAmount); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + if ((abilityToModify instanceof SpellAbility) && abilityToModify.getSourceId().equals(source.getSourceId())) { + return game.getCard(abilityToModify.getSourceId()) != null; + } + return false; + } + + @Override + public AncientStoneIdolCostReductionEffect copy() { + return new AncientStoneIdolCostReductionEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArgentSphinx.java b/Mage.Sets/src/mage/cards/a/ArgentSphinx.java index e3c98324de0..7f76777b23d 100644 --- a/Mage.Sets/src/mage/cards/a/ArgentSphinx.java +++ b/Mage.Sets/src/mage/cards/a/ArgentSphinx.java @@ -36,7 +36,7 @@ public final class ArgentSphinx extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Metalcraft - {U}: Exile Argent Sphinx. Return it to the battlefield under your control at the beginning of the next end step. Activate this ability only if you control three or more artifacts. + // Metalcraft — {U}: Exile Argent Sphinx. Return it to the battlefield under your control at the beginning of the next end step. Activate this ability only if you control three or more artifacts. Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new ArgentSphinxEffect(), new ManaCostsImpl("{U}"), MetalcraftCondition.instance); ability.setAbilityWord(AbilityWord.METALCRAFT); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java b/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java new file mode 100644 index 00000000000..3c6578b392a --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java @@ -0,0 +1,121 @@ +package mage.cards.a; + +import java.util.UUID; +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import static mage.constants.Layer.TypeChangingEffects_4; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author spjspj + */ +public final class ArixmethesSlumberingIsle extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a spell"); + + public ArixmethesSlumberingIsle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); + + addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.KRAKEN); + this.power = new MageInt(12); + this.toughness = new MageInt(12); + + // Arixmethes, Slumbering Isle enters the battlefield tapped with five slumber counters on it. + this.addAbility(new EntersBattlefieldTappedAbility()); + Effect effect = new AddCountersSourceEffect(CounterType.SLUMBER.createInstance(5)); + this.addAbility(new EntersBattlefieldAbility(effect, "with five slumber counters")); + + // As long as Arixmethes, Slumbering Isle has a slumber counter on it, it's a land. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( + new ArixmethesIsLandEffect(), + new SourceHasCounterCondition(CounterType.SLUMBER, 1, Integer.MAX_VALUE), + "As long as {this} has a slumber counter on it, it's a land"))); + + // Whenever you cast a spell, you may remove a slumber counter from Arixmethes. + this.addAbility(new SpellCastControllerTriggeredAbility(new RemoveCounterSourceEffect(CounterType.SLUMBER.createInstance(1)), true)); + + // {T}: Add {G}{U}. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new Mana(0, 1, 1, 0, 0, 0, 0, 0), new TapSourceCost())); + } + + public ArixmethesSlumberingIsle(final ArixmethesSlumberingIsle card) { + super(card); + } + + @Override + public ArixmethesSlumberingIsle copy() { + return new ArixmethesSlumberingIsle(this); + } +} + +class ArixmethesIsLandEffect extends ContinuousEffectImpl { + + public ArixmethesIsLandEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); + this.staticText = "As long as {this} has a slumber counter on it, it's a land"; + } + + public ArixmethesIsLandEffect(final ArixmethesIsLandEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public ArixmethesIsLandEffect copy() { + return new ArixmethesIsLandEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getSourceId()); + + if (permanent != null) { + switch (layer) { + case TypeChangingEffects_4: + permanent.getCardType().clear(); + permanent.addCardType(CardType.LAND); + permanent.getSubtype(game).clear(); + break; + } + return true; + } + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AuriokEdgewright.java b/Mage.Sets/src/mage/cards/a/AuriokEdgewright.java index f4d24b54469..a38161a38a5 100644 --- a/Mage.Sets/src/mage/cards/a/AuriokEdgewright.java +++ b/Mage.Sets/src/mage/cards/a/AuriokEdgewright.java @@ -22,7 +22,7 @@ import mage.constants.Zone; */ public final class AuriokEdgewright extends CardImpl { - protected static String effectText = "Metalcraft - Auriok Edgewright has double strike as long as you control three or more artifacts."; + protected static String effectText = "Metalcraft — Auriok Edgewright has double strike as long as you control three or more artifacts."; public AuriokEdgewright(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{W}"); diff --git a/Mage.Sets/src/mage/cards/a/AuriokSunchaser.java b/Mage.Sets/src/mage/cards/a/AuriokSunchaser.java index 6d071200ba8..249c912b492 100644 --- a/Mage.Sets/src/mage/cards/a/AuriokSunchaser.java +++ b/Mage.Sets/src/mage/cards/a/AuriokSunchaser.java @@ -23,8 +23,8 @@ import mage.constants.Zone; */ public final class AuriokSunchaser extends CardImpl { - protected static String effect1Text = "Metalcraft - As long as you control three or more artifacts, Auriok Sunchaser gets +2/+2"; - protected static String effect2Text = "Metalcraft - As long as you control three or more artifacts, Auriok Sunchaser has flying"; + protected static String effect1Text = "Metalcraft — As long as you control three or more artifacts, Auriok Sunchaser gets +2/+2"; + protected static String effect2Text = "Metalcraft — As long as you control three or more artifacts, Auriok Sunchaser has flying"; public AuriokSunchaser(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); diff --git a/Mage.Sets/src/mage/cards/b/Balance.java b/Mage.Sets/src/mage/cards/b/Balance.java index f89d8499531..279a020a109 100644 --- a/Mage.Sets/src/mage/cards/b/Balance.java +++ b/Mage.Sets/src/mage/cards/b/Balance.java @@ -1,4 +1,3 @@ - package mage.cards.b; import mage.abilities.Ability; @@ -26,7 +25,7 @@ import java.util.UUID; public final class Balance extends CardImpl { public Balance(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}"); // Each player chooses a number of lands he or she controls equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players discard cards and sacrifice creatures the same way. this.getSpellAbility().addEffect(new BalanceEffect()); @@ -46,7 +45,10 @@ class BalanceEffect extends OneShotEffect { BalanceEffect() { super(Outcome.Sacrifice); - staticText = "Each player chooses a number of lands he or she controls equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players sacrifice creatures and discard cards the same way"; + staticText = "each player chooses a number of lands they control " + + "equal to the number of lands controlled by the player " + + "who controls the fewest, then sacrifices the rest. " + + "Players discard cards and sacrifice creatures the same way"; } BalanceEffect(final BalanceEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BelbesPortal.java b/Mage.Sets/src/mage/cards/b/BelbesPortal.java index 99433187af5..3364c22b2b6 100644 --- a/Mage.Sets/src/mage/cards/b/BelbesPortal.java +++ b/Mage.Sets/src/mage/cards/b/BelbesPortal.java @@ -30,7 +30,7 @@ public final class BelbesPortal extends CardImpl { this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.PutCreatureInPlay))); // {3}, {tap}: You may put a creature card of the chosen type from your hand onto the battlefield. FilterCreatureCard filter = new FilterCreatureCard("a creature card of the chosen type"); - filter.add(new ChosenSubtypePredicate(this.getId())); + filter.add(new ChosenSubtypePredicate()); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutCardFromHandOntoBattlefieldEffect(filter), new ManaCostsImpl("{3}")); diff --git a/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java b/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java index fef59f0aaab..53de13427a4 100644 --- a/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java +++ b/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java @@ -22,7 +22,7 @@ import mage.constants.SubType; */ public final class BladeTribeBerserkers extends CardImpl { - private static final String effectText = "Metalcraft - When Blade-Tribe Berserkers enters the battlefield, if you control three or more artifacts, Blade-Tribe Berserkers gets +3/+3 and gains haste until end of turn."; + private static final String effectText = "Metalcraft — When Blade-Tribe Berserkers enters the battlefield, if you control three or more artifacts, Blade-Tribe Berserkers gets +3/+3 and gains haste until end of turn."; public BladeTribeBerserkers(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); @@ -31,7 +31,7 @@ public final class BladeTribeBerserkers extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - //Metalcraft - When Blade-Tribe Berserkers enters the battlefield, if you control three or more artifacts, Blade-Tribe Berserkers gets +3/+3 and gains haste until end of turn. + //Metalcraft — When Blade-Tribe Berserkers enters the battlefield, if you control three or more artifacts, Blade-Tribe Berserkers gets +3/+3 and gains haste until end of turn. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new BoostSourceEffect(3, 3, Duration.EndOfTurn), false); ability.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MetalcraftCondition.instance, effectText)); diff --git a/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java b/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java index 605e3cf3b23..61ec1d16679 100644 --- a/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java +++ b/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java @@ -22,7 +22,7 @@ import mage.target.TargetPlayer; */ public final class BleakCovenVampires extends CardImpl { - private static final String effectText = "Metalcraft - When Bleak Coven Vampires enters the battlefield, if you control three or more artifacts, target player loses 4 life and you gain 4 life."; + private static final String effectText = "Metalcraft — When Bleak Coven Vampires enters the battlefield, if you control three or more artifacts, target player loses 4 life and you gain 4 life."; public BleakCovenVampires(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); @@ -31,7 +31,7 @@ public final class BleakCovenVampires extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(3); - //Metalcraft - When Bleak Coven Vampires enters the battlefield, if you control three or more artifacts, target player loses 4 life and you gain 4 life. + //Metalcraft — When Bleak Coven Vampires enters the battlefield, if you control three or more artifacts, target player loses 4 life and you gain 4 life. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(4), false); ability.addEffect(new GainLifeEffect(4)); Target target = new TargetPlayer(); diff --git a/Mage.Sets/src/mage/cards/b/Bloodtracker.java b/Mage.Sets/src/mage/cards/b/Bloodtracker.java new file mode 100644 index 00000000000..bd4895f996e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/Bloodtracker.java @@ -0,0 +1,57 @@ +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; + +/** + * + * @author TheElk801 + */ +public final class Bloodtracker extends CardImpl { + + public Bloodtracker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {B}, Pay 2 life: Put a +1/+1 counter on Bloodtracker. + Ability ability = new SimpleActivatedAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{B}")); + ability.addCost(new PayLifeCost(2)); + this.addAbility(ability); + + // When Bloodtracker leaves the battlefield, draw a card for each +1/+1 counter on it. + this.addAbility(new LeavesBattlefieldTriggeredAbility( + new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.P1P1)) + .setText("draw a card for each +1/+1 counter on it"), false + )); + } + + public Bloodtracker(final Bloodtracker card) { + super(card); + } + + @Override + public Bloodtracker copy() { + return new Bloodtracker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BoreasCharger.java b/Mage.Sets/src/mage/cards/b/BoreasCharger.java new file mode 100644 index 00000000000..826bf3c5987 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoreasCharger.java @@ -0,0 +1,168 @@ +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.FilterPlayer; +import mage.filter.StaticFilters; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author TheElk801 + */ +public final class BoreasCharger extends CardImpl { + + public BoreasCharger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.PEGASUS); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Boreas Charger leaves the battlefield, choose an opponent who controls more lands than you. Search your library for a number of Plains cards equal to the difference and reveal them. Put one of them onto the battlefield tapped and the rest into your hand. Then shuffle your library. + this.addAbility(new LeavesBattlefieldTriggeredAbility( + new BoreasChargerEffect(), false + )); + } + + public BoreasCharger(final BoreasCharger card) { + super(card); + } + + @Override + public BoreasCharger copy() { + return new BoreasCharger(this); + } +} + +class BoreasChargerEffect extends OneShotEffect { + + private static final FilterPlayer filter + = new FilterPlayer("opponent who controls more lands than you"); + private static final FilterCard filter2 + = new FilterCard("Plains cards"); + private static final FilterCard filter3 + = new FilterCard("a card to put onto the battlefield tapped"); + + static { + filter.add(new BoreasChargerPredicate()); + filter2.add(new SubtypePredicate(SubType.PLAINS)); + } + + public BoreasChargerEffect() { + super(Outcome.Benefit); + this.staticText = "choose an opponent who controls more lands than you. " + + "Search your library for a number of Plains cards " + + "equal to the difference and reveal them. " + + "Put one of them onto the battlefield tapped " + + "and the rest into your hand. Then shuffle your library"; + } + + public BoreasChargerEffect(final BoreasChargerEffect effect) { + super(effect); + } + + @Override + public BoreasChargerEffect copy() { + return new BoreasChargerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getSourceId()); + if (controller == null) { + return false; + } + TargetPlayer target = new TargetPlayer(1, 1, true, filter); + controller.choose(outcome, target, source.getSourceId(), game); + Player opponent = game.getPlayer(target.getFirstTarget()); + if (opponent == null) { + controller.shuffleLibrary(source, game); + return false; + } + int landDifference = game.getBattlefield().getAllActivePermanents( + StaticFilters.FILTER_LANDS, opponent.getId(), game + ).size(); + landDifference -= game.getBattlefield().getAllActivePermanents( + StaticFilters.FILTER_LANDS, controller.getId(), game + ).size(); + landDifference = Math.abs(landDifference); + TargetCardInLibrary target2 + = new TargetCardInLibrary(0, landDifference, filter2); + Cards cardsToHand = new CardsImpl(); + if (controller.searchLibrary(target2, game)) { + for (UUID cardId : target2.getTargets()) { + Card card = game.getCard(cardId); + if (card != null) { + cardsToHand.add(card); + } + } + } + if (cardsToHand.isEmpty()) { + controller.shuffleLibrary(source, game); + return true; + } + TargetCard target3 = new TargetCard(Zone.LIBRARY, filter3); + Card cardToBattlefield = null; + if (controller.choose(outcome, cardsToHand, target3, game)) { + cardToBattlefield = cardsToHand.get(target2.getFirstTarget(), game); + cardsToHand.remove(cardToBattlefield); + } + if (cardToBattlefield != null) { + controller.moveCards( + cardToBattlefield, Zone.BATTLEFIELD, source, game, + true, false, true, null + ); + } + controller.moveCards(cardsToHand, Zone.HAND, source, game); + controller.shuffleLibrary(source, game); + return true; + } +} + +class BoreasChargerPredicate implements ObjectSourcePlayerPredicate> { + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Player targetPlayer = input.getObject(); + UUID playerId = input.getPlayerId(); + if (targetPlayer == null || playerId == null) { + return false; + } + if (!targetPlayer.hasOpponent(playerId, game)) { + return false; + } + int countTargetPlayer = game.getBattlefield().countAll( + StaticFilters.FILTER_LANDS, targetPlayer.getId(), game + ); + int countController = game.getBattlefield().countAll( + StaticFilters.FILTER_LANDS, playerId, game + ); + + return countTargetPlayer > countController; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BrassHerald.java b/Mage.Sets/src/mage/cards/b/BrassHerald.java index 86d57ee69ba..870ccda042a 100644 --- a/Mage.Sets/src/mage/cards/b/BrassHerald.java +++ b/Mage.Sets/src/mage/cards/b/BrassHerald.java @@ -69,7 +69,7 @@ class BrassHeraldEntersEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { FilterCard filter = new FilterCard("creature cards of the chosen type"); - filter.add(new ChosenSubtypePredicate(source.getSourceId())); + filter.add(new ChosenSubtypePredicate()); return new RevealLibraryPutIntoHandEffect(4, filter, Zone.LIBRARY).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/b/BrimstoneVolley.java b/Mage.Sets/src/mage/cards/b/BrimstoneVolley.java index b0d2761eac0..dd37a947c6d 100644 --- a/Mage.Sets/src/mage/cards/b/BrimstoneVolley.java +++ b/Mage.Sets/src/mage/cards/b/BrimstoneVolley.java @@ -24,7 +24,7 @@ public final class BrimstoneVolley extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); // Brimstone Volley deals 3 damage to any target. - // Morbid - Brimstone Volley deals 5 damage to that creature or player instead if a creature died this turn. + // Morbid — Brimstone Volley deals 5 damage to that creature or player instead if a creature died this turn. this.getSpellAbility().addEffect(new BrimstoneVolleyEffect()); this.getSpellAbility().addTarget(new TargetAnyTarget()); } @@ -43,7 +43,7 @@ class BrimstoneVolleyEffect extends OneShotEffect { public BrimstoneVolleyEffect() { super(Outcome.Damage); - staticText = "{this} deals 3 damage to any target.\n Morbid - {this} deals 5 damage to that permanent or player instead if a creature died this turn"; + staticText = "{this} deals 3 damage to any target.\n Morbid — {this} deals 5 damage to that permanent or player instead if a creature died this turn"; } public BrimstoneVolleyEffect(final BrimstoneVolleyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BrudicladTelchorEngineer.java b/Mage.Sets/src/mage/cards/b/BrudicladTelchorEngineer.java new file mode 100644 index 00000000000..6da9effd66a --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BrudicladTelchorEngineer.java @@ -0,0 +1,113 @@ +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.BrudicladTelchorMyrToken; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; +import mage.util.functions.EmptyApplyToPermanent; + +/** + * + * @author spjspj + */ +public final class BrudicladTelchorEngineer extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature tokens you control"); + + static { + filter.add(new TokenPredicate()); + } + + public BrudicladTelchorEngineer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}{U}{R}"); + + addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Creature tokens you control have haste. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter, true))); + + // At the beginning of combat on your turn, create a 2/1 blue Myr artifact creature token. Then you may choose a token you control. If you do, each other token you control becomes a copy of that token. + this.addAbility(new BeginningOfCombatTriggeredAbility(new BrudicladTelchorCombatffect(), TargetController.YOU, false)); + } + + public BrudicladTelchorEngineer(final BrudicladTelchorEngineer card) { + super(card); + } + + @Override + public BrudicladTelchorEngineer copy() { + return new BrudicladTelchorEngineer(this); + } +} + +class BrudicladTelchorCombatffect extends OneShotEffect { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent(" token you control. If you do, each other token you control becomes a copy of that token"); + + static { + filter.add(new TokenPredicate()); + } + + public BrudicladTelchorCombatffect() { + super(Outcome.Sacrifice); + this.staticText = " you may choose a token you control. If you do, each other token you control becomes a copy of that token"; + } + + public BrudicladTelchorCombatffect(final BrudicladTelchorCombatffect effect) { + super(effect); + } + + @Override + public BrudicladTelchorCombatffect copy() { + return new BrudicladTelchorCombatffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + CreateTokenEffect effect = new CreateTokenEffect(new BrudicladTelchorMyrToken(), 1); + + if (effect.apply(game, source)) { + TargetControlledPermanent target = new TargetControlledPermanent(0, 1, filter, true); + target.setNotTarget(true); + if (controller.choose(Outcome.Neutral, target, source.getSourceId(), game)) { + Permanent toCopyFromPermanent = game.getPermanent(target.getFirstTarget()); + + if (toCopyFromPermanent != null) { + for (Permanent toCopyToPermanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { + if (!toCopyToPermanent.equals(toCopyFromPermanent)) { + game.copyPermanent(toCopyFromPermanent, toCopyToPermanent.getId(), source, new EmptyApplyToPermanent()); + } + } + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CallerOfTheHunt.java b/Mage.Sets/src/mage/cards/c/CallerOfTheHunt.java index d27e9069319..f05249eeac4 100644 --- a/Mage.Sets/src/mage/cards/c/CallerOfTheHunt.java +++ b/Mage.Sets/src/mage/cards/c/CallerOfTheHunt.java @@ -49,7 +49,7 @@ public final class CallerOfTheHunt extends CardImpl { if (mageObject != null && effect.apply(game, ability)) { FilterPermanent filter = new FilterPermanent(); - filter.add(new ChosenSubtypePredicate(mageObject.getId())); + filter.add(new ChosenSubtypePredicate()); ContinuousEffect effectPower = new SetPowerSourceEffect(new PermanentsOnBattlefieldCount(filter), Duration.Custom); ContinuousEffect effectToughness = new SetToughnessSourceEffect(new PermanentsOnBattlefieldCount(filter), Duration.Custom); game.addEffect(effectPower, ability); diff --git a/Mage.Sets/src/mage/cards/c/CarapaceForger.java b/Mage.Sets/src/mage/cards/c/CarapaceForger.java index 7e8b3f6367f..03ce8bd225e 100644 --- a/Mage.Sets/src/mage/cards/c/CarapaceForger.java +++ b/Mage.Sets/src/mage/cards/c/CarapaceForger.java @@ -21,7 +21,7 @@ import mage.constants.Zone; * @author Loki */ public final class CarapaceForger extends CardImpl { - private static final String text = "Metalcraft - Carapace Forger gets +2/+2 as long as you control three or more artifacts"; + private static final String text = "Metalcraft — Carapace Forger gets +2/+2 as long as you control three or more artifacts"; public CarapaceForger (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); diff --git a/Mage.Sets/src/mage/cards/c/CaravanVigil.java b/Mage.Sets/src/mage/cards/c/CaravanVigil.java index 543efe0bfce..963e149db37 100644 --- a/Mage.Sets/src/mage/cards/c/CaravanVigil.java +++ b/Mage.Sets/src/mage/cards/c/CaravanVigil.java @@ -26,7 +26,7 @@ public final class CaravanVigil extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{G}"); // Search your library for a basic land card, reveal it, put it into your hand, then shuffle your library. - // Morbid - You may put that card onto the battlefield instead of putting it into your hand if a creature died this turn. + // Morbid — You may put that card onto the battlefield instead of putting it into your hand if a creature died this turn. this.getSpellAbility().addEffect(new CaravanVigilEffect()); } diff --git a/Mage.Sets/src/mage/cards/c/ChromeSteed.java b/Mage.Sets/src/mage/cards/c/ChromeSteed.java index d9053812486..b0e34f7e10f 100644 --- a/Mage.Sets/src/mage/cards/c/ChromeSteed.java +++ b/Mage.Sets/src/mage/cards/c/ChromeSteed.java @@ -21,7 +21,7 @@ import mage.constants.Zone; * @author Loki */ public final class ChromeSteed extends CardImpl { - private static final String text = "Metalcraft - Chrome Steed gets +2/+2 as long as you control three or more artifacts"; + private static final String text = "Metalcraft — Chrome Steed gets +2/+2 as long as you control three or more artifacts"; public ChromeSteed (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}"); diff --git a/Mage.Sets/src/mage/cards/c/ConcussiveBolt.java b/Mage.Sets/src/mage/cards/c/ConcussiveBolt.java index b8f39e589a3..50103876f9a 100644 --- a/Mage.Sets/src/mage/cards/c/ConcussiveBolt.java +++ b/Mage.Sets/src/mage/cards/c/ConcussiveBolt.java @@ -29,7 +29,7 @@ public final class ConcussiveBolt extends CardImpl { // Concussive Bolt deals 4 damage to target player. this.getSpellAbility().addTarget(new TargetPlayerOrPlaneswalker()); this.getSpellAbility().addEffect(new DamageTargetEffect(4)); - // Metalcraft - If you control three or more artifacts, creatures that player controls can't block this turn. + // Metalcraft — If you control three or more artifacts, creatures that player controls can't block this turn. this.getSpellAbility().addEffect(new ConcussiveBoltEffect()); this.getSpellAbility().addEffect(new ConcussiveBoltRestrictionEffect()); } @@ -48,7 +48,7 @@ class ConcussiveBoltEffect extends OneShotEffect { public ConcussiveBoltEffect() { super(Outcome.Benefit); - this.staticText = "Metalcraft - If you control three or more artifacts, creatures controlled by that player or by that planeswalker's controller can't block this turn."; + this.staticText = "Metalcraft — If you control three or more artifacts, creatures controlled by that player or by that planeswalker's controller can't block this turn."; } public ConcussiveBoltEffect(final ConcussiveBoltEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CovetedJewel.java b/Mage.Sets/src/mage/cards/c/CovetedJewel.java new file mode 100644 index 00000000000..63007242df7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CovetedJewel.java @@ -0,0 +1,135 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author TheElk801 + */ +public final class CovetedJewel extends CardImpl { + + public CovetedJewel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}"); + + // When Coveted Jewel enters the battlefield, draw three cards. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new DrawCardSourceControllerEffect(3) + )); + + // {T}: Add three mana of any one color. + this.addAbility(new SimpleManaAbility( + Zone.BATTLEFIELD, + new AddManaOfAnyColorEffect(3), + new TapSourceCost() + )); + + // Whenever one or more creatures an opponent controls attack you and aren't blocked, that player draws three cards and gains control of Coveted Jewel. Untap it. + this.addAbility(new CovetedJewelTriggeredAbility()); + } + + public CovetedJewel(final CovetedJewel card) { + super(card); + } + + @Override + public CovetedJewel copy() { + return new CovetedJewel(this); + } +} + +class CovetedJewelTriggeredAbility extends TriggeredAbilityImpl { + + public CovetedJewelTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardTargetEffect(3), false); + this.addEffect(new CovetedJewelEffect()); + this.addEffect(new UntapSourceEffect()); + } + + public CovetedJewelTriggeredAbility(final CovetedJewelTriggeredAbility ability) { + super(ability); + } + + @Override + public CovetedJewelTriggeredAbility copy() { + return new CovetedJewelTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Player player = game.getPlayer(this.getControllerId()); + if (player == null) { + return false; + } + for (UUID attacker : game.getCombat().getAttackers()) { + Permanent creature = game.getPermanent(attacker); + if (creature != null + && player.hasOpponent(creature.getControllerId(), game) + && player.getId().equals(game.getCombat().getDefendingPlayerId(attacker, game)) + && !creature.isBlocked(game)) { + this.getEffects().setTargetPointer(new FixedTarget(this.getControllerId(), game)); + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever one or more creatures an opponent controls attack you " + + "and aren't blocked, that player draws three cards " + + "and gains control of {this}. Untap it."; + } +} + +class CovetedJewelEffect extends ContinuousEffectImpl { + + public CovetedJewelEffect() { + super(Duration.Custom, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); + } + + public CovetedJewelEffect(final CovetedJewelEffect effect) { + super(effect); + } + + @Override + public CovetedJewelEffect copy() { + return new CovetedJewelEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return false; + } + return permanent.changeControllerId(source.getFirstTarget(), game); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CrashOfRhinoBeetles.java b/Mage.Sets/src/mage/cards/c/CrashOfRhinoBeetles.java new file mode 100644 index 00000000000..720d7490e2e --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrashOfRhinoBeetles.java @@ -0,0 +1,59 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.constants.SubType; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.StaticFilters; + +/** + * + * @author TheElk801 + */ +public final class CrashOfRhinoBeetles extends CardImpl { + + public CrashOfRhinoBeetles(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.INSECT); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Crash of Rhino Beetles gets +10/+10 as long as you control ten or more lands. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new ConditionalContinuousEffect( + new BoostSourceEffect( + 10, 10, Duration.WhileOnBattlefield + ), + new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + ComparisonType.MORE_THAN, 9 + ), + "{this} gets +10/+10 as long as you control ten or more lands" + ) + )); + } + + public CrashOfRhinoBeetles(final CrashOfRhinoBeetles card) { + super(card); + } + + @Override + public CrashOfRhinoBeetles copy() { + return new CrashOfRhinoBeetles(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DeathreapRitual.java b/Mage.Sets/src/mage/cards/d/DeathreapRitual.java index 5761d43d37b..afe72c7fbdd 100644 --- a/Mage.Sets/src/mage/cards/d/DeathreapRitual.java +++ b/Mage.Sets/src/mage/cards/d/DeathreapRitual.java @@ -20,7 +20,7 @@ public final class DeathreapRitual extends CardImpl { public DeathreapRitual(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}{G}"); - // Morbid - At the beginning of each end step, if a creature died this turn, you may draw a card. + // Morbid — At the beginning of each end step, if a creature died this turn, you may draw a card. this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), TargetController.ANY, MorbidCondition.instance, true)); } diff --git a/Mage.Sets/src/mage/cards/d/Dispatch.java b/Mage.Sets/src/mage/cards/d/Dispatch.java index fe769a5fe18..6fd5815ce9e 100644 --- a/Mage.Sets/src/mage/cards/d/Dispatch.java +++ b/Mage.Sets/src/mage/cards/d/Dispatch.java @@ -22,7 +22,7 @@ public final class Dispatch extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}"); this.getSpellAbility().addEffect(new TapTargetEffect()); - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ExileTargetEffect(), MetalcraftCondition.instance, "Metalcraft - If you control three or more artifacts, exile that creature")); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ExileTargetEffect(), MetalcraftCondition.instance, "Metalcraft — If you control three or more artifacts, exile that creature")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/d/DispenseJustice.java b/Mage.Sets/src/mage/cards/d/DispenseJustice.java index 16ea025c058..98da9261938 100644 --- a/Mage.Sets/src/mage/cards/d/DispenseJustice.java +++ b/Mage.Sets/src/mage/cards/d/DispenseJustice.java @@ -43,7 +43,7 @@ public final class DispenseJustice extends CardImpl { class DispenseJusticeEffect extends OneShotEffect { private static final String effectText = "Target player sacrifices an attacking creature.\r\n\r\n" - + "Metalcraft - That player sacrifices two attacking creatures instead if you control three or more artifacts"; + + "Metalcraft — That player sacrifices two attacking creatures instead if you control three or more artifacts"; private static final FilterAttackingCreature filter = new FilterAttackingCreature(); diff --git a/Mage.Sets/src/mage/cards/e/EchoStorm.java b/Mage.Sets/src/mage/cards/e/EchoStorm.java new file mode 100644 index 00000000000..3a7277c482d --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EchoStorm.java @@ -0,0 +1,36 @@ +package mage.cards.e; + +import java.util.UUID; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.keyword.CommanderStormAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetArtifactPermanent; + +/** + * + * @author TheElk801 + */ +public final class EchoStorm extends CardImpl { + + public EchoStorm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}{U}"); + + // When you cast this spell, copy it for each time you've cast your commander from the command zone this game. You may choose new targets for the copies. + this.addAbility(new CommanderStormAbility()); + + // Create a token that's a copy of target artifact. + this.getSpellAbility().addEffect(new CreateTokenCopyTargetEffect()); + this.getSpellAbility().addTarget(new TargetArtifactPermanent()); + } + + public EchoStorm(final EchoStorm card) { + super(card); + } + + @Override + public EchoStorm copy() { + return new EchoStorm(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EmpyrialStorm.java b/Mage.Sets/src/mage/cards/e/EmpyrialStorm.java new file mode 100644 index 00000000000..1b96845f052 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EmpyrialStorm.java @@ -0,0 +1,35 @@ +package mage.cards.e; + +import java.util.UUID; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.CommanderStormAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.AngelToken; + +/** + * + * @author TheElk801 + */ +public final class EmpyrialStorm extends CardImpl { + + public EmpyrialStorm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}{W}"); + + // When you cast this spell, copy it for each time you've cast your commander from the command zone this game. + this.addAbility(new CommanderStormAbility()); + + // Create a 4/4 white Angel creature token with flying. + this.getSpellAbility().addEffect(new CreateTokenEffect(new AngelToken())); + } + + public EmpyrialStorm(final EmpyrialStorm card) { + super(card); + } + + @Override + public EmpyrialStorm copy() { + return new EmpyrialStorm(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EndlessAtlas.java b/Mage.Sets/src/mage/cards/e/EndlessAtlas.java new file mode 100644 index 00000000000..022cb4b44e3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EndlessAtlas.java @@ -0,0 +1,74 @@ +package mage.cards.e; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author TheElk801 + */ +public final class EndlessAtlas extends CardImpl { + + public EndlessAtlas(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + // {2}, {T}: Draw a card. Activate this ability only if you control three or more lands with the same name. + Ability ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, + new DrawCardSourceControllerEffect(1), + new GenericManaCost(2), + new EndlessAtlasCondition() + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + public EndlessAtlas(final EndlessAtlas card) { + super(card); + } + + @Override + public EndlessAtlas copy() { + return new EndlessAtlas(this); + } +} + +class EndlessAtlasCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + Map landMap = new HashMap(); + for (Permanent land : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + source.getControllerId(), game + )) { + if (land != null) { + int landCount = landMap.getOrDefault(land.getName(), 0); + if (landCount > 1) { + return true; + } + landMap.put(land.getName(), landCount + 1); + } + } + return false; + } + + @Override + public String toString() { + return "you control three or more lands with the same name"; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EntreatTheDead.java b/Mage.Sets/src/mage/cards/e/EntreatTheDead.java new file mode 100644 index 00000000000..86d76b7b4a5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EntreatTheDead.java @@ -0,0 +1,59 @@ +package mage.cards.e; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.keyword.MiracleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureCard; +import mage.game.Game; +import mage.target.Target; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author TheElk801 + */ +public final class EntreatTheDead extends CardImpl { + + public EntreatTheDead(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{B}{B}{B}"); + + // Return X target creature cards from your graveyard to the battlefield. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(1, StaticFilters.FILTER_CARD_CREATURE)); + + // Miracle {X}{B}{B} + this.addAbility(new MiracleAbility(this, new ManaCostsImpl("{X}{B}{B}"))); + + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SpellAbility) { + ability.getTargets().clear(); + int xValue = ability.getManaCostsToPay().getX(); + String filterName = xValue + + (xValue != 1 ? " creature cards" : "creature card") + + " from your graveyard"; + Target target = new TargetCardInYourGraveyard( + xValue, new FilterCreatureCard(filterName) + ); + ability.addTarget(target); + } + } + + public EntreatTheDead(final EntreatTheDead card) { + super(card); + } + + @Override + public EntreatTheDead copy() { + return new EntreatTheDead(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EstridTheMasked.java b/Mage.Sets/src/mage/cards/e/EstridTheMasked.java new file mode 100644 index 00000000000..fee3b6e092c --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EstridTheMasked.java @@ -0,0 +1,160 @@ +package mage.cards.e; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.CanBeYourCommanderAbility; +import mage.abilities.common.PlanswalkerEntersWithLoyalityCountersAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; +import mage.abilities.effects.common.UntapAllControllerEffect; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterEnchantmentCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.EnchantedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.MaskToken; +import mage.players.Player; +import mage.target.TargetPermanent; + +/** + * + * @author TheElk801 + */ +public final class EstridTheMasked extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(); + private static final FilterPermanent filter2 = new FilterPermanent("another permanent"); + + static { + filter.add(new EnchantedPredicate()); + filter2.add(new AnotherPredicate()); + } + + public EstridTheMasked(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{G}{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ESTRID); + this.addAbility(new PlanswalkerEntersWithLoyalityCountersAbility(3)); + + // +2: Untap each enchanted permanent you control. + this.addAbility(new LoyaltyAbility(new UntapAllControllerEffect( + filter, "untap each enchanted permanent you control" + ), 2)); + + // -1: Create a white Aura enchantment token named Mask attached to another target permanent. The token has enchant permanent and totem armor. + Ability ability = new LoyaltyAbility( + new EstridTheMaskedTokenEffect(), -1 + ); + ability.addTarget(new TargetPermanent(filter2)); + this.addAbility(ability); + + // -7: Put the top seven cards of your library into your graveyard. Return all non-Aura enchantment cards from your graveyard to the battlefield, then do the same for Aura cards. + this.addAbility(new LoyaltyAbility( + new EstridTheMaskedGraveyardEffect(), -7 + )); + + // Estrid, the Masked can be your commander. + this.addAbility(CanBeYourCommanderAbility.getInstance()); + } + + public EstridTheMasked(final EstridTheMasked card) { + super(card); + } + + @Override + public EstridTheMasked copy() { + return new EstridTheMasked(this); + } +} + +class EstridTheMaskedTokenEffect extends OneShotEffect { + + public EstridTheMaskedTokenEffect() { + super(Outcome.Benefit); + this.staticText = "create a white Aura enchantment token named Mask " + + "attached to another target permanent. " + + "The token has enchant permanent and totem armor"; + } + + public EstridTheMaskedTokenEffect(final EstridTheMaskedTokenEffect effect) { + super(effect); + } + + @Override + public EstridTheMaskedTokenEffect copy() { + return new EstridTheMaskedTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + CreateTokenEffect effect = new CreateTokenEffect(new MaskToken()); + effect.apply(game, source); + for (UUID tokenId : effect.getLastAddedTokenIds()) { + Permanent token = game.getPermanent(tokenId); + if (token == null) { + continue; + } + token.attachTo(source.getFirstTarget(), game); + } + return true; + } +} + +class EstridTheMaskedGraveyardEffect extends OneShotEffect { + + private static final FilterEnchantmentCard filter + = new FilterEnchantmentCard(); + private static final FilterEnchantmentCard filter2 + = new FilterEnchantmentCard(); + + static { + filter.add(Predicates.not(new SubtypePredicate(SubType.AURA))); + filter.add(new SubtypePredicate(SubType.AURA)); + } + + public EstridTheMaskedGraveyardEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "put the top seven cards of your library " + + "into your graveyard. Return all non-Aura enchantment cards " + + "from your graveyard to the battlefield, " + + "then do the same for Aura cards"; + } + + public EstridTheMaskedGraveyardEffect(final EstridTheMaskedGraveyardEffect effect) { + super(effect); + } + + @Override + public EstridTheMaskedGraveyardEffect copy() { + return new EstridTheMaskedGraveyardEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + new PutTopCardOfLibraryIntoGraveControllerEffect(7).apply(game, source); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + controller.moveCards(controller.getGraveyard().getCards( + filter, source.getSourceId(), source.getControllerId(), game + ), Zone.BATTLEFIELD, source, game); + controller.moveCards(controller.getGraveyard().getCards( + filter2, source.getSourceId(), source.getControllerId(), game + ), Zone.BATTLEFIELD, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EstridsInvocation.java b/Mage.Sets/src/mage/cards/e/EstridsInvocation.java new file mode 100644 index 00000000000..0f6094c73a0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EstridsInvocation.java @@ -0,0 +1,105 @@ +package mage.cards.e; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopyPermanentEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.util.functions.ApplyToPermanent; + +/** + * + * @author TheElk801 + */ +public final class EstridsInvocation extends CardImpl { + + public EstridsInvocation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + // You may have Estrid's Invocation enter the battlefield as a copy of any enchantment you control, except it gains "At the beginning of your upkeep, you may exile this enchantment. If you do, return it to the battlefield under its owner's control." + this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect( + StaticFilters.FILTER_ENCHANTMENT_PERMANENT, + new EstridsInvocationApplier() + ).setText("as a copy of any enchantment you control, except it gains " + + "\"At the beginning of your upkeep, " + + "you may exile this enchantment. " + + "If you do, return it to the battlefield " + + "under its owner's control.\""), true + )); + } + + public EstridsInvocation(final EstridsInvocation card) { + super(card); + } + + @Override + public EstridsInvocation copy() { + return new EstridsInvocation(this); + } +} + +class EstridsInvocationApplier extends ApplyToPermanent { + + @Override + public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { + // At the beginning of your upkeep, you may exile this enchantment. If you do, return it to the battlefield under its owner's control. + permanent.addAbility(new BeginningOfUpkeepTriggeredAbility( + new EstridsInvocationEffect(), TargetController.YOU, true + ), source.getSourceId(), game); + return true; + } + + @Override + public boolean apply(Game game, MageObject mageObject, Ability source, UUID copyToObjectId) { + // At the beginning of your upkeep, you may exile this enchantment. If you do, return it to the battlefield under its owner's control. + mageObject.getAbilities().add(new BeginningOfUpkeepTriggeredAbility( + new EstridsInvocationEffect(), TargetController.YOU, true + )); + return true; + } + +} + +class EstridsInvocationEffect extends OneShotEffect { + + public EstridsInvocationEffect() { + super(Outcome.Neutral); + this.staticText = "you may exile this enchantment. " + + "If you do, return it to the battlefield under its owner's control"; + } + + public EstridsInvocationEffect(final EstridsInvocationEffect effect) { + super(effect); + } + + @Override + public EstridsInvocationEffect copy() { + return new EstridsInvocationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + if (permanent.moveToExile(source.getSourceId(), "Estrid's Invocation", source.getSourceId(), game)) { + Card card = game.getExile().getCard(source.getSourceId(), game); + if (card != null) { + return card.moveToZone(Zone.BATTLEFIELD, source.getSourceId(), game, false); + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EverWatchingThreshold.java b/Mage.Sets/src/mage/cards/e/EverWatchingThreshold.java new file mode 100644 index 00000000000..64cf1a648ab --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EverWatchingThreshold.java @@ -0,0 +1,81 @@ +package mage.cards.e; + +import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author TheElk801 + */ +public final class EverWatchingThreshold extends CardImpl { + + public EverWatchingThreshold(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + // Whenever an opponent attacks you and/or a planeswalker you control with one or more creatures, draw a card. + this.addAbility(new EverWatchingThresholdTriggeredAbility()); + } + + public EverWatchingThreshold(final EverWatchingThreshold card) { + super(card); + } + + @Override + public EverWatchingThreshold copy() { + return new EverWatchingThreshold(this); + } +} + +class EverWatchingThresholdTriggeredAbility extends TriggeredAbilityImpl { + + public EverWatchingThresholdTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false); + } + + public EverWatchingThresholdTriggeredAbility(final EverWatchingThresholdTriggeredAbility ability) { + super(ability); + } + + @Override + public EverWatchingThresholdTriggeredAbility copy() { + return new EverWatchingThresholdTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Player player = game.getPlayer(this.getControllerId()); + if (player == null) { + return false; + } + for (UUID attacker : game.getCombat().getAttackers()) { + Permanent creature = game.getPermanent(attacker); + if (creature != null + && player.hasOpponent(creature.getControllerId(), game) + && player.getId().equals(game.getCombat().getDefendingPlayerId(attacker, game))) { + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever an opponent attacks you " + + "and/or a planeswalker you control " + + "with one or more creatures, draw a card."; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EzurisBrigade.java b/Mage.Sets/src/mage/cards/e/EzurisBrigade.java index 1235e8f298a..1602de0fe62 100644 --- a/Mage.Sets/src/mage/cards/e/EzurisBrigade.java +++ b/Mage.Sets/src/mage/cards/e/EzurisBrigade.java @@ -24,7 +24,7 @@ import mage.constants.Zone; * @author Loki */ public final class EzurisBrigade extends CardImpl { - private static final String text = "Metalcraft - As long as you control three or more artifacts, Ezuri's Brigade gets +4/+4 and has trample"; + private static final String text = "Metalcraft — As long as you control three or more artifacts, Ezuri's Brigade gets +4/+4 and has trample"; public EzurisBrigade (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}"); diff --git a/Mage.Sets/src/mage/cards/f/FesterhideBoar.java b/Mage.Sets/src/mage/cards/f/FesterhideBoar.java index fb053b0f709..9e80b1494ad 100644 --- a/Mage.Sets/src/mage/cards/f/FesterhideBoar.java +++ b/Mage.Sets/src/mage/cards/f/FesterhideBoar.java @@ -28,7 +28,7 @@ public final class FesterhideBoar extends CardImpl { this.toughness = new MageInt(3); this.addAbility(TrampleAbility.getInstance()); - // Morbid - Festerhide Boar enters the battlefield with two +1/+1 counters on it if a creature died this turn. + // Morbid — Festerhide Boar enters the battlefield with two +1/+1 counters on it if a creature died this turn. this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), MorbidCondition.instance, ""), "with two +1/+1 counters on it if a creature died this turn")); } diff --git a/Mage.Sets/src/mage/cards/f/FreneticEfreet.java b/Mage.Sets/src/mage/cards/f/FreneticEfreet.java index 836e1fd82e7..b2f407420be 100644 --- a/Mage.Sets/src/mage/cards/f/FreneticEfreet.java +++ b/Mage.Sets/src/mage/cards/f/FreneticEfreet.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -25,16 +24,20 @@ import mage.players.Player; public final class FreneticEfreet extends CardImpl { public FreneticEfreet(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); this.subtype.add(SubType.EFREET); this.power = new MageInt(2); this.toughness = new MageInt(1); // Flying this.addAbility(FlyingAbility.getInstance()); - + // {0}: Flip a coin. If you win the flip, Frenetic Efreet phases out. If you lose the flip, sacrifice Frenetic Efreet. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new FreneticEfreetEffect(), new GenericManaCost(0))); + this.addAbility(new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new FreneticEfreetEffect(), + new GenericManaCost(0) + )); } public FreneticEfreet(final FreneticEfreet card) { @@ -50,8 +53,9 @@ public final class FreneticEfreet extends CardImpl { class FreneticEfreetEffect extends OneShotEffect { public FreneticEfreetEffect() { - super(Outcome.Damage); - staticText = "Flip a coin. If you win the flip, {this} phases out. If you lose the flip, sacrifice {this}"; + super(Outcome.Neutral); + staticText = "Flip a coin. If you win the flip, " + + "{this} phases out. If you lose the flip, sacrifice {this}"; } public FreneticEfreetEffect(FreneticEfreetEffect effect) { @@ -62,15 +66,19 @@ class FreneticEfreetEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && permanent != null) { - if (controller.flipCoin(game)) { - return permanent.phaseOut(game); - } else { - permanent.sacrifice(source.getSourceId(), game); - return true; - } + if (controller == null) { + return false; + } + boolean flip = controller.flipCoin(game); + if (permanent == null) { + return false; + } + if (flip) { + return permanent.phaseOut(game); + } else { + permanent.sacrifice(source.getSourceId(), game); + return true; } - return false; } @Override diff --git a/Mage.Sets/src/mage/cards/f/FuryStorm.java b/Mage.Sets/src/mage/cards/f/FuryStorm.java new file mode 100644 index 00000000000..ce1ad8bd1ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FuryStorm.java @@ -0,0 +1,37 @@ +package mage.cards.f; + +import java.util.UUID; +import mage.abilities.effects.common.CopyTargetSpellEffect; +import mage.abilities.keyword.CommanderStormAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetSpell; + +/** + * + * @author TheElk801 + */ +public final class FuryStorm extends CardImpl { + + public FuryStorm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}{R}"); + + // When you cast this spell, copy it for each time you've cast your commander from the command zone this game. You may choose new targets for the copies. + this.addAbility(new CommanderStormAbility()); + + // Copy target instant or sorcery spell. You may choose new targets for the copy. + this.getSpellAbility().addEffect(new CopyTargetSpellEffect()); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY)); + } + + public FuryStorm(final FuryStorm card) { + super(card); + } + + @Override + public FuryStorm copy() { + return new FuryStorm(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GalvanicBlast.java b/Mage.Sets/src/mage/cards/g/GalvanicBlast.java index 6f18df57d98..6c696f13df9 100644 --- a/Mage.Sets/src/mage/cards/g/GalvanicBlast.java +++ b/Mage.Sets/src/mage/cards/g/GalvanicBlast.java @@ -16,14 +16,14 @@ import mage.target.common.TargetAnyTarget; */ public final class GalvanicBlast extends CardImpl { - private static final String effectText = "{this} deals 2 damage to anytarget.
Metalcraft - {this} deals 4 damage to that permanent or player instead if you control three or more artifacts"; + private static final String effectText = "{this} deals 2 damage to anytarget.
Metalcraft — {this} deals 4 damage to that permanent or player instead if you control three or more artifacts"; public GalvanicBlast(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); this.color.setRed(true); // Galvanic Blast deals 2 damage to any target. - // Metalcraft - Galvanic Blast deals 4 damage to that creature or player instead if you control three or more artifacts. + // Metalcraft — Galvanic Blast deals 4 damage to that creature or player instead if you control three or more artifacts. this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DamageTargetEffect(4), new DamageTargetEffect(2), MetalcraftCondition.instance, effectText)); this.getSpellAbility().addTarget(new TargetAnyTarget()); } diff --git a/Mage.Sets/src/mage/cards/g/GenesisStorm.java b/Mage.Sets/src/mage/cards/g/GenesisStorm.java new file mode 100644 index 00000000000..48528b01284 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GenesisStorm.java @@ -0,0 +1,91 @@ +package mage.cards.g; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CommanderStormAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author TheElk801 + */ +public final class GenesisStorm extends CardImpl { + + public GenesisStorm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}"); + + // When you cast this spell, copy it for each time you've cast your commander from the command zone this game. + this.addAbility(new CommanderStormAbility()); + + // Reveal cards from the top of your library until you reveal a nonland permanent card. You may put that card onto the battlefield. Then put all cards revealed this way that weren't put onto the battlefield on the bottom of your library in a random order. + this.getSpellAbility().addEffect(new GenesisStormEffect()); + } + + public GenesisStorm(final GenesisStorm card) { + super(card); + } + + @Override + public GenesisStorm copy() { + return new GenesisStorm(this); + } +} + +class GenesisStormEffect extends OneShotEffect { + + public GenesisStormEffect() { + super(Outcome.PlayForFree); + this.staticText = "reveal cards from the top of your library " + + "until you reveal a nonland permanent card. " + + "You may put that card onto the battlefield. " + + "Then put all cards revealed this way " + + "that weren't put onto the battlefield " + + "on the bottom of your library in a random order"; + } + + public GenesisStormEffect(GenesisStormEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards toReveal = new CardsImpl(); + Card nonLandCard = null; + for (Card card : controller.getLibrary().getCards(game)) { + toReveal.add(card); + if (card.isPermanent() && !card.isLand()) { + nonLandCard = card; + break; + } + } + controller.revealCards(source, toReveal, game); + if (nonLandCard != null && controller.chooseUse( + outcome, "Put " + nonLandCard.getLogName() + + " onto the battlefield?", source, game + )) { + controller.moveCards(nonLandCard, Zone.BATTLEFIELD, source, game); + toReveal.remove(nonLandCard); + } + controller.putCardsOnBottomOfLibrary(toReveal, game, source, false); + return true; + } + + @Override + public GenesisStormEffect copy() { + return new GenesisStormEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GhalmasWarden.java b/Mage.Sets/src/mage/cards/g/GhalmasWarden.java index d6db8e9c294..22f8bb494b8 100644 --- a/Mage.Sets/src/mage/cards/g/GhalmasWarden.java +++ b/Mage.Sets/src/mage/cards/g/GhalmasWarden.java @@ -22,7 +22,7 @@ import mage.constants.Zone; */ public final class GhalmasWarden extends CardImpl { - private static final String text = "Metalcraft - Ghalma's Warden gets +2/+2 as long as you control three or more artifacts"; + private static final String text = "Metalcraft — Ghalma's Warden gets +2/+2 as long as you control three or more artifacts"; public GhalmasWarden (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); diff --git a/Mage.Sets/src/mage/cards/g/GravetillerWurm.java b/Mage.Sets/src/mage/cards/g/GravetillerWurm.java index f4af78c066e..dcd6c349670 100644 --- a/Mage.Sets/src/mage/cards/g/GravetillerWurm.java +++ b/Mage.Sets/src/mage/cards/g/GravetillerWurm.java @@ -28,7 +28,7 @@ public final class GravetillerWurm extends CardImpl { this.toughness = new MageInt(4); this.addAbility(TrampleAbility.getInstance()); - // Morbid - Gravetiller Wurm enters the battlefield with four +1/+1 counters on it if a creature died this turn. + // Morbid — Gravetiller Wurm enters the battlefield with four +1/+1 counters on it if a creature died this turn. this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(4)), MorbidCondition.instance, ""), "with four +1/+1 counters on it if a creature died this turn")); } diff --git a/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java b/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java index 9c9789fd37d..f3221cdf1aa 100644 --- a/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java +++ b/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java @@ -31,12 +31,12 @@ public final class GruesomeDiscovery extends CardImpl { // Target player discards two cards. - // Morbid - If a creature died this turn, instead that player reveals their hand, you choose two cards from it, then that player discards those cards. + // Morbid — If a creature died this turn, instead that player reveals their hand, you choose two cards from it, then that player discards those cards. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new GruesomeDiscoveryEffect(), new DiscardTargetEffect(2), MorbidCondition.instance, - "Target player discards two cards. Morbid - If a creature died this turn, instead that player reveals their hand, you choose two cards from it, then that player discards those cards")); + "Target player discards two cards. Morbid — If a creature died this turn, instead that player reveals their hand, you choose two cards from it, then that player discards those cards")); this.getSpellAbility().addTarget(new TargetPlayer()); } diff --git a/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java b/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java new file mode 100644 index 00000000000..b7224b44243 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java @@ -0,0 +1,129 @@ +/* + * 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.g; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.dynamicvalue.common.ManaSpentToCastCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author Will + */ +public final class GyrusWakerOfCorpses extends CardImpl { + + private static final FilterCreatureCard filter = new FilterCreatureCard("target creature card with lesser power from your graveyard"); + + static { + filter.add(new GyrusWakerOfCorpsesPowerLessThanSourcePredicate()); + } + + public GyrusWakerOfCorpses(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{B}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HYDRA); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Gyrus, Walker of Corpses enters the battlefield with a number of +1/+1 counters on it equal to the amount of mana spent to cast it. + Effect effect = new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), new ManaSpentToCastCount(), true); + effect.setText("with a number of +1/+1 counters on it equal to the amount of mana spent to cast it"); + this.addAbility(new EntersBattlefieldAbility(effect)); + + // Whenever Gyrus attacks, you may exile target creature card with lesser power from your graveyard. If you do, create a token that’s a copy of that card and that’s tapped and attacking. Exile the token at the end of combat. + Ability ability = new AttacksTriggeredAbility(new GyrusWakerOfCorpsesEffect(), true); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + public GyrusWakerOfCorpses(final GyrusWakerOfCorpses card) { + super(card); + } + + @Override + public GyrusWakerOfCorpses copy() { + return new GyrusWakerOfCorpses(this); + } +} + +class GyrusWakerOfCorpsesEffect extends OneShotEffect { + + public GyrusWakerOfCorpsesEffect() { + super(Outcome.Copy); + this.staticText = "exile target creature card with lesser power from your graveyard. If you do, create a token that’s a copy of that card and that’s tapped and attacking. Exile the token at the end of combat."; + } + + public GyrusWakerOfCorpsesEffect(final GyrusWakerOfCorpsesEffect effect) { + super(effect); + } + + @Override + public GyrusWakerOfCorpsesEffect copy() { + return new GyrusWakerOfCorpsesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + + Card card = game.getCard(source.getFirstTarget()); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null || card == null) { + return false; + } + controller.moveCards(card, Zone.EXILED, source, game); + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true, 1, true, true); + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + Effect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), false).apply(game, source); + } + return true; + } +} + +class GyrusWakerOfCorpsesPowerLessThanSourcePredicate implements ObjectSourcePlayerPredicate> { + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(input.getSourceId()); + return sourcePermanent != null && input.getObject().getPower().getValue() < sourcePermanent.getPower().getValue(); + } + + @Override + public String toString() { + return "lesser power"; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeavenlyBlademaster.java b/Mage.Sets/src/mage/cards/h/HeavenlyBlademaster.java new file mode 100644 index 00000000000..ee4ee6d4683 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeavenlyBlademaster.java @@ -0,0 +1,129 @@ +package mage.cards.h; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.AdditiveDynamicValue; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.AuraAttachedCount; +import mage.abilities.dynamicvalue.common.EquipmentAttachedCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; + +/** + * + * @author TheElk801 + */ +public final class HeavenlyBlademaster extends CardImpl { + + public HeavenlyBlademaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}"); + + this.subtype.add(SubType.ANGEL); + this.power = new MageInt(3); + this.toughness = new MageInt(6); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // When Heavenly Blademaster enters the battlefield, you may attach any number of Auras and Equipment you control to it. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new HeavenlyBlademasterEffect(), true + )); + + // Other creatures you control get +1/+1 for each Aura and Equipment attached to Heavenly Blademaster. + DynamicValue totalAmount = new AdditiveDynamicValue( + new EquipmentAttachedCount(1), + new AuraAttachedCount(1) + ); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new BoostControlledEffect( + totalAmount, totalAmount, Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURES, true + ) + )); + } + + public HeavenlyBlademaster(final HeavenlyBlademaster card) { + super(card); + } + + @Override + public HeavenlyBlademaster copy() { + return new HeavenlyBlademaster(this); + } +} + +class HeavenlyBlademasterEffect extends OneShotEffect { + + private static final FilterPermanent filter + = new FilterControlledPermanent("Aura or Equipment you control"); + + static { + filter.add(Predicates.or( + new SubtypePredicate(SubType.AURA), + new SubtypePredicate(SubType.EQUIPMENT) + )); + } + + public HeavenlyBlademasterEffect() { + super(Outcome.Benefit); + this.staticText = "you may attach any number " + + "of Auras and Equipment you control to it"; + } + + public HeavenlyBlademasterEffect(final HeavenlyBlademasterEffect effect) { + super(effect); + } + + @Override + public HeavenlyBlademasterEffect copy() { + return new HeavenlyBlademasterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + Player player = game.getPlayer(source.getControllerId()); + if (permanent == null || player == null) { + return false; + } + Target target = new TargetPermanent(0, Integer.MAX_VALUE, filter, true); + if (!player.choose(outcome, target, source.getSourceId(), game)) { + return false; + } + target.getTargets().stream().map( + attachmentId -> game.getPermanent(attachmentId) + ).filter( + attachment -> attachment != null + ).forEachOrdered((attachment) -> { + attachment.attachTo(permanent.getId(), game); + }); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeraldsHorn.java b/Mage.Sets/src/mage/cards/h/HeraldsHorn.java index 7172601b325..1dfbd2b6ec3 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldsHorn.java +++ b/Mage.Sets/src/mage/cards/h/HeraldsHorn.java @@ -79,7 +79,7 @@ class HeraldsHornEffect extends OneShotEffect { // If it's a creature card of the chosen type, you may reveal it and put it into your hand. FilterCreatureCard filter = new FilterCreatureCard("creature card of the chosen type"); - filter.add(new ChosenSubtypePredicate(source.getSourceId())); + filter.add(new ChosenSubtypePredicate()); String message = "Reveal the top card of your library and put that card into your hand?"; if (card != null) { if (filter.match(card, game) && controller.chooseUse(Outcome.Benefit, message, source, game)) { diff --git a/Mage.Sets/src/mage/cards/h/HollowhengeScavenger.java b/Mage.Sets/src/mage/cards/h/HollowhengeScavenger.java index 339ef6426ad..0da64ee8dbd 100644 --- a/Mage.Sets/src/mage/cards/h/HollowhengeScavenger.java +++ b/Mage.Sets/src/mage/cards/h/HollowhengeScavenger.java @@ -19,7 +19,7 @@ import mage.constants.SubType; */ public final class HollowhengeScavenger extends CardImpl { - private static final String staticText = "Morbid - When {this} enters the battlefield, if a creature died this turn, you gain 5 life."; + private static final String staticText = "Morbid — When {this} enters the battlefield, if a creature died this turn, you gain 5 life."; public HollowhengeScavenger(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}{G}"); @@ -28,7 +28,7 @@ public final class HollowhengeScavenger extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(5); - // Morbid - When Hollowhenge Scavenger enters the battlefield, if a creature died this turn, you gain 5 life. + // Morbid — When Hollowhenge Scavenger enters the battlefield, if a creature died this turn, you gain 5 life. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new GainLifeEffect(5)); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MorbidCondition.instance, staticText)); } diff --git a/Mage.Sets/src/mage/cards/h/HungerOfTheHowlpack.java b/Mage.Sets/src/mage/cards/h/HungerOfTheHowlpack.java index 5995ac16c07..c29819bd733 100644 --- a/Mage.Sets/src/mage/cards/h/HungerOfTheHowlpack.java +++ b/Mage.Sets/src/mage/cards/h/HungerOfTheHowlpack.java @@ -22,13 +22,13 @@ public final class HungerOfTheHowlpack extends CardImpl { // Put a +1/+1 counter on target creature. - // Morbid - Put three +1/+1 counters on that creature instead if a creature died this turn. + // Morbid — Put three +1/+1 counters on that creature instead if a creature died this turn. this.getSpellAbility().addEffect( new ConditionalOneShotEffect( new AddCountersTargetEffect(CounterType.P1P1.createInstance(3)), new AddCountersTargetEffect(CounterType.P1P1.createInstance()), MorbidCondition.instance, - "Put a +1/+1 counter on target creature. Morbid - Put three +1/+1 counters on that creature instead if a creature died this turn")); + "Put a +1/+1 counter on target creature. Morbid — Put three +1/+1 counters on that creature instead if a creature died this turn")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/i/IndomitableArchangel.java b/Mage.Sets/src/mage/cards/i/IndomitableArchangel.java index edc9cec8cb2..2a1e889e73d 100644 --- a/Mage.Sets/src/mage/cards/i/IndomitableArchangel.java +++ b/Mage.Sets/src/mage/cards/i/IndomitableArchangel.java @@ -25,7 +25,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate; */ public final class IndomitableArchangel extends CardImpl { - private static final String rule = "Metalcraft - Artifacts you control have shroud as long as you control three or more artifacts."; + private static final String rule = "Metalcraft — Artifacts you control have shroud as long as you control three or more artifacts."; private static final FilterPermanent filter = new FilterPermanent("Artifacts"); diff --git a/Mage.Sets/src/mage/cards/i/IsolatedWatchtower.java b/Mage.Sets/src/mage/cards/i/IsolatedWatchtower.java new file mode 100644 index 00000000000..f3307da13fb --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IsolatedWatchtower.java @@ -0,0 +1,118 @@ +package mage.cards.i; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author TheElk801 + */ +public final class IsolatedWatchtower extends CardImpl { + + public IsolatedWatchtower(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {2}, {T}: Scry 1, then you may reveal the top card of your library. If a basic land card is revealed this way, put it onto the battlefield tapped. Activate this ability only if an opponent controls at least two more lands than you. + Ability ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, + new IsolatedWatchtowerEffect(), + new GenericManaCost(2), + new IsolatedWatchtowerCondition() + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + public IsolatedWatchtower(final IsolatedWatchtower card) { + super(card); + } + + @Override + public IsolatedWatchtower copy() { + return new IsolatedWatchtower(this); + } +} + +class IsolatedWatchtowerEffect extends OneShotEffect { + + public IsolatedWatchtowerEffect() { + super(Outcome.Benefit); + this.staticText = "scry 1, then you may reveal the top card " + + "of your library. If a basic land card is revealed this way, " + + "put it onto the battlefield tapped"; + } + + public IsolatedWatchtowerEffect(final IsolatedWatchtowerEffect effect) { + super(effect); + } + + @Override + public IsolatedWatchtowerEffect copy() { + return new IsolatedWatchtowerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.scry(1, source, game); + if (!player.chooseUse( + outcome, "Reveal the top card of your library?", source, game + )) { + return true; + } + Card card = player.getLibrary().getFromTop(game); + player.revealCards(source, new CardsImpl(card), game); + if (card.isBasic() && card.isLand()) { + player.moveCards( + card, Zone.BATTLEFIELD, source, + game, true, false, true, null + ); + } + return true; + } +} + +class IsolatedWatchtowerCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + int numLands = game.getBattlefield().countAll( + StaticFilters.FILTER_LAND, source.getControllerId(), game + ); + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + if (numLands < 1 + game.getBattlefield().countAll( + StaticFilters.FILTER_LAND, opponentId, game + )) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "an opponent controls at least two more lands than you"; + } +} diff --git a/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java b/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java index 19cde3a53bf..0a9910044d7 100644 --- a/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java +++ b/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java @@ -23,7 +23,7 @@ import mage.filter.StaticFilters; */ public final class JorKadeenThePrevailer extends CardImpl { - private static final String effectText = "Metalcraft - Creatures you control get +3/+0 as long as you control three or more artifacts."; + private static final String effectText = "Metalcraft — Creatures you control get +3/+0 as long as you control three or more artifacts."; public JorKadeenThePrevailer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); @@ -37,7 +37,7 @@ public final class JorKadeenThePrevailer extends CardImpl { // First strike this.addAbility(FirstStrikeAbility.getInstance()); - // Metalcraft - Creatures you control get +3/+0 as long as you control three or more artifacts. + // Metalcraft — Creatures you control get +3/+0 as long as you control three or more artifacts. ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new BoostControlledEffect(3, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE, false), MetalcraftCondition.instance, effectText); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); diff --git a/Mage.Sets/src/mage/cards/k/KestiaTheCultivator.java b/Mage.Sets/src/mage/cards/k/KestiaTheCultivator.java new file mode 100644 index 00000000000..18f4dfcdc3b --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KestiaTheCultivator.java @@ -0,0 +1,74 @@ +package mage.cards.k; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.AttacksAllTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.abilities.keyword.BestowAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SetTargetPointer; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.predicate.permanent.EnchantedPredicate; + +/** + * + * @author TheElk801 + */ +public final class KestiaTheCultivator extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("an enchanted creature or enchantment creature you control"); + + static { + filter.add(Predicates.or( + new EnchantedPredicate(), + new CardTypePredicate(CardType.ENCHANTMENT) + )); + filter.add(new ControllerPredicate(TargetController.YOU)); + } + + public KestiaTheCultivator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{G}{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.NYMPH); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Bestow {3}{G}{W}{U} + this.addAbility(new BestowAbility(this, "{3}{G}{W}{U}")); + + // Enchanted creature gets +4/+4. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new BoostEnchantedEffect(4, 4, Duration.WhileOnBattlefield) + )); + + // Whenever an enchanted creature or enchantment creature you control attacks, draw a card. + this.addAbility(new AttacksAllTriggeredAbility( + new DrawCardSourceControllerEffect(1), + false, filter, SetTargetPointer.NONE, false + )); + } + + public KestiaTheCultivator(final KestiaTheCultivator card) { + super(card); + } + + @Override + public KestiaTheCultivator copy() { + return new KestiaTheCultivator(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KindredBoon.java b/Mage.Sets/src/mage/cards/k/KindredBoon.java index a61037e021e..979ade1ccf8 100644 --- a/Mage.Sets/src/mage/cards/k/KindredBoon.java +++ b/Mage.Sets/src/mage/cards/k/KindredBoon.java @@ -42,7 +42,7 @@ public final class KindredBoon extends CardImpl { // {1}{W}: Put a divinity counter on target creature you control of the chosen type. FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature you control of the chosen type"); - filter.add(new ChosenSubtypePredicate(this.getId())); + filter.add(new ChosenSubtypePredicate()); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.DIVINITY.createInstance()), new ManaCostsImpl("{1}{W}")); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/k/KindredDiscovery.java b/Mage.Sets/src/mage/cards/k/KindredDiscovery.java index 34ddc649653..9dc8464b32d 100644 --- a/Mage.Sets/src/mage/cards/k/KindredDiscovery.java +++ b/Mage.Sets/src/mage/cards/k/KindredDiscovery.java @@ -27,7 +27,7 @@ public final class KindredDiscovery extends CardImpl { // Whenever a creature you control of the chosen type enters the battlefield or attacks, draw a card. FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a creature you control of the chosen type"); - filter.add(new ChosenSubtypePredicate(this.getId())); + filter.add(new ChosenSubtypePredicate()); this.addAbility(new EntersBattlefieldOrAttacksAllTriggeredAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), filter, false)); } diff --git a/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java b/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java index 278caca7917..37a4218383e 100644 --- a/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java +++ b/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java @@ -37,7 +37,7 @@ public final class KuldothaPhoenix extends CardImpl { this.addAbility(FlyingAbility.getInstance()); this.addAbility(HasteAbility.getInstance()); - // Metalcraft - {4}: Return Kuldotha Phoenix from your graveyard to the battlefield. + // Metalcraft — {4}: Return Kuldotha Phoenix from your graveyard to the battlefield. // Activate this ability only during your upkeep and only if you control three or more artifacts. Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), diff --git a/Mage.Sets/src/mage/cards/l/LordWindgrace.java b/Mage.Sets/src/mage/cards/l/LordWindgrace.java new file mode 100644 index 00000000000..3cbd21e70f2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LordWindgrace.java @@ -0,0 +1,112 @@ +package mage.cards.l; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.CanBeYourCommanderAbility; +import mage.abilities.common.PlanswalkerEntersWithLoyalityCountersAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.Card; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.filter.common.FilterLandCard; +import mage.filter.predicate.other.OwnerPredicate; +import mage.game.Game; +import mage.game.permanent.token.CatWarriorToken; +import mage.players.Player; +import mage.target.common.TargetCardInASingleGraveyard; +import mage.target.common.TargetNonlandPermanent; + +/** + * + * @author TheElk801 + */ +public final class LordWindgrace extends CardImpl { + + private static final FilterLandCard filter = new FilterLandCard("land cards from your graveyard"); + + static { + filter.add(new OwnerPredicate(TargetController.YOU)); + } + + public LordWindgrace(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{B}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.WINDGRACE); + this.addAbility(new PlanswalkerEntersWithLoyalityCountersAbility(5)); + + // +2: Discard a card, then draw a card. If a land card is discarded this way, draw an additional card. + this.addAbility(new LoyaltyAbility(new LordWindgraceEffect(), 2)); + + // -3: Return up to two target land cards from your graveyard to the battlefield. + Ability ability = new LoyaltyAbility( + new ReturnFromGraveyardToBattlefieldTargetEffect(), -3 + ); + ability.addTarget(new TargetCardInASingleGraveyard(0, 2, filter)); + this.addAbility(ability); + + // -11: Destroy up to six target nonland permanents, then create six 2/2 green Cat Warrior creature tokens with forestwalk. + ability = new LoyaltyAbility(new DestroyTargetEffect(), -11); + ability.addEffect( + new CreateTokenEffect(new CatWarriorToken(), 6) + .setText(", then create six 2/2 green Cat Warrior " + + "creature tokens with forestwalk") + ); + ability.addTarget(new TargetNonlandPermanent(0, 6, false)); + this.addAbility(ability); + + // Lord Windgrace can be your commander. + this.addAbility(CanBeYourCommanderAbility.getInstance()); + } + + public LordWindgrace(final LordWindgrace card) { + super(card); + } + + @Override + public LordWindgrace copy() { + return new LordWindgrace(this); + } +} + +class LordWindgraceEffect extends OneShotEffect { + + public LordWindgraceEffect() { + super(Outcome.Benefit); + this.staticText = "discard a card, then draw a card. " + + "If a land card is discarded this way, draw an additional card"; + } + + public LordWindgraceEffect(final LordWindgraceEffect effect) { + super(effect); + } + + @Override + public LordWindgraceEffect copy() { + return new LordWindgraceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.discardOne(false, source, game); + if (card == null || !card.isLand()) { + player.drawCards(1, game); + } else { + player.drawCards(2, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoyalApprentice.java b/Mage.Sets/src/mage/cards/l/LoyalApprentice.java new file mode 100644 index 00000000000..048afacdf9e --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoyalApprentice.java @@ -0,0 +1,95 @@ +package mage.cards.l; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.common.CommanderInPlayCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.constants.SubType; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.game.Game; +import mage.game.permanent.token.ThopterColorlessToken; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author TheElk801 + */ +public final class LoyalApprentice extends CardImpl { + + public LoyalApprentice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Lieutenant — At the beginning of combat on your turn, if you control your commander, create a 1/1 colorless Thopter artifact creature token with flying. That token gains haste until end of turn. + this.addAbility(new ConditionalTriggeredAbility( + new BeginningOfCombatTriggeredAbility( + new LoyalApprenticeEffect(), + TargetController.YOU, false + ), CommanderInPlayCondition.instance, + "Lieutenant — At the beginning of combat " + + "on your turn, create a 1/1 colorless Thopter " + + "artifact creature token with flying. " + + "That token gains haste until end of turn" + )); + } + + public LoyalApprentice(final LoyalApprentice card) { + super(card); + } + + @Override + public LoyalApprentice copy() { + return new LoyalApprentice(this); + } +} + +class LoyalApprenticeEffect extends OneShotEffect { + + public LoyalApprenticeEffect() { + super(Outcome.Benefit); + } + + public LoyalApprenticeEffect(final LoyalApprenticeEffect effect) { + super(effect); + } + + @Override + public LoyalApprenticeEffect copy() { + return new LoyalApprenticeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + CreateTokenEffect effect = new CreateTokenEffect(new ThopterColorlessToken()); + effect.apply(game, source); + effect.getLastAddedTokenIds().stream().map((tokenId) -> { + ContinuousEffect continuousEffect = new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ); + continuousEffect.setTargetPointer(new FixedTarget(tokenId, game)); + return continuousEffect; + }).forEachOrdered((continuousEffect) -> { + game.addEffect(continuousEffect, source); + }); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoyalGuardian.java b/Mage.Sets/src/mage/cards/l/LoyalGuardian.java new file mode 100644 index 00000000000..9b368b0bac7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoyalGuardian.java @@ -0,0 +1,56 @@ +package mage.cards.l; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.common.CommanderInPlayCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.constants.SubType; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +/** + * + * @author TheElk801 + */ +public final class LoyalGuardian extends CardImpl { + + public LoyalGuardian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.RHINO); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Lieutenant — At the beginning of combat on your turn, if you control your commander, put a +1/+1 counter on each creature you control. + this.addAbility(new ConditionalTriggeredAbility( + new BeginningOfCombatTriggeredAbility( + new AddCountersAllEffect( + CounterType.P1P1.createInstance(), + StaticFilters.FILTER_CONTROLLED_CREATURE + ), TargetController.YOU, false + ), CommanderInPlayCondition.instance, + "Lieutenant — At the beginning of combat " + + "on your turn, if you control your commander, " + + "put a +1/+1 counter on each creature you control." + )); + } + + public LoyalGuardian(final LoyalGuardian card) { + super(card); + } + + @Override + public LoyalGuardian copy() { + return new LoyalGuardian(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java b/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java new file mode 100644 index 00000000000..fe87346014a --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java @@ -0,0 +1,52 @@ +package mage.cards.l; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.common.CommanderInPlayCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.constants.SubType; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; + +/** + * + * @author TheElk801 + */ +public final class LoyalSubordinate extends CardImpl { + + public LoyalSubordinate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Menace + this.addAbility(new MenaceAbility()); + + // Lieutenant — At the beginning of combat on your turn, if you control your commander, each opponent loses 3 life. + this.addAbility(new ConditionalTriggeredAbility( + new BeginningOfCombatTriggeredAbility( + new LoseLifeOpponentsEffect(3), + TargetController.YOU, false + ), CommanderInPlayCondition.instance, + "Lieutenant — At the beginning of combat " + + "on your turn, if you control your commander, " + + "each opponent loses 3 life." + )); + } + + public LoyalSubordinate(final LoyalSubordinate card) { + super(card); + } + + @Override + public LoyalSubordinate copy() { + return new LoyalSubordinate(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoyalUnicorn.java b/Mage.Sets/src/mage/cards/l/LoyalUnicorn.java new file mode 100644 index 00000000000..40b8aec51e1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoyalUnicorn.java @@ -0,0 +1,66 @@ +package mage.cards.l; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.common.CommanderInPlayCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.PreventAllDamageToAllEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.constants.SubType; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TargetController; +import mage.filter.StaticFilters; + +/** + * + * @author TheElk801 + */ +public final class LoyalUnicorn extends CardImpl { + + public LoyalUnicorn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.UNICORN); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Lieutenant — At the beginning of combat on your turn, if you control your commander, prevent all combat damage that would be dealt to creatures you control this turn. Other creatures you control gain vigilance until end of turn. + TriggeredAbility ability = new BeginningOfCombatTriggeredAbility( + new PreventAllDamageToAllEffect( + Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURES, + true + ), TargetController.YOU, false + ); + ability.addEffect(new GainAbilityAllEffect( + VigilanceAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURES, true + )); + this.addAbility(new ConditionalTriggeredAbility( + ability, CommanderInPlayCondition.instance, + "Lieutenant — At the beginning of combat " + + "on your turn, if you control your commander, " + + "prevent all combat damage that would be dealt " + + "to creatures you control this turn. " + + "Other creatures you control gain vigilance until end of turn." + )); + } + + public LoyalUnicorn(final LoyalUnicorn card) { + super(card); + } + + @Override + public LoyalUnicorn copy() { + return new LoyalUnicorn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LumengridDrake.java b/Mage.Sets/src/mage/cards/l/LumengridDrake.java index 6cf66783fb1..884ac2cced3 100644 --- a/Mage.Sets/src/mage/cards/l/LumengridDrake.java +++ b/Mage.Sets/src/mage/cards/l/LumengridDrake.java @@ -20,7 +20,7 @@ import mage.target.common.TargetCreaturePermanent; */ public final class LumengridDrake extends CardImpl { - private static final String ruleText = "Metalcraft - When {this} enters the battlefield, if you control three or more artifacts, return target creature to its owner's hand."; + private static final String ruleText = "Metalcraft — When {this} enters the battlefield, if you control three or more artifacts, return target creature to its owner's hand."; public LumengridDrake(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java b/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java new file mode 100644 index 00000000000..49a0aba1f41 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java @@ -0,0 +1,199 @@ +package mage.cards.m; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.FilterCard; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledLandPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author TheElk801 + */ +public final class MagusOfTheBalance extends CardImpl { + + public MagusOfTheBalance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {4}{W}, {T}, Sacrifice Magus of the Balance: Each player chooses a number of lands they control equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players discard cards and sacrifice creatures the same way. + Ability ability = new SimpleActivatedAbility( + new MagusOfTheBalanceEffect(), + new ManaCostsImpl("{4}{W}") + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + public MagusOfTheBalance(final MagusOfTheBalance card) { + super(card); + } + + @Override + public MagusOfTheBalance copy() { + return new MagusOfTheBalance(this); + } +} + +class MagusOfTheBalanceEffect extends OneShotEffect { + + MagusOfTheBalanceEffect() { + super(Outcome.Sacrifice); + staticText = "each player chooses a number of lands they control " + + "equal to the number of lands controlled by the player " + + "who controls the fewest, then sacrifices the rest. " + + "Players discard cards and sacrifice creatures the same way"; + } + + MagusOfTheBalanceEffect(final MagusOfTheBalanceEffect effect) { + super(effect); + } + + @Override + public MagusOfTheBalanceEffect copy() { + return new MagusOfTheBalanceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + //Lands + int minLand = Integer.MAX_VALUE; + Cards landsToSacrifice = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + int count = game.getBattlefield().countAll(new FilterControlledLandPermanent(), player.getId(), game); + if (count < minLand) { + minLand = count; + } + } + } + + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + TargetControlledPermanent target = new TargetControlledPermanent(minLand, minLand, new FilterControlledLandPermanent("lands to keep"), true); + if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), player.getId(), source.getSourceId(), game)) { + if (permanent != null && !target.getTargets().contains(permanent.getId())) { + landsToSacrifice.add(permanent); + } + } + } + } + } + + for (UUID cardId : landsToSacrifice) { + Permanent permanent = game.getPermanent(cardId); + if (permanent != null) { + permanent.sacrifice(source.getSourceId(), game); + } + } + + //Creatures + int minCreature = Integer.MAX_VALUE; + Cards creaturesToSacrifice = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + int count = game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), player.getId(), game); + if (count < minCreature) { + minCreature = count; + } + } + } + + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + TargetControlledPermanent target = new TargetControlledPermanent(minCreature, minCreature, new FilterControlledCreaturePermanent("creatures to keep"), true); + if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), player.getId(), source.getSourceId(), game)) { + if (permanent != null && !target.getTargets().contains(permanent.getId())) { + creaturesToSacrifice.add(permanent); + } + } + } + } + } + + for (UUID cardId : creaturesToSacrifice) { + Permanent permanent = game.getPermanent(cardId); + if (permanent != null) { + permanent.sacrifice(source.getSourceId(), game); + } + } + + //Cards in hand + int minCard = Integer.MAX_VALUE; + Map cardsToDiscard = new HashMap<>(2); + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + int count = player.getHand().size(); + if (count < minCard) { + minCard = count; + } + } + } + + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + Cards cards = new CardsImpl(); + TargetCardInHand target = new TargetCardInHand(minCard, new FilterCard("cards to keep")); + if (target.choose(Outcome.Discard, player.getId(), source.getSourceId(), game)) { + for (Card card : player.getHand().getCards(game)) { + if (card != null && !target.getTargets().contains(card.getId())) { + cards.add(card); + } + } + cardsToDiscard.put(playerId, cards); + } + } + } + + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null && cardsToDiscard.get(playerId) != null) { + for (UUID cardId : cardsToDiscard.get(playerId)) { + Card card = game.getCard(cardId); + if (card != null) { + player.discard(card, source, game); + } + } + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java b/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java index 7df96dfdce9..56840b6b5b3 100644 --- a/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java +++ b/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java @@ -38,7 +38,7 @@ public final class MaliciousAffliction extends CardImpl { public MaliciousAffliction(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}{B}"); - // Morbid - When you cast Malicious Affliction, if a creature died this turn, you may copy Malicious Affliction and may choose a new target for the copy. + // Morbid — When you cast Malicious Affliction, if a creature died this turn, you may copy Malicious Affliction and may choose a new target for the copy. Ability ability = new ConditionalInterveningIfTriggeredAbility( new CastSourceTriggeredAbility(new CopySourceSpellEffect(), true), new LockedInCondition(MorbidCondition.instance), diff --git a/Mage.Sets/src/mage/cards/m/MetallicMimic.java b/Mage.Sets/src/mage/cards/m/MetallicMimic.java index 58c8282e391..9dfcca6887d 100644 --- a/Mage.Sets/src/mage/cards/m/MetallicMimic.java +++ b/Mage.Sets/src/mage/cards/m/MetallicMimic.java @@ -4,6 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.effects.common.continuous.AddChosenSubtypeEffect; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; @@ -36,6 +37,7 @@ public final class MetallicMimic extends CardImpl { // Metallic Mimic is the chosen type in addition to its other types. ability.addEffect(new EnterAttributeAddChosenSubtypeEffect()); this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AddChosenSubtypeEffect())); // Each other creature you control of the chosen type enters the battlefield with an additional +1/+1 counter on it. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MetallicMimicReplacementEffect())); diff --git a/Mage.Sets/src/mage/cards/m/MirranMettle.java b/Mage.Sets/src/mage/cards/m/MirranMettle.java index d8f0751f0c7..50b66bd8e03 100644 --- a/Mage.Sets/src/mage/cards/m/MirranMettle.java +++ b/Mage.Sets/src/mage/cards/m/MirranMettle.java @@ -18,7 +18,7 @@ import mage.target.common.TargetCreaturePermanent; */ public final class MirranMettle extends CardImpl { - private static final String effectText = "Metalcraft - That creature gets +4/+4 until end of turn instead if you control three or more artifacts."; + private static final String effectText = "Metalcraft — That creature gets +4/+4 until end of turn instead if you control three or more artifacts."; public MirranMettle(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G}"); diff --git a/Mage.Sets/src/mage/cards/m/MirrorOfTheForebears.java b/Mage.Sets/src/mage/cards/m/MirrorOfTheForebears.java index 165770358e9..737385b8fef 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorOfTheForebears.java +++ b/Mage.Sets/src/mage/cards/m/MirrorOfTheForebears.java @@ -38,7 +38,7 @@ public final class MirrorOfTheForebears extends CardImpl { // 1: Until end of turn, Mirror of the Forebears becomes a copy of target creature you control of the chosen type, except it's an artifact in addition to its other types. FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); - filter.add(new ChosenSubtypePredicate(this.getId())); + filter.add(new ChosenSubtypePredicate()); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MirrorOfTheForebearsCopyEffect(), new ManaCostsImpl("{1}")); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MoltenPsyche.java b/Mage.Sets/src/mage/cards/m/MoltenPsyche.java index 0757e2fbd19..bd9f8b6e8de 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenPsyche.java +++ b/Mage.Sets/src/mage/cards/m/MoltenPsyche.java @@ -29,7 +29,7 @@ public final class MoltenPsyche extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}{R}"); // Each player shuffles the cards from their hand into their library, then draws that many cards. - // Metalcraft - If you control three or more artifacts, Molten Psyche deals damage to each opponent equal to the number of cards that player has drawn this turn. + // Metalcraft — If you control three or more artifacts, Molten Psyche deals damage to each opponent equal to the number of cards that player has drawn this turn. this.getSpellAbility().addEffect(new MoltenPsycheEffect()); this.getSpellAbility().addWatcher(new MoltenPsycheWatcher()); } diff --git a/Mage.Sets/src/mage/cards/m/MorkrutBanshee.java b/Mage.Sets/src/mage/cards/m/MorkrutBanshee.java index fba035c3c19..f3d68ccc2c9 100644 --- a/Mage.Sets/src/mage/cards/m/MorkrutBanshee.java +++ b/Mage.Sets/src/mage/cards/m/MorkrutBanshee.java @@ -20,7 +20,7 @@ import mage.target.common.TargetCreaturePermanent; */ public final class MorkrutBanshee extends CardImpl { - private static final String staticText = "Morbid - When {this} enters the battlefield, if a creature died this turn, target creature gets -4/-4 until end of turn."; + private static final String staticText = "Morbid — When {this} enters the battlefield, if a creature died this turn, target creature gets -4/-4 until end of turn."; public MorkrutBanshee(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); @@ -30,7 +30,7 @@ public final class MorkrutBanshee extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // Morbid - When Morkut Banshee enters the battlefield, if a creature died this turn, target creature gets -4/-4 until end of turn. + // Morbid — When Morkut Banshee enters the battlefield, if a creature died this turn, target creature gets -4/-4 until end of turn. TriggeredAbility triggeredAbility = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-4, -4, Duration.EndOfTurn)); TriggeredAbility ability = new ConditionalInterveningIfTriggeredAbility(triggeredAbility, MorbidCondition.instance, staticText); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/m/MythUnbound.java b/Mage.Sets/src/mage/cards/m/MythUnbound.java new file mode 100644 index 00000000000..28924a6075f --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MythUnbound.java @@ -0,0 +1,113 @@ +package mage.cards.m; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.ZoneChangeAllTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.other.OwnerPredicate; +import mage.filter.predicate.permanent.CommanderPredicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author TheElk801 + */ +public final class MythUnbound extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(); + + static { + filter.add(new CommanderPredicate()); + filter.add(new OwnerPredicate(TargetController.YOU)); + } + + public MythUnbound(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + // Your commander costs {1} less to cast for each time it's been cast from the command zone this game. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new MythUnboundCostReductionEffect() + )); + + // Whenever your commander is put into the command zone from anywhere, draw a card. + this.addAbility(new ZoneChangeAllTriggeredAbility( + Zone.BATTLEFIELD, Zone.ALL, Zone.COMMAND, + new DrawCardSourceControllerEffect(1), filter, + "Whenever your commander is put into " + + "the command zone from anywhere, ", false + )); + } + + public MythUnbound(final MythUnbound card) { + super(card); + } + + @Override + public MythUnbound copy() { + return new MythUnbound(this); + } +} + +class MythUnboundCostReductionEffect extends CostModificationEffectImpl { + + MythUnboundCostReductionEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "your commander costs {1} less to cast for each time " + + "it's been cast from the command zone this game"; + } + + MythUnboundCostReductionEffect(MythUnboundCostReductionEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + Ability spellAbility = (SpellAbility) abilityToModify; + if (spellAbility != null) { + Integer amount = (Integer) game.getState().getValue(abilityToModify.getControllerId() + "_castCount"); + if (amount != null && amount > 0) { + CardUtil.reduceCost(spellAbility, amount); + return true; + } + } + return false; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + if (abilityToModify instanceof SpellAbility) { + if (abilityToModify.isControlledBy(source.getControllerId())) { + Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); + if (spell != null) { + return player.getCommandersIds().contains(spell.getId()); + } + } + } + return false; + } + + @Override + public MythUnboundCostReductionEffect copy() { + return new MythUnboundCostReductionEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NaturesWill.java b/Mage.Sets/src/mage/cards/n/NaturesWill.java index f96bc4b0065..4c771bf5fee 100644 --- a/Mage.Sets/src/mage/cards/n/NaturesWill.java +++ b/Mage.Sets/src/mage/cards/n/NaturesWill.java @@ -1,20 +1,18 @@ package mage.cards.n; -import java.util.HashSet; -import java.util.List; -import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ControlledCreaturesDealCombatDamagePlayerTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.TapAllTargetPlayerControlsEffect; +import mage.abilities.effects.common.UntapAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.constants.Zone; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterLandPermanent; /** * @@ -26,7 +24,11 @@ public final class NaturesWill extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}"); // Whenever one or more creatures you control deal combat damage to a player, tap all lands that player controls and untap all lands you control. - this.addAbility(new ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(new NaturesWillEffect())); + Effect tapAllEffect = new TapAllTargetPlayerControlsEffect(new FilterLandPermanent()); + tapAllEffect.setText("tap all lands that player controls"); + Ability ability = new ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Zone.BATTLEFIELD, tapAllEffect, true); + ability.addEffect(new UntapAllEffect(new FilterControlledLandPermanent())); + addAbility(ability); } public NaturesWill(final NaturesWill card) { @@ -38,37 +40,3 @@ public final class NaturesWill extends CardImpl { return new NaturesWill(this); } } - -class NaturesWillEffect extends OneShotEffect { - - public NaturesWillEffect() { - super(Outcome.Benefit); - this.staticText = "tap all lands that player controls and untap all lands you control"; - } - - public NaturesWillEffect(final NaturesWillEffect effect) { - super(effect); - } - - @Override - public NaturesWillEffect copy() { - return new NaturesWillEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Set damagedPlayers = (HashSet) this.getValue("damagedPlayers"); - if (damagedPlayers != null) { - List lands = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_LAND, source.getControllerId(), source.getSourceId(), game); - for (Permanent land : lands) { - if (damagedPlayers.contains(land.getControllerId())) { - land.tap(game); - } else if (land.isControlledBy(source.getControllerId())) { - land.untap(game); - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/n/NestingDragon.java b/Mage.Sets/src/mage/cards/n/NestingDragon.java new file mode 100644 index 00000000000..9f50c82d243 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NestingDragon.java @@ -0,0 +1,44 @@ +package mage.cards.n; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LandfallAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.NestingDragonToken; + +/** + * + * @author TheElk801 + */ +public final class NestingDragon extends CardImpl { + + public NestingDragon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Landfall — Whenever a land enters the battlefield under your control, create a 0/2 red Dragon Egg creature token with defender and "When this creature dies, create a 2/2 red Dragon creature token with flying and '{R}: This creature gets +1/+0 until end of turn.'" + this.addAbility(new LandfallAbility( + new CreateTokenEffect(new NestingDragonToken()), false + )); + } + + public NestingDragon(final NestingDragon card) { + super(card); + } + + @Override + public NestingDragon copy() { + return new NestingDragon(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NightIncarnate.java b/Mage.Sets/src/mage/cards/n/NightIncarnate.java new file mode 100644 index 00000000000..e4cac134219 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NightIncarnate.java @@ -0,0 +1,49 @@ +package mage.cards.n; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.constants.SubType; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.EvokeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; + +/** + * + * @author TheElk801 + */ +public final class NightIncarnate extends CardImpl { + + public NightIncarnate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // When Night Incarnate leaves the battlefield, all creatures get -3/-3 until end of turn. + this.addAbility(new LeavesBattlefieldTriggeredAbility( + new BoostAllEffect(-3, -3, Duration.EndOfTurn), false + )); + + // Evoke {3}{B} + this.addAbility(new EvokeAbility(this, "{3}{B}")); + + } + + public NightIncarnate(final NightIncarnate card) { + super(card); + } + + @Override + public NightIncarnate copy() { + return new NightIncarnate(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NyleasColossus.java b/Mage.Sets/src/mage/cards/n/NyleasColossus.java new file mode 100644 index 00000000000..4f0f7caf7ec --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NyleasColossus.java @@ -0,0 +1,75 @@ +package mage.cards.n; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.abilityword.ConstellationAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author TheElk801 + */ +public final class NyleasColossus extends CardImpl { + + public NyleasColossus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{6}{G}"); + + this.subtype.add(SubType.GIANT); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Constellation — Whenever Nylea's Colossus or another enchantment enters the battlefield under your control, double target creature's power and toughness until end of turn. + Ability ability = new ConstellationAbility(new NyleasColossusEffect(), false); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public NyleasColossus(final NyleasColossus card) { + super(card); + } + + @Override + public NyleasColossus copy() { + return new NyleasColossus(this); + } +} + +class NyleasColossusEffect extends OneShotEffect { + + public NyleasColossusEffect() { + super(Outcome.BoostCreature); + this.staticText = "double target creature's power and toughness until end of turn"; + } + + public NyleasColossusEffect(final NyleasColossusEffect effect) { + super(effect); + } + + @Override + public NyleasColossusEffect copy() { + return new NyleasColossusEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + int power = permanent.getPower().getValue(); + int toughness = permanent.getToughness().getValue(); + game.addEffect(new BoostTargetEffect(power, toughness, Duration.EndOfTurn), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OctopusUmbra.java b/Mage.Sets/src/mage/cards/o/OctopusUmbra.java new file mode 100644 index 00000000000..148586f6fc1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OctopusUmbra.java @@ -0,0 +1,76 @@ +package mage.cards.o; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessEnchantedEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.TotemArmorAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; + +/** + * + * @author TheElk801 + */ +public final class OctopusUmbra extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with power 8 or less"); + + static { + filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 9)); + } + + public OctopusUmbra(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has base power and toughness 8/8 and has "Whenever this creature attacks, you may tap target creature with power 8 or less." + Ability abilityToAdd = new AttacksTriggeredAbility(new TapTargetEffect(), true); + abilityToAdd.addTarget(new TargetCreaturePermanent(filter)); + ability = new SimpleStaticAbility( + Zone.BATTLEFIELD, + new SetPowerToughnessEnchantedEffect(8, 8) + ); + ability.addEffect(new GainAbilityAttachedEffect( + abilityToAdd, AttachmentType.AURA + ).setText("and has \"Whenever this creature attacks, " + + "you may tap target creature with power 8 or less.\"")); + this.addAbility(ability); + + // Totem armor + this.addAbility(new TotemArmorAbility()); + + } + + public OctopusUmbra(final OctopusUmbra card) { + super(card); + } + + @Override + public OctopusUmbra copy() { + return new OctopusUmbra(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PredatorsHowl.java b/Mage.Sets/src/mage/cards/p/PredatorsHowl.java index 38f2d633786..b0ebabf2149 100644 --- a/Mage.Sets/src/mage/cards/p/PredatorsHowl.java +++ b/Mage.Sets/src/mage/cards/p/PredatorsHowl.java @@ -21,7 +21,7 @@ public final class PredatorsHowl extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{G}"); // Create a 2/2 green Wolf creature token. - // Morbid - Create three 2/2 green Wolf creature tokens instead if a creature died this turn. + // Morbid — Create three 2/2 green Wolf creature tokens instead if a creature died this turn. Effect effect = new ConditionalOneShotEffect( new CreateTokenEffect(new WolfToken(), 3), new CreateTokenEffect(new WolfToken(), 1), diff --git a/Mage.Sets/src/mage/cards/p/PrimordialMist.java b/Mage.Sets/src/mage/cards/p/PrimordialMist.java new file mode 100644 index 00000000000..ee917e9486e --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PrimordialMist.java @@ -0,0 +1,146 @@ +package mage.cards.p; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.keyword.ManifestEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AsThoughEffectType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.other.FaceDownPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author spjspj + */ +public final class PrimordialMist extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("face down permanent"); + + static { + filter.add(new FaceDownPredicate()); + } + + public PrimordialMist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}"); + + // At the beginning of your end step, you may manifest the top card of your library. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new ManifestEffect(1), TargetController.YOU, true)); + + // Exile a face-down permanent you control face-up: You may play that card this turn + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new PrimordialMistCastFromExileEffect(), + new PrimordialMistCost(target)); + this.addAbility(ability); + } + + public PrimordialMist(final PrimordialMist card) { + super(card); + } + + @Override + public PrimordialMist copy() { + return new PrimordialMist(this); + } +} + +class PrimordialMistCost extends CostImpl { + + TargetPermanent target; + + public PrimordialMistCost(TargetPermanent target) { + this.target = target; + this.text = "Exile a face-down permanent you control face-up"; + } + + public PrimordialMistCost(final PrimordialMistCost cost) { + super(cost); + this.target = cost.target.copy(); + } + + @Override + public PrimordialMistCost copy() { + return new PrimordialMistCost(this); + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + return target.canChoose(controllerId, game); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Player controller = game.getPlayer(controllerId); + if (controller != null) { + if (target.choose(Outcome.Exile, controllerId, sourceId, game)) { + Card card = game.getCard(sourceId); + if (card != null) { + Permanent sourcePermanent = game.getPermanent(sourceId); + if (sourcePermanent != null) { + Permanent targetPermanent = game.getPermanent(target.getFirstTarget()); + Card targetCard = game.getCard(target.getFirstTarget()); + if (targetPermanent != null && targetCard != null) { + String exileName = sourcePermanent.getIdName() + " "; + controller.moveCardsToExile(targetPermanent, ability, game, true, sourceId, exileName); + targetPermanent.setFaceDown(false, game); + ContinuousEffect effect = new PrimordialMistCastFromExileEffect(); + effect.setTargetPointer(new FixedTarget(targetCard.getId(), targetCard.getZoneChangeCounter(game))); + game.addEffect(effect, ability); + this.setPaid(); + } + } + } + this.setPaid(); + return true; + } + } + return false; + } +} + +class PrimordialMistCastFromExileEffect extends AsThoughEffectImpl { + + public PrimordialMistCastFromExileEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); + staticText = "Exile a face-down permanent you control face-up. You may play the card from exile"; + } + + public PrimordialMistCastFromExileEffect(final PrimordialMistCastFromExileEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public PrimordialMistCastFromExileEffect copy() { + return new PrimordialMistCastFromExileEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + return source.isControlledBy(affectedControllerId) + && (game.getCard(getTargetPointer().getFirst(game, source)) != null); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PuresteelPaladin.java b/Mage.Sets/src/mage/cards/p/PuresteelPaladin.java index 395ea6b16e8..19252b5c053 100644 --- a/Mage.Sets/src/mage/cards/p/PuresteelPaladin.java +++ b/Mage.Sets/src/mage/cards/p/PuresteelPaladin.java @@ -39,11 +39,11 @@ public final class PuresteelPaladin extends CardImpl { // Whenever an Equipment enters the battlefield under your control, you may draw a card. this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), filter, true)); - // Metalcraft - Equipment you control have equip {0} as long as you control three or more artifacts + // Metalcraft — Equipment you control have equip {0} as long as you control three or more artifacts this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new GainAbilityControlledEffect(new EquipAbility(Outcome.AddAbility, new GenericManaCost(0)), Duration.WhileOnBattlefield, filter), MetalcraftCondition.instance, - "Metalcraft - Equipment you control have equip {0} as long as you control three or more artifacts"))); + "Metalcraft — Equipment you control have equip {0} as long as you control three or more artifacts"))); } public PuresteelPaladin(final PuresteelPaladin card) { diff --git a/Mage.Sets/src/mage/cards/r/RavenousSlime.java b/Mage.Sets/src/mage/cards/r/RavenousSlime.java new file mode 100644 index 00000000000..f68af5571d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RavenousSlime.java @@ -0,0 +1,141 @@ +package mage.cards.r; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleEvasionAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author TheElk801 + */ +public final class RavenousSlime extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creatures with power 2 or less"); + + static { + filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 3)); + } + + public RavenousSlime(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.OOZE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Ravenous Slime can't be blocked by creatures with power 2 or less. + this.addAbility(new SimpleEvasionAbility( + new CantBeBlockedByCreaturesSourceEffect( + filter, Duration.WhileOnBattlefield + ) + )); + + // If a creature an opponent controls would die, instead exile it and put a number of +1/+1 counters equal to that creature's power on Ravenous Slime. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new RavenousSlimeEffect() + )); + } + + public RavenousSlime(final RavenousSlime card) { + super(card); + } + + @Override + public RavenousSlime copy() { + return new RavenousSlime(this); + } +} + +class RavenousSlimeEffect extends ReplacementEffectImpl { + + public RavenousSlimeEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If a creature an opponent controls would die, " + + "instead exile it and put a number of +1/+1 counters " + + "equal to that creature's power on {this}"; + } + + public RavenousSlimeEffect(final RavenousSlimeEffect effect) { + super(effect); + } + + @Override + public RavenousSlimeEffect copy() { + return new RavenousSlimeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent sourceCreature = game.getPermanent(source.getSourceId()); + if (controller == null || sourceCreature == null) { + return false; + } + if (((ZoneChangeEvent) event).getFromZone() != Zone.BATTLEFIELD) { + return false; + } + Permanent permanent = ((ZoneChangeEvent) event).getTarget(); + if (permanent == null) { + return false; + } + int power = permanent.getPower().getValue(); + controller.moveCards(permanent, Zone.EXILED, source, game); + return new AddCountersSourceEffect( + CounterType.P1P1.createInstance(power) + ).apply(game, source); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getToZone() != Zone.GRAVEYARD) { + return false; + } + Permanent permanent = ((ZoneChangeEvent) event).getTarget(); + if (permanent == null + || !game.getOpponents(source.getControllerId()).contains(permanent.getControllerId())) { + return false; + } + if (zEvent.getTarget() != null) { // if it comes from permanent, check if it was a creature on the battlefield + if (zEvent.getTarget().isCreature()) { + return true; + } + } else if (permanent.isCreature()) { + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RazorfieldRhino.java b/Mage.Sets/src/mage/cards/r/RazorfieldRhino.java index bf23b4fca7a..28b09384e2a 100644 --- a/Mage.Sets/src/mage/cards/r/RazorfieldRhino.java +++ b/Mage.Sets/src/mage/cards/r/RazorfieldRhino.java @@ -28,7 +28,7 @@ public final class RazorfieldRhino extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); ContinuousEffect effect1 = new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect1, MetalcraftCondition.instance, "Metalcraft - Razorfield Rhino gets +2/+2 as long as you control three or more artifacts"))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect1, MetalcraftCondition.instance, "Metalcraft — Razorfield Rhino gets +2/+2 as long as you control three or more artifacts"))); } public RazorfieldRhino (final RazorfieldRhino card) { diff --git a/Mage.Sets/src/mage/cards/r/RealityScramble.java b/Mage.Sets/src/mage/cards/r/RealityScramble.java new file mode 100644 index 00000000000..f3f8096971e --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RealityScramble.java @@ -0,0 +1,111 @@ +package mage.cards.r; + +import java.util.EnumSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.RetraceAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.other.OwnerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +/** + * + * @author TheElk801 + */ +public final class RealityScramble extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent("permanent you own"); + + static { + filter.add(new OwnerPredicate(TargetController.YOU)); + } + + public RealityScramble(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}"); + + // Put target permanent you own on the bottom of your library. Reveal cards from the top of your library until you reveal a card that shares a card type with that permanent. Put that card onto the battlefield and the rest on the bottom of your library in a random order. + this.getSpellAbility().addEffect(new RealityScrambleEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + // Retrace + this.addAbility(new RetraceAbility(this)); + } + + public RealityScramble(final RealityScramble card) { + super(card); + } + + @Override + public RealityScramble copy() { + return new RealityScramble(this); + } +} + +class RealityScrambleEffect extends OneShotEffect { + + public RealityScrambleEffect() { + super(Outcome.Benefit); + this.staticText = "Put target permanent you own " + + "on the bottom of your library. Reveal cards from " + + "the top of your library until you reveal a card " + + "that shares a card type with that permanent. " + + "Put that card onto the battlefield and the rest " + + "on the bottom of your library in a random order."; + } + + public RealityScrambleEffect(final RealityScrambleEffect effect) { + super(effect); + } + + @Override + public RealityScrambleEffect copy() { + return new RealityScrambleEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + Player controller = game.getPlayer(source.getControllerId()); + if (permanent == null || controller == null) { + return false; + } + Set types = EnumSet.noneOf(CardType.class); + types.addAll(permanent.getCardType()); + controller.putCardsOnBottomOfLibrary( + new CardsImpl(permanent), game, source, false + ); + Cards toReveal = new CardsImpl(); + Card cardToPlay = null; + for (Card card : controller.getLibrary().getCards(game)) { + toReveal.add(card); + for (CardType type : types) { + if (card.getCardType().contains(type)) { + cardToPlay = card; + break; + } + } + } + controller.revealCards(source, toReveal, game); + if (cardToPlay != null) { + controller.moveCards(cardToPlay, Zone.BATTLEFIELD, source, game); + toReveal.remove(cardToPlay); + } + controller.putCardsOnBottomOfLibrary(toReveal, game, source, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReaperFromTheAbyss.java b/Mage.Sets/src/mage/cards/r/ReaperFromTheAbyss.java index c9f2aa56786..c9a9bf3f10d 100644 --- a/Mage.Sets/src/mage/cards/r/ReaperFromTheAbyss.java +++ b/Mage.Sets/src/mage/cards/r/ReaperFromTheAbyss.java @@ -87,6 +87,6 @@ class ReaperFromTheAbyssAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Morbid - At the beginning of each end step, if a creature died this turn, destroy target non-demon creature."; + return "Morbid — At the beginning of each end step, if a creature died this turn, destroy target non-demon creature."; } } diff --git a/Mage.Sets/src/mage/cards/r/RustedRelic.java b/Mage.Sets/src/mage/cards/r/RustedRelic.java index b1303c1436e..95014e11eb4 100644 --- a/Mage.Sets/src/mage/cards/r/RustedRelic.java +++ b/Mage.Sets/src/mage/cards/r/RustedRelic.java @@ -29,7 +29,7 @@ public final class RustedRelic extends CardImpl { new ConditionalContinuousEffect( new BecomesCreatureSourceEffect(new RustedRelicToken(), "artifact", Duration.WhileOnBattlefield), MetalcraftCondition.instance, - "Metalcraft - {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts"))); + "Metalcraft — {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts"))); } public RustedRelic (final RustedRelic card) { diff --git a/Mage.Sets/src/mage/cards/s/SaheeliTheGifted.java b/Mage.Sets/src/mage/cards/s/SaheeliTheGifted.java new file mode 100644 index 00000000000..4e31f691b7f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SaheeliTheGifted.java @@ -0,0 +1,162 @@ +package mage.cards.s; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.SpellAbility; +import mage.abilities.common.CanBeYourCommanderAbility; +import mage.abilities.common.PlanswalkerEntersWithLoyalityCountersAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.ServoToken; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; +import mage.watchers.common.CastSpellLastTurnWatcher; + +/** + * + * @author TheElk801 + */ +public final class SaheeliTheGifted extends CardImpl { + + public SaheeliTheGifted(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SAHEELI); + this.addAbility(new PlanswalkerEntersWithLoyalityCountersAbility(4)); + + // +1: Create a 1/1 colorless Servo artifact creature token. + this.addAbility(new LoyaltyAbility( + new CreateTokenEffect(new ServoToken()), 1 + )); + + // +1: The next spell you cast this turn costs {1} less to cast for each artifact you control as you cast it. + this.addAbility(new LoyaltyAbility( + new SaheeliTheGiftedCostReductionEffect(), 1 + )); + + // -7: For each artifact you control, create a token that's a copy of it. Those tokens gain haste. Exile those tokens at the beginning of the next end step. + this.addAbility(new LoyaltyAbility( + new SaheeliTheGiftedTokenEffect(), -7 + )); + + // Saheeli, the Gifted can be your commander. + this.addAbility(CanBeYourCommanderAbility.getInstance()); + } + + public SaheeliTheGifted(final SaheeliTheGifted card) { + super(card); + } + + @Override + public SaheeliTheGifted copy() { + return new SaheeliTheGifted(this); + } +} + +class SaheeliTheGiftedCostReductionEffect extends CostModificationEffectImpl { + + private int spellsCast; + + public SaheeliTheGiftedCostReductionEffect() { + super(Duration.EndOfTurn, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "the next spell you cast this turn costs {1} less to cast " + + "for each artifact you control as you cast it"; + } + + protected SaheeliTheGiftedCostReductionEffect(final SaheeliTheGiftedCostReductionEffect effect) { + super(effect); + this.spellsCast = effect.spellsCast; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getSimpleName()); + if (watcher != null) { + spellsCast = watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()); + } + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + int artifactCount = game.getBattlefield().getAllActivePermanents( + StaticFilters.FILTER_PERMANENT_ARTIFACT_AN, + source.getControllerId(), game + ).size(); + CardUtil.reduceCost(abilityToModify, artifactCount); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getSimpleName()); + if (watcher != null) { + if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()) > spellsCast) { + discard(); // only one use + return false; + } + } + if (abilityToModify instanceof SpellAbility) { + return abilityToModify.isControlledBy(source.getControllerId()); + } + return false; + } + + @Override + public SaheeliTheGiftedCostReductionEffect copy() { + return new SaheeliTheGiftedCostReductionEffect(this); + } +} + +class SaheeliTheGiftedTokenEffect extends OneShotEffect { + + public SaheeliTheGiftedTokenEffect() { + super(Outcome.Benefit); + this.staticText = "for each artifact you control, " + + "create a token that's a copy of it. " + + "Those tokens gain haste. " + + "Exile those tokens at the beginning of the next end step."; + } + + public SaheeliTheGiftedTokenEffect(final SaheeliTheGiftedTokenEffect effect) { + super(effect); + } + + @Override + public SaheeliTheGiftedTokenEffect copy() { + return new SaheeliTheGiftedTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents( + StaticFilters.FILTER_PERMANENT_ARTIFACT_AN, + source.getControllerId(), game + )) { + if (permanent != null) { + CreateTokenCopyTargetEffect effect + = new CreateTokenCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.setHasHaste(true); + effect.apply(game, source); + effect.exileTokensCreatedAtNextEndStep(game, source); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java b/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java index 544612e3fd6..b356cdac110 100644 --- a/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java +++ b/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java @@ -20,7 +20,7 @@ import mage.constants.SubType; */ public final class ScreechingSilcaw extends CardImpl { - private static final String text = "Metalcraft - Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard."; + private static final String text = "Metalcraft — Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard."; public ScreechingSilcaw(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); @@ -31,7 +31,7 @@ public final class ScreechingSilcaw extends CardImpl { this.addAbility(FlyingAbility.getInstance()); - //"Metalcraft - Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard. + //"Metalcraft — Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard. TriggeredAbility conditional = new ConditionalInterveningIfTriggeredAbility( new DealsCombatDamageToAPlayerTriggeredAbility(new PutLibraryIntoGraveTargetEffect(4), false, true), MetalcraftCondition.instance, text); diff --git a/Mage.Sets/src/mage/cards/s/SkirsdagHighPriest.java b/Mage.Sets/src/mage/cards/s/SkirsdagHighPriest.java index ffb876f5998..15b2be1cafd 100644 --- a/Mage.Sets/src/mage/cards/s/SkirsdagHighPriest.java +++ b/Mage.Sets/src/mage/cards/s/SkirsdagHighPriest.java @@ -41,7 +41,7 @@ public final class SkirsdagHighPriest extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - // Morbid - {tap}, Tap two untapped creatures you control: Create a 5/5 black Demon creature token with flying. Activate this ability only if a creature died this turn. + // Morbid — {tap}, Tap two untapped creatures you control: Create a 5/5 black Demon creature token with flying. Activate this ability only if a creature died this turn. Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new DemonToken()), new TapSourceCost(), MorbidCondition.instance); ability.addCost(new TapTargetCost(new TargetControlledCreaturePermanent(2, 2, filter, false))); diff --git a/Mage.Sets/src/mage/cards/s/SkullStorm.java b/Mage.Sets/src/mage/cards/s/SkullStorm.java new file mode 100644 index 00000000000..164cf6e3033 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkullStorm.java @@ -0,0 +1,88 @@ +package mage.cards.s; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeEffect; +import mage.abilities.keyword.CommanderStormAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author TheElk801 + */ +public final class SkullStorm extends CardImpl { + + public SkullStorm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{7}{B}{B}"); + + // When you cast this spell, copy it for each time you've cast your commander from the command zone this game. + this.addAbility(new CommanderStormAbility()); + + // Each opponent sacrifices a creature. Each opponent who can't loses half their life, rounded up. + this.getSpellAbility().addEffect(new SkullStormEffect()); + } + + public SkullStorm(final SkullStorm card) { + super(card); + } + + @Override + public SkullStorm copy() { + return new SkullStorm(this); + } +} + +class SkullStormEffect extends OneShotEffect { + + public SkullStormEffect() { + super(Outcome.Benefit); + this.staticText = "Each opponent sacrifices a creature. " + + "Each opponent who can't loses half their life, rounded up."; + } + + public SkullStormEffect(final SkullStormEffect effect) { + super(effect); + } + + @Override + public SkullStormEffect copy() { + return new SkullStormEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.getOpponents(source.getControllerId()).forEach((playerId) -> { + Player player = game.getPlayer(playerId); + if (!(player == null)) { + FilterPermanent filter = new FilterCreaturePermanent(); + filter.add(new ControllerIdPredicate(playerId)); + if (game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), game + ).isEmpty()) { + int lifeToLose = (int) Math.ceil(player.getLife() / 2); + player.loseLife(lifeToLose, game, false); + } else { + Effect effect = new SacrificeEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, 1, null + ); + effect.setTargetPointer(new FixedTarget(playerId, game)); + effect.apply(game, source); + } + } + }); + return true; + } +} +//doot doot diff --git a/Mage.Sets/src/mage/cards/s/SnapsailGlider.java b/Mage.Sets/src/mage/cards/s/SnapsailGlider.java index 0650452dde5..7cc55dd5da2 100644 --- a/Mage.Sets/src/mage/cards/s/SnapsailGlider.java +++ b/Mage.Sets/src/mage/cards/s/SnapsailGlider.java @@ -23,7 +23,7 @@ import mage.constants.Zone; */ public final class SnapsailGlider extends CardImpl { - protected static String rule = "Metalcraft - Snapsail Glider has flying as long as you control three or more artifacts"; + protected static String rule = "Metalcraft — Snapsail Glider has flying as long as you control three or more artifacts"; public SnapsailGlider (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); diff --git a/Mage.Sets/src/mage/cards/s/SomberwaldSpider.java b/Mage.Sets/src/mage/cards/s/SomberwaldSpider.java index 4fb9b1ec7dd..177a0ec798f 100644 --- a/Mage.Sets/src/mage/cards/s/SomberwaldSpider.java +++ b/Mage.Sets/src/mage/cards/s/SomberwaldSpider.java @@ -28,7 +28,7 @@ public final class SomberwaldSpider extends CardImpl { this.toughness = new MageInt(4); this.addAbility(ReachAbility.getInstance()); - // Morbid - Somberwald Spider enters the battlefield with two +1/+1 counters on it if a creature died this turn. + // Morbid — Somberwald Spider enters the battlefield with two +1/+1 counters on it if a creature died this turn. this.addAbility(new EntersBattlefieldAbility( new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), MorbidCondition.instance, ""), "with two +1/+1 counters on it if a creature died this turn")); diff --git a/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java b/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java new file mode 100644 index 00000000000..18d91f47571 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java @@ -0,0 +1,162 @@ +package mage.cards.s; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author TheElk801 + */ +public final class SowerOfDiscord extends CardImpl { + + public SowerOfDiscord(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); + + this.subtype.add(SubType.DEMON); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // As Sower of Discord enters the battlefield, choose two players. + this.addAbility(new AsEntersBattlefieldAbility( + new SowerOfDiscordEntersBattlefieldEffect() + )); + + // Whenever damage is dealt to one of the chosen players, the other chosen player also loses that much life. + this.addAbility(new SowerOfDiscordTriggeredAbility()); + } + + public SowerOfDiscord(final SowerOfDiscord card) { + super(card); + } + + @Override + public SowerOfDiscord copy() { + return new SowerOfDiscord(this); + } +} + +class SowerOfDiscordEntersBattlefieldEffect extends OneShotEffect { + + public SowerOfDiscordEntersBattlefieldEffect() { + super(Outcome.Damage); + staticText = "choose two players"; + } + + public SowerOfDiscordEntersBattlefieldEffect(final SowerOfDiscordEntersBattlefieldEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + if (controller == null || permanent == null) { + return false; + } + TargetPlayer target = new TargetPlayer(2, 2, true); + controller.chooseTarget(outcome, target, source, game); + Player player1 = game.getPlayer(target.getFirstTarget()); + if (target.getTargets().size() <= 1) { + return false; + } + Player player2 = game.getPlayer(target.getTargets().get(1)); + if (player1 == null || player2 == null) { + return false; + } + game.getState().setValue(source.getSourceId() + "_player1", player1); + game.getState().setValue(source.getSourceId() + "_player2", player2); + game.informPlayers(permanent.getLogName() + ": " + + controller.getLogName() + " has chosen " + + player1.getLogName() + " and " + player2.getLogName() + ); + permanent.addInfo( + "chosen players", + "Chosen players: " + + player1.getName() + ", " + + player2.getName() + "", game + ); + return true; + } + + @Override + public SowerOfDiscordEntersBattlefieldEffect copy() { + return new SowerOfDiscordEntersBattlefieldEffect(this); + } + +} + +class SowerOfDiscordTriggeredAbility extends TriggeredAbilityImpl { + + public SowerOfDiscordTriggeredAbility() { + super(Zone.BATTLEFIELD, null); + } + + public SowerOfDiscordTriggeredAbility(final SowerOfDiscordTriggeredAbility ability) { + super(ability); + } + + @Override + public SowerOfDiscordTriggeredAbility copy() { + return new SowerOfDiscordTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + int damage = event.getAmount(); + Player player1 = (Player) game.getState().getValue( + this.getSourceId() + "_player1" + ); + Player player2 = (Player) game.getState().getValue( + this.getSourceId() + "_player2" + ); + if (player1 == null || player2 == null || damage == 0) { + return false; + } + Effect effect = new LoseLifeTargetEffect(damage); + if (event.getTargetId().equals(player1.getId())) { + this.getEffects().clear(); + effect.setTargetPointer(new FixedTarget(player2.getId())); + this.addEffect(effect); + return true; + } else if (event.getTargetId().equals(player2.getId())) { + this.getEffects().clear(); + effect.setTargetPointer(new FixedTarget(player1.getId())); + this.addEffect(effect); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever damage is dealt to one of the chosen players, " + + "the other chosen player also loses that much life."; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiralingDuelist.java b/Mage.Sets/src/mage/cards/s/SpiralingDuelist.java index fe37f728610..52af0e6edc4 100644 --- a/Mage.Sets/src/mage/cards/s/SpiralingDuelist.java +++ b/Mage.Sets/src/mage/cards/s/SpiralingDuelist.java @@ -22,7 +22,7 @@ import mage.constants.Zone; */ public final class SpiralingDuelist extends CardImpl { - private static final String effectText = "Metalcraft - Spiraling Duelist has double strike as long as you control three or more artifacts."; + private static final String effectText = "Metalcraft — Spiraling Duelist has double strike as long as you control three or more artifacts."; public SpiralingDuelist(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); diff --git a/Mage.Sets/src/mage/cards/s/SpireSerpent.java b/Mage.Sets/src/mage/cards/s/SpireSerpent.java index 0023dc7d2e2..a8338bd6519 100644 --- a/Mage.Sets/src/mage/cards/s/SpireSerpent.java +++ b/Mage.Sets/src/mage/cards/s/SpireSerpent.java @@ -25,7 +25,7 @@ import mage.constants.Zone; */ public final class SpireSerpent extends CardImpl { - private static final String abilityText1 = "Metalcraft - As long as you control three or more artifacts, {this} gets +2/+2"; + private static final String abilityText1 = "Metalcraft — As long as you control three or more artifacts, {this} gets +2/+2"; public SpireSerpent(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}"); diff --git a/Mage.Sets/src/mage/cards/s/StoicRebuttal.java b/Mage.Sets/src/mage/cards/s/StoicRebuttal.java index 7722c315e80..a9340141640 100644 --- a/Mage.Sets/src/mage/cards/s/StoicRebuttal.java +++ b/Mage.Sets/src/mage/cards/s/StoicRebuttal.java @@ -23,7 +23,7 @@ public final class StoicRebuttal extends CardImpl { public StoicRebuttal(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{U}"); - // Metalcraft - Stoic Rebuttal costs {1} less to cast if you control three or more artifacts. + // Metalcraft — Stoic Rebuttal costs {1} less to cast if you control three or more artifacts. Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, MetalcraftCondition.instance)); ability.setRuleAtTheTop(true); ability.setAbilityWord(AbilityWord.METALCRAFT); diff --git a/Mage.Sets/src/mage/cards/s/StoneIdolTrap.java b/Mage.Sets/src/mage/cards/s/StoneIdolTrap.java index 3d4421c55d0..ec704870134 100644 --- a/Mage.Sets/src/mage/cards/s/StoneIdolTrap.java +++ b/Mage.Sets/src/mage/cards/s/StoneIdolTrap.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.abilities.Ability; @@ -57,7 +56,7 @@ class StoneIdolTrapCostReductionEffect extends CostModificationEffectImpl { public StoneIdolTrapCostReductionEffect() { super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "{this} costs {1} less to cast for each attacking creature"; + staticText = "This spell costs {1} less to cast for each attacking creature"; } protected StoneIdolTrapCostReductionEffect(StoneIdolTrapCostReductionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/StormTheVault.java b/Mage.Sets/src/mage/cards/s/StormTheVault.java index 41e3e483241..968f3b72d52 100644 --- a/Mage.Sets/src/mage/cards/s/StormTheVault.java +++ b/Mage.Sets/src/mage/cards/s/StormTheVault.java @@ -2,10 +2,14 @@ package mage.cards.s; import java.util.UUID; + +import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.ControlledCreaturesDealCombatDamagePlayerTriggeredAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; @@ -17,6 +21,7 @@ import mage.constants.ComparisonType; import mage.constants.SuperType; import mage.constants.TargetController; import mage.filter.StaticFilters; +import mage.game.Game; import mage.game.permanent.token.TreasureToken; /** @@ -45,6 +50,8 @@ public final class StormTheVault extends CardImpl { } + + public StormTheVault(final StormTheVault card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/t/ThantisTheWarweaver.java b/Mage.Sets/src/mage/cards/t/ThantisTheWarweaver.java new file mode 100644 index 00000000000..75fc2ef69b9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThantisTheWarweaver.java @@ -0,0 +1,65 @@ +package mage.cards.t; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.AttacksAllTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.combat.AttacksIfAbleAllEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +/** + * + * @author TheElk801 + */ +public final class ThantisTheWarweaver extends CardImpl { + + public ThantisTheWarweaver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // All creatures attack each combat if able. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new AttacksIfAbleAllEffect( + StaticFilters.FILTER_PERMANENT_CREATURES, + Duration.WhileOnBattlefield, true + ).setText("All creatures attack each combat if able") + )); + + // Whenever a creature attacks you or a planeswalker you control, put a +1/+1 counter on Thantis the Warweaver. + this.addAbility(new AttacksAllTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + false, true + )); + } + + public ThantisTheWarweaver(final ThantisTheWarweaver card) { + super(card); + } + + @Override + public ThantisTheWarweaver copy() { + return new ThantisTheWarweaver(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TragicSlip.java b/Mage.Sets/src/mage/cards/t/TragicSlip.java index 6b5b595f2c2..af22f6aa801 100644 --- a/Mage.Sets/src/mage/cards/t/TragicSlip.java +++ b/Mage.Sets/src/mage/cards/t/TragicSlip.java @@ -23,12 +23,12 @@ public final class TragicSlip extends CardImpl { // Target creature gets -1/-1 until end of turn. - // Morbid - That creature gets -13/-13 until end of turn instead if a creature died this turn. + // Morbid — That creature gets -13/-13 until end of turn instead if a creature died this turn. this.getSpellAbility().addEffect(new ConditionalContinuousEffect( new BoostTargetEffect(-13, -13, Duration.EndOfTurn), new BoostTargetEffect(-1, -1, Duration.EndOfTurn), new LockedInCondition(MorbidCondition.instance), - "Target creature gets -1/-1 until end of turn. Morbid - That creature gets -13/-13 until end of turn instead if a creature died this turn")); + "Target creature gets -1/-1 until end of turn. Morbid — That creature gets -13/-13 until end of turn instead if a creature died this turn")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/t/TravelersCloak.java b/Mage.Sets/src/mage/cards/t/TravelersCloak.java index 02f4ef13dcc..9145af115df 100644 --- a/Mage.Sets/src/mage/cards/t/TravelersCloak.java +++ b/Mage.Sets/src/mage/cards/t/TravelersCloak.java @@ -50,7 +50,7 @@ public final class TravelersCloak extends CardImpl { // Enchanted creature has landwalk of the chosen type. FilterLandPermanent filter = new FilterLandPermanent("Landwalk of the chosen type"); - filter.add(new ChosenSubtypePredicate(this.getId())); + filter.add(new ChosenSubtypePredicate()); Ability landwalkAbility = new LandwalkAbility(filter); Effect effect = new GainAbilityAttachedEffect(landwalkAbility, AttachmentType.AURA); effect.setText("Enchanted creature has landwalk of the chosen type"); diff --git a/Mage.Sets/src/mage/cards/t/TreasureNabber.java b/Mage.Sets/src/mage/cards/t/TreasureNabber.java new file mode 100644 index 00000000000..9a62b20545d --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TreasureNabber.java @@ -0,0 +1,146 @@ +package mage.cards.t; + +import java.util.List; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.PhaseStep; +import mage.constants.SubLayer; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.FixedTargets; + +/** + * + * @author spjspj + */ +public final class TreasureNabber extends CardImpl { + + public TreasureNabber(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever an opponent taps an artifact for mana, gain control of that artifact until the end of your next turn. + this.addAbility(new TreasureNabberAbility()); + } + + public TreasureNabber(final TreasureNabber card) { + super(card); + } + + @Override + public TreasureNabber copy() { + return new TreasureNabber(this); + } +} + +class TreasureNabberAbility extends TriggeredAbilityImpl { + + public TreasureNabberAbility() { + super(Zone.BATTLEFIELD, new TreasureNabberEffect()); + } + + public TreasureNabberAbility(TreasureNabberAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.TAPPED_FOR_MANA; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (game.getOpponents(controllerId).contains(event.getPlayerId())) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null && permanent.isArtifact()) { + getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + return true; + } + } + return false; + } + + @Override + public TreasureNabberAbility copy() { + return new TreasureNabberAbility(this); + } + + @Override + public String getRule() { + return "Whenever an opponent taps an artifact for mana, gain control of that artifact until the end of your next turn"; + } +} + +class TreasureNabberEffect extends ContinuousEffectImpl { + + protected FixedTargets fixedTargets; + protected int startingTurn; + + TreasureNabberEffect() { + super(Duration.Custom, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); + this.staticText = "gain control of that artifact until the end of your next turn"; + startingTurn = 0; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + startingTurn = game.getTurnNum(); + if (game.getPhase().getStep().getType() == PhaseStep.END_TURN) { + startingTurn = game.getTurnNum() + 1; + } + } + + TreasureNabberEffect(final TreasureNabberEffect effect) { + super(effect); + this.fixedTargets = effect.fixedTargets; + } + + @Override + public TreasureNabberEffect copy() { + return new TreasureNabberEffect(this); + } + + @Override + public boolean isInactive(Ability source, Game game) { + if (startingTurn != 0 && game.getTurnNum() >= startingTurn && game.getPhase().getStep().getType() == PhaseStep.END_TURN) { + if (game.isActivePlayer(source.getControllerId())) { + return true; + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + + if (permanent != null) { + permanent.changeControllerId(source.getControllerId(), game); + return true; + } + return false; + } + + public void setTargets(List targetedPermanents, Game game) { + this.fixedTargets = new FixedTargets(targetedPermanents, game); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TurntimberSower.java b/Mage.Sets/src/mage/cards/t/TurntimberSower.java new file mode 100644 index 00000000000..2db016a36cd --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TurntimberSower.java @@ -0,0 +1,120 @@ +package mage.cards.t; + +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.Card; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeGroupEvent; +import mage.game.permanent.token.PlantToken; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author TheElk801 + */ +public final class TurntimberSower extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledCreaturePermanent("three creatures"); + + public TurntimberSower(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever one or more land cards are put into your graveyard from anywhere, create a 0/1 green Plant creature token. + this.addAbility(new TurntimberSowerTriggeredAbility()); + + // {G}, Sacrifice three creatures: Return target land card from your graveyard to your hand. + Ability ability = new SimpleActivatedAbility( + new ReturnToHandTargetEffect() + .setText("Return target land card " + + "from your graveyard to your hand"), + new ManaCostsImpl("{G}") + ); + ability.addCost(new SacrificeTargetCost( + new TargetControlledPermanent(3, 3, filter, true) + )); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_LAND)); + this.addAbility(ability); + } + + public TurntimberSower(final TurntimberSower card) { + super(card); + } + + @Override + public TurntimberSower copy() { + return new TurntimberSower(this); + } +} + +class TurntimberSowerTriggeredAbility extends TriggeredAbilityImpl { + + public TurntimberSowerTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new PlantToken()), false); + } + + public TurntimberSowerTriggeredAbility(final TurntimberSowerTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE_GROUP; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeGroupEvent zEvent = (ZoneChangeGroupEvent) event; + if (zEvent != null + && Zone.GRAVEYARD == zEvent.getToZone() + && zEvent.getCards() != null) { + for (Card card : zEvent.getCards()) { + if (card != null) { + UUID cardOwnerId = card.getOwnerId(); + Set cardType = card.getCardType(); + if (cardOwnerId != null + && card.isOwnedBy(getControllerId()) + && cardType != null + && card.isLand()) { + return true; + } + } + } + } + return false; + } + + @Override + public TurntimberSowerTriggeredAbility copy() { + return new TurntimberSowerTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever one or more land cards are put into your graveyard " + + "from anywhere, create a 0/1 green Plant creature token."; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TuvasaTheSunlit.java b/Mage.Sets/src/mage/cards/t/TuvasaTheSunlit.java new file mode 100644 index 00000000000..7cb517ce679 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TuvasaTheSunlit.java @@ -0,0 +1,153 @@ +package mage.cards.t; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TargetController; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; +import mage.filter.common.FilterEnchantmentPermanent; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.watchers.Watcher; + +/** + * + * @author TheElk801 + */ +public final class TuvasaTheSunlit extends CardImpl { + + private static final FilterEnchantmentPermanent filter + = new FilterEnchantmentPermanent("enchantment you control"); + + static { + filter.add(new ControllerPredicate(TargetController.YOU)); + } + + public TuvasaTheSunlit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.MERFOLK); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Tuvasa the Sunlit gets +1/+1 for each enchantment you control. + DynamicValue value + = new PermanentsOnBattlefieldCount(new FilterPermanent(filter)); + Ability ability = new SimpleStaticAbility( + Zone.BATTLEFIELD, + new BoostSourceEffect( + value, value, Duration.WhileOnBattlefield + ).setText("{this} gets +1/+1 for each enchantment you control") + ); + this.addAbility(ability); + + // Whenever you cast your first enchantment spell each turn, draw a card. + this.addAbility( + new TuvasaTheSunlitTriggeredAbility(), + new TuvasaTheSunlitWatcher() + ); + } + + public TuvasaTheSunlit(final TuvasaTheSunlit card) { + super(card); + } + + @Override + public TuvasaTheSunlit copy() { + return new TuvasaTheSunlit(this); + } +} + +class TuvasaTheSunlitTriggeredAbility extends SpellCastControllerTriggeredAbility { + + private static final FilterSpell filter + = new FilterSpell("an enchantment spell"); + + static { + filter.add(new CardTypePredicate(CardType.ENCHANTMENT)); + } + + public TuvasaTheSunlitTriggeredAbility() { + super(new DrawCardSourceControllerEffect(1), filter, false); + this.rule = "Whenever you cast your first enchantment spell each turn, draw a card."; + } + + public TuvasaTheSunlitTriggeredAbility(final TuvasaTheSunlitTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!super.checkTrigger(event, game)) { + return false; + } + TuvasaTheSunlitWatcher watcher = (TuvasaTheSunlitWatcher) game.getState().getWatchers().get(TuvasaTheSunlitWatcher.class.getSimpleName()); + MageObjectReference mor = watcher.getFirstEnchantmentThisTurn(this.getControllerId()); + return mor == null || mor.refersTo(event.getTargetId(), game); + } + +} + +class TuvasaTheSunlitWatcher extends Watcher { + + private final Map firstEnchantmentThisTurn = new HashMap(); + + public TuvasaTheSunlitWatcher() { + super(TuvasaTheSunlitWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public TuvasaTheSunlitWatcher(final TuvasaTheSunlitWatcher watcher) { + super(watcher); + this.firstEnchantmentThisTurn.putAll(watcher.firstEnchantmentThisTurn); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.SPELL_CAST) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && spell.isEnchantment()) { + firstEnchantmentThisTurn.putIfAbsent( + event.getPlayerId(), + new MageObjectReference(spell, game) + ); + } + } + } + + @Override + public void reset() { + firstEnchantmentThisTurn.clear(); + } + + public MageObjectReference getFirstEnchantmentThisTurn(UUID playerId) { + return firstEnchantmentThisTurn.get(playerId); + } + + @Override + public Watcher copy() { + return new TuvasaTheSunlitWatcher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UlvenwaldBear.java b/Mage.Sets/src/mage/cards/u/UlvenwaldBear.java index d7c335f2f99..d219c273b84 100644 --- a/Mage.Sets/src/mage/cards/u/UlvenwaldBear.java +++ b/Mage.Sets/src/mage/cards/u/UlvenwaldBear.java @@ -29,7 +29,7 @@ public final class UlvenwaldBear extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // Morbid - When Ulvenwald Bear enters the battlefield, if a creature died this turn, put two +1/+1 counters on target creature. + // Morbid — When Ulvenwald Bear enters the battlefield, if a creature died this turn, put two +1/+1 counters on target creature. Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2), Outcome.BoostCreature)), MorbidCondition.instance, "When {this} enters the battlefield, if a creature died this turn, put two +1/+1 counters on target creature."); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/v/VarchildBetrayerOfKjeldor.java b/Mage.Sets/src/mage/cards/v/VarchildBetrayerOfKjeldor.java new file mode 100644 index 00000000000..bbff89dbf7c --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VarchildBetrayerOfKjeldor.java @@ -0,0 +1,117 @@ +package mage.cards.v; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.effects.common.combat.CantAttackYouOrPlaneswalkerAllEffect; +import mage.abilities.effects.common.combat.CantBlockAllEffect; +import mage.abilities.effects.common.continuous.GainControlAllEffect; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.game.Game; +import mage.game.permanent.token.SurvivorToken; + +/** + * + * @author TheElk801 + */ +public final class VarchildBetrayerOfKjeldor extends CardImpl { + + private static final FilterCreaturePermanent filter1 + = new FilterCreaturePermanent( + SubType.SURVIVOR, + "Survivors your opponents control" + ); + private static final FilterCreaturePermanent filter2 + = new FilterCreaturePermanent( + SubType.SURVIVOR, "all Survivors" + ); + + static { + filter1.add(new ControllerPredicate(TargetController.OPPONENT)); + } + + public VarchildBetrayerOfKjeldor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever Varchild, Betrayer of Kjeldor deals combat damage to a player, that player creates that many 1/1 red Survivor creature tokens. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new VarchildBetrayerOfKjeldorEffect(), false, true + )); + + // Survivors your opponents control can't block, and they can't attack you or a planeswalker you control. + Ability ability = new SimpleStaticAbility( + Zone.BATTLEFIELD, + new CantBlockAllEffect( + filter1, Duration.WhileOnBattlefield + ) + ); + ability.addEffect(new CantAttackYouOrPlaneswalkerAllEffect( + Duration.WhileOnBattlefield, filter1 + ).setText("and can't attack you or a planeswalker you control")); + this.addAbility(ability); + + // When Varchild leaves the battlefield, gain control of all Survivors. + this.addAbility(new LeavesBattlefieldTriggeredAbility( + new GainControlAllEffect(Duration.Custom, filter2), false + )); + } + + public VarchildBetrayerOfKjeldor(final VarchildBetrayerOfKjeldor card) { + super(card); + } + + @Override + public VarchildBetrayerOfKjeldor copy() { + return new VarchildBetrayerOfKjeldor(this); + } +} + +class VarchildBetrayerOfKjeldorEffect extends OneShotEffect { + + public VarchildBetrayerOfKjeldorEffect() { + super(Outcome.Benefit); + this.staticText = "that player creates " + + "that many 1/1 red Survivor creature tokens"; + } + + public VarchildBetrayerOfKjeldorEffect(final VarchildBetrayerOfKjeldorEffect effect) { + super(effect); + } + + @Override + public VarchildBetrayerOfKjeldorEffect copy() { + return new VarchildBetrayerOfKjeldorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int damage = (int) this.getValue("damage"); + if (damage > 0) { + return new CreateTokenTargetEffect( + new SurvivorToken(), damage + ).apply(game, source); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VarinaLichQueen.java b/Mage.Sets/src/mage/cards/v/VarinaLichQueen.java new file mode 100644 index 00000000000..514473cd7be --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VarinaLichQueen.java @@ -0,0 +1,117 @@ +package mage.cards.v; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.ZombieToken; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author TheElk801 + */ +public final class VarinaLichQueen extends CardImpl { + + public VarinaLichQueen(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever you attack with one or more Zombies, draw that many cards, then discard that many cards. You gain that much life. + this.addAbility(new VarinaLichQueenTriggeredAbility()); + + // {2}, Exile two cards from your graveyard: Create a tapped 2/2 black Zombie creature token. + Ability ability = new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new CreateTokenEffect( + new ZombieToken(), + 1, true, false + ), new GenericManaCost(2) + ); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard( + 2, new FilterCard("cards from your graveyard") + ))); + this.addAbility(ability); + } + + public VarinaLichQueen(final VarinaLichQueen card) { + super(card); + } + + @Override + public VarinaLichQueen copy() { + return new VarinaLichQueen(this); + } +} + +class VarinaLichQueenTriggeredAbility extends TriggeredAbilityImpl { + + public VarinaLichQueenTriggeredAbility() { + super(Zone.BATTLEFIELD, null, false); + } + + public VarinaLichQueenTriggeredAbility(final VarinaLichQueenTriggeredAbility ability) { + super(ability); + } + + @Override + public VarinaLichQueenTriggeredAbility copy() { + return new VarinaLichQueenTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + int attackingZombies = 0; + for (UUID attacker : game.getCombat().getAttackers()) { + Permanent creature = game.getPermanent(attacker); + if (creature != null + && creature.getControllerId() != null + && creature.isControlledBy(this.getControllerId()) + && creature.hasSubtype(SubType.ZOMBIE, game)) { + attackingZombies++; + } + } + if (attackingZombies > 0) { + this.getEffects().clear(); + addEffect(new DrawCardSourceControllerEffect(attackingZombies)); + addEffect(new DiscardControllerEffect(attackingZombies, false)); + addEffect(new GainLifeEffect(attackingZombies)); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever you attack with one or more Zombies, " + + "draw that many cards, then discard that many cards. " + + "You gain that much life."; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VedalkenCertarch.java b/Mage.Sets/src/mage/cards/v/VedalkenCertarch.java index 2e590c66e38..afa85439ab1 100644 --- a/Mage.Sets/src/mage/cards/v/VedalkenCertarch.java +++ b/Mage.Sets/src/mage/cards/v/VedalkenCertarch.java @@ -43,7 +43,7 @@ public final class VedalkenCertarch extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - // Metalcraft - {T}: Tap target artifact, creature, or land. Activate this ability only if you control three or more artifacts. + // Metalcraft — {T}: Tap target artifact, creature, or land. Activate this ability only if you control three or more artifacts. Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new TapSourceCost(), MetalcraftCondition.instance); ability.setAbilityWord(AbilityWord.METALCRAFT); ability.addTarget(new TargetPermanent(filter)); diff --git a/Mage.Sets/src/mage/cards/v/VedalkenHumiliator.java b/Mage.Sets/src/mage/cards/v/VedalkenHumiliator.java new file mode 100644 index 00000000000..9997b830b2d --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VedalkenHumiliator.java @@ -0,0 +1,61 @@ +package mage.cards.v; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.condition.common.MetalcraftCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.continuous.LoseAllAbilitiesAllEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessAllEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.StaticFilters; + +/** + * + * @author TheElk801 + */ +public final class VedalkenHumiliator extends CardImpl { + + public VedalkenHumiliator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.VEDALKEN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Metalcraft — Whenever Vedalken Humiliator attacks, if you control three or more artifacts, creatures your opponents control lose all abilities and have base power and toughness 1/1 until end of turn. + TriggeredAbility ability = new AttacksTriggeredAbility( + new SetPowerToughnessAllEffect( + 1, 1, Duration.EndOfTurn, + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, + true + ), false + ); + ability.addEffect(new LoseAllAbilitiesAllEffect( + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, + Duration.EndOfTurn + )); + this.addAbility(new ConditionalTriggeredAbility( + ability, MetalcraftCondition.instance, + "Metalcraft — Whenever {this} attacks, " + + "if you control three or more artifacts, " + + "creatures your opponents control lose all abilities " + + "and have base power and toughness 1/1 until end of turn." + )); + } + + public VedalkenHumiliator(final VedalkenHumiliator card) { + super(card); + } + + @Override + public VedalkenHumiliator copy() { + return new VedalkenHumiliator(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/Wakedancer.java b/Mage.Sets/src/mage/cards/w/Wakedancer.java index 095f2420b0b..2616976978c 100644 --- a/Mage.Sets/src/mage/cards/w/Wakedancer.java +++ b/Mage.Sets/src/mage/cards/w/Wakedancer.java @@ -20,7 +20,7 @@ import mage.game.permanent.token.ZombieToken; */ public final class Wakedancer extends CardImpl { - private static final String staticText = "Morbid - When {this} enters the battlefield, if a creature died this turn, create a 2/2 black Zombie creature token."; + private static final String staticText = "Morbid — When {this} enters the battlefield, if a creature died this turn, create a 2/2 black Zombie creature token."; public Wakedancer(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); @@ -31,7 +31,7 @@ public final class Wakedancer extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // Morbid - When Wakedancer enters the battlefield, if a creature died this turn, create a 2/2 black Zombie creature token. + // Morbid — When Wakedancer enters the battlefield, if a creature died this turn, create a 2/2 black Zombie creature token. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ZombieToken())); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MorbidCondition.instance, staticText)); } diff --git a/Mage.Sets/src/mage/cards/w/WhiptongueHydra.java b/Mage.Sets/src/mage/cards/w/WhiptongueHydra.java new file mode 100644 index 00000000000..fae1fde2f16 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WhiptongueHydra.java @@ -0,0 +1,91 @@ +package mage.cards.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.constants.SubType; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author TheElk801 + */ +public final class WhiptongueHydra extends CardImpl { + + public WhiptongueHydra(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}"); + + this.subtype.add(SubType.LIZARD); + this.subtype.add(SubType.HYDRA); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // When Whiptongue Hydra enters the battlefield, destroy all creatures with flying. Put a +1/+1 counter on Whiptongue Hydra for each creature destroyed this way. + this.addAbility(new EntersBattlefieldTriggeredAbility(new WhiptongueHydraEffect(), false)); + } + + public WhiptongueHydra(final WhiptongueHydra card) { + super(card); + } + + @Override + public WhiptongueHydra copy() { + return new WhiptongueHydra(this); + } +} + +class WhiptongueHydraEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public WhiptongueHydraEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "destroy all creatures with flying. " + + "Put a +1/+1 counter on {this} for each permanent destroyed this way"; + } + + public WhiptongueHydraEffect(final WhiptongueHydraEffect effect) { + super(effect); + } + + @Override + public WhiptongueHydraEffect copy() { + return new WhiptongueHydraEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int destroyedPermanents = 0; + destroyedPermanents = game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + ).stream().filter( + (permanent) -> (permanent.destroy(source.getSourceId(), game, false)) + ).map((_item) -> 1).reduce(destroyedPermanents, Integer::sum); + if (destroyedPermanents > 0) { + return new AddCountersSourceEffect( + CounterType.P1P1.createInstance(destroyedPermanents), true + ).apply(game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WindgracesJudgment.java b/Mage.Sets/src/mage/cards/w/WindgracesJudgment.java new file mode 100644 index 00000000000..00d95d3090d --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WindgracesJudgment.java @@ -0,0 +1,60 @@ +package mage.cards.w; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; + +/** + * + * @author TheElk801 + */ +public final class WindgracesJudgment extends CardImpl { + + public WindgracesJudgment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}{G}"); + + // For any number of opponents, destroy target nonland permanent that player controls. + this.getSpellAbility().addEffect( + new DestroyTargetEffect(). + setText("For any number of opponents, " + + "destroy target nonland permanent " + + "that player controls") + ); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SpellAbility) { + ability.getTargets().clear(); + game.getOpponents(ability.getControllerId()).forEach(playerId -> { + Player player = game.getPlayer(playerId); + if (player != null) { + FilterNonlandPermanent filter = new FilterNonlandPermanent( + "nonland permanent controlled by " + + player.getLogName() + ); + filter.add(new ControllerIdPredicate(playerId)); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + } + }); + } + } + + public WindgracesJudgment(final WindgracesJudgment card) { + super(card); + } + + @Override + public WindgracesJudgment copy() { + return new WindgracesJudgment(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WoodlandSleuth.java b/Mage.Sets/src/mage/cards/w/WoodlandSleuth.java index 00b5fc45ef7..bb5b2b2a95e 100644 --- a/Mage.Sets/src/mage/cards/w/WoodlandSleuth.java +++ b/Mage.Sets/src/mage/cards/w/WoodlandSleuth.java @@ -27,7 +27,7 @@ import mage.util.RandomUtil; */ public final class WoodlandSleuth extends CardImpl { - private static final String staticText = "Morbid - When {this} enters the battlefield, if a creature died this turn, return a creature card at random from your graveyard to your hand."; + private static final String staticText = "Morbid — When {this} enters the battlefield, if a creature died this turn, return a creature card at random from your graveyard to your hand."; public WoodlandSleuth(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); @@ -38,7 +38,7 @@ public final class WoodlandSleuth extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - // Morbid - When Woodland Sleuth enters the battlefield, if a creature died this turn, return a creature card at random from your graveyard to your hand. + // Morbid — When Woodland Sleuth enters the battlefield, if a creature died this turn, return a creature card at random from your graveyard to your hand. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new WoodlandSleuthEffect()); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MorbidCondition.instance, staticText)); } diff --git a/Mage.Sets/src/mage/cards/y/YennetCryptSovereign.java b/Mage.Sets/src/mage/cards/y/YennetCryptSovereign.java new file mode 100644 index 00000000000..59b7e84f17f --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YennetCryptSovereign.java @@ -0,0 +1,101 @@ +package mage.cards.y; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author TheElk801 + */ +public final class YennetCryptSovereign extends CardImpl { + + public YennetCryptSovereign(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPHINX); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever Yennet, Crypt Sovereign attacks, reveal the top card of your library. If that card's converted mana cost is odd, you may cast it without paying its mana cost. Otherwise, draw a card. + this.addAbility(new AttacksTriggeredAbility( + new YennetCryptSovereignEffect(), false + )); + } + + public YennetCryptSovereign(final YennetCryptSovereign card) { + super(card); + } + + @Override + public YennetCryptSovereign copy() { + return new YennetCryptSovereign(this); + } +} + +class YennetCryptSovereignEffect extends OneShotEffect { + + public YennetCryptSovereignEffect() { + super(Outcome.Benefit); + this.staticText = "reveal the top card of your library. " + + "If that card's converted mana cost is odd, " + + "you may cast it without paying its mana cost. " + + "Otherwise, draw a card"; + } + + public YennetCryptSovereignEffect(final YennetCryptSovereignEffect effect) { + super(effect); + } + + @Override + public YennetCryptSovereignEffect copy() { + return new YennetCryptSovereignEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || !player.getLibrary().hasCards()) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.revealCards(source, new CardsImpl(card), game); + if (card.getConvertedManaCost() % 2 == 1) { + if (player.chooseUse(outcome, "Cast " + card.getLogName() + " without paying its mana cost?", source, game)) { + player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game)); + } + } else { + player.drawCards(1, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/Commander2018.java b/Mage.Sets/src/mage/sets/Commander2018.java index a7684a575a0..7f1aeea8b0b 100644 --- a/Mage.Sets/src/mage/sets/Commander2018.java +++ b/Mage.Sets/src/mage/sets/Commander2018.java @@ -20,13 +20,307 @@ public final class Commander2018 extends ExpansionSet { super("Commander 2018 Edition", "C18", ExpansionSet.buildDate(2018, 8, 10), SetType.SUPPLEMENTAL); this.blockName = "Command Zone"; + cards.add(new SetCardInfo("Acidic Slime", 127, Rarity.UNCOMMON, mage.cards.a.AcidicSlime.class)); + cards.add(new SetCardInfo("Adarkar Valkyrie", 60, Rarity.RARE, mage.cards.a.AdarkarValkyrie.class)); + cards.add(new SetCardInfo("Aether Gale", 80, Rarity.RARE, mage.cards.a.AetherGale.class)); + cards.add(new SetCardInfo("Aethermage's Touch", 168, Rarity.RARE, mage.cards.a.AethermagesTouch.class)); + cards.add(new SetCardInfo("Ajani's Chosen", 61, Rarity.RARE, mage.cards.a.AjanisChosen.class)); + cards.add(new SetCardInfo("Akoum Refuge", 231, Rarity.UNCOMMON, mage.cards.a.AkoumRefuge.class)); + cards.add(new SetCardInfo("Akroma's Vengeance", 62, Rarity.RARE, mage.cards.a.AkromasVengeance.class)); + cards.add(new SetCardInfo("Amninatou, the Fateshifter", 37, Rarity.MYTHIC, mage.cards.a.AminatouTheFateShifter.class)); + cards.add(new SetCardInfo("Ancient Stone Idol", 53, Rarity.RARE, mage.cards.a.AncientStoneIdol.class)); + cards.add(new SetCardInfo("Arcane Sanctum", 232, Rarity.UNCOMMON, mage.cards.a.ArcaneSanctum.class)); + cards.add(new SetCardInfo("Archetype of Imagination", 81, Rarity.UNCOMMON, mage.cards.a.ArchetypeOfImagination.class)); + cards.add(new SetCardInfo("Arixmethes, Slumbering Isle", 38, Rarity.RARE, mage.cards.a.ArixmethesSlumberingIsle.class)); + cards.add(new SetCardInfo("Army of the Damned", 113, Rarity.MYTHIC, mage.cards.a.ArmyOfTheDamned.class)); + cards.add(new SetCardInfo("Aura Gnarlid", 128, Rarity.COMMON, mage.cards.a.AuraGnarlid.class)); + cards.add(new SetCardInfo("Avenger of Zendikar", 129, Rarity.MYTHIC, mage.cards.a.AvengerOfZendikar.class)); + cards.add(new SetCardInfo("Azorius Chancery", 233, Rarity.UNCOMMON, mage.cards.a.AzoriusChancery.class)); + cards.add(new SetCardInfo("Azorius Guildgate", 234, Rarity.COMMON, mage.cards.a.AzoriusGuildgate.class)); + cards.add(new SetCardInfo("Azorius Signet", 196, Rarity.UNCOMMON, mage.cards.a.AzoriusSignet.class)); + cards.add(new SetCardInfo("Baloth Woodcrasher", 130, Rarity.UNCOMMON, mage.cards.b.BalothWoodcrasher.class)); + cards.add(new SetCardInfo("Banishing Stroke", 63, Rarity.UNCOMMON, mage.cards.b.BanishingStroke.class)); + cards.add(new SetCardInfo("Bant Charm", 169, Rarity.UNCOMMON, mage.cards.b.BantCharm.class)); + cards.add(new SetCardInfo("Barren Moor", 235, Rarity.COMMON, mage.cards.b.BarrenMoor.class)); + cards.add(new SetCardInfo("Bear Umbra", 131, Rarity.RARE, mage.cards.b.BearUmbra.class)); + cards.add(new SetCardInfo("Blasphemous Act", 120, Rarity.RARE, mage.cards.b.BlasphemousAct.class)); + cards.add(new SetCardInfo("Blighted Woodland", 236, Rarity.UNCOMMON, mage.cards.b.BlightedWoodland.class)); + cards.add(new SetCardInfo("Blinkmoth Urn", 197, Rarity.RARE, mage.cards.b.BlinkmothUrn.class)); + cards.add(new SetCardInfo("Bloodtracker", 14, Rarity.RARE, mage.cards.b.Bloodtracker.class)); + cards.add(new SetCardInfo("Blossoming Sands", 237, Rarity.COMMON, mage.cards.b.BlossomingSands.class)); + cards.add(new SetCardInfo("Bojuka Bog", 238, Rarity.COMMON, mage.cards.b.BojukaBog.class)); + cards.add(new SetCardInfo("Boon Satyr", 132, Rarity.RARE, mage.cards.b.BoonSatyr.class)); + cards.add(new SetCardInfo("Borderland Explorer", 133, Rarity.COMMON, mage.cards.b.BorderlandExplorer.class)); + cards.add(new SetCardInfo("Boreas Charger", 1, Rarity.RARE, mage.cards.b.BoreasCharger.class)); + cards.add(new SetCardInfo("Bosh, Iron Golem", 198, Rarity.RARE, mage.cards.b.BoshIronGolem.class)); + cards.add(new SetCardInfo("Brainstorm", 82, Rarity.UNCOMMON, mage.cards.b.Brainstorm.class)); + cards.add(new SetCardInfo("Brudiclad, Telchor Engineer", 39, Rarity.MYTHIC, mage.cards.b.BrudicladTelchorEngineer.class)); + cards.add(new SetCardInfo("Bruna, Light of Alabaster", 170, Rarity.MYTHIC, mage.cards.b.BrunaLightOfAlabaster.class)); + cards.add(new SetCardInfo("Budoka Gardener", 134, Rarity.RARE, mage.cards.b.BudokaGardener.class)); + cards.add(new SetCardInfo("Buried Ruin", 239, Rarity.UNCOMMON, mage.cards.b.BuriedRuin.class)); + cards.add(new SetCardInfo("Celestial Archon", 64, Rarity.RARE, mage.cards.c.CelestialArchon.class)); + cards.add(new SetCardInfo("Centaur Vinecrasher", 135, Rarity.RARE, mage.cards.c.CentaurVinecrasher.class)); + cards.add(new SetCardInfo("Chain Reaction", 121, Rarity.RARE, mage.cards.c.ChainReaction.class)); cards.add(new SetCardInfo("Chaos Warp", 122, Rarity.RARE, mage.cards.c.ChaosWarp.class)); + cards.add(new SetCardInfo("Charnelhoard Wurm", 171, Rarity.RARE, mage.cards.c.CharnelhoardWurm.class)); + cards.add(new SetCardInfo("Chief of the Foundry", 199, Rarity.UNCOMMON, mage.cards.c.ChiefOfTheFoundry.class)); + cards.add(new SetCardInfo("Cloudform", 83, Rarity.UNCOMMON, mage.cards.c.Cloudform.class)); + cards.add(new SetCardInfo("Cold-Eyed Selkie", 172, Rarity.RARE, mage.cards.c.ColdEyedSelkie.class)); + cards.add(new SetCardInfo("Command Tower", 240, Rarity.COMMON, mage.cards.c.CommandTower.class)); + cards.add(new SetCardInfo("Commander's Sphere", 200, Rarity.COMMON, mage.cards.c.CommandersSphere.class)); + cards.add(new SetCardInfo("Consign to Dust", 136, Rarity.UNCOMMON, mage.cards.c.ConsignToDust.class)); + cards.add(new SetCardInfo("Conundrum Sphinx", 84, Rarity.RARE, mage.cards.c.ConundrumSphinx.class)); + cards.add(new SetCardInfo("Coveted Jewel", 54, Rarity.RARE, mage.cards.c.CovetedJewel.class)); + cards.add(new SetCardInfo("Crash of Rhino Beetles", 29, Rarity.RARE, mage.cards.c.CrashOfRhinoBeetles.class)); + cards.add(new SetCardInfo("Creeping Renaissance", 137, Rarity.RARE, mage.cards.c.CreepingRenaissance.class)); + cards.add(new SetCardInfo("Crib Swap", 65, Rarity.UNCOMMON, mage.cards.c.CribSwap.class)); + cards.add(new SetCardInfo("Crystal Ball", 201, Rarity.UNCOMMON, mage.cards.c.CrystalBall.class)); + cards.add(new SetCardInfo("Cultivate", 138, Rarity.COMMON, mage.cards.c.Cultivate.class)); + cards.add(new SetCardInfo("Darksteel Citadel", 241, Rarity.UNCOMMON, mage.cards.d.DarksteelCitadel.class)); + cards.add(new SetCardInfo("Darksteel Juggernaut", 202, Rarity.RARE, mage.cards.d.DarksteelJuggernaut.class)); + cards.add(new SetCardInfo("Dawn's Reflection", 139, Rarity.COMMON, mage.cards.d.DawnsReflection.class)); + cards.add(new SetCardInfo("Daxos of Meletis", 173, Rarity.RARE, mage.cards.d.DaxosOfMeletis.class)); + cards.add(new SetCardInfo("Deathreap Ritual", 174, Rarity.UNCOMMON, mage.cards.d.DeathreapRitual.class)); + cards.add(new SetCardInfo("Decimate", 175, Rarity.RARE, mage.cards.d.Decimate.class)); + cards.add(new SetCardInfo("Devastation Tide", 85, Rarity.RARE, mage.cards.d.DevastationTide.class)); + cards.add(new SetCardInfo("Dictate of Kruphix", 86, Rarity.RARE, mage.cards.d.DictateOfKruphix.class)); + cards.add(new SetCardInfo("Dimir Aqueduct", 242, Rarity.UNCOMMON, mage.cards.d.DimirAqueduct.class)); + cards.add(new SetCardInfo("Dimir Guildgate", 243, Rarity.COMMON, mage.cards.d.DimirGuildgate.class)); + cards.add(new SetCardInfo("Dimir Signet", 203, Rarity.UNCOMMON, mage.cards.d.DimirSignet.class)); + cards.add(new SetCardInfo("Dismal Backwater", 244, Rarity.COMMON, mage.cards.d.DismalBackwater.class)); + cards.add(new SetCardInfo("Dismantling Blow", 66, Rarity.COMMON, mage.cards.d.DismantlingBlow.class)); + cards.add(new SetCardInfo("Djinn of Wishes", 87, Rarity.RARE, mage.cards.d.DjinnOfWishes.class)); + cards.add(new SetCardInfo("Dream Cache", 88, Rarity.COMMON, mage.cards.d.DreamCache.class)); + cards.add(new SetCardInfo("Dreamstone Hedron", 204, Rarity.UNCOMMON, mage.cards.d.DreamstoneHedron.class)); + cards.add(new SetCardInfo("Duplicant", 205, Rarity.RARE, mage.cards.d.Duplicant.class)); + cards.add(new SetCardInfo("Duskmantle Seer", 176, Rarity.RARE, mage.cards.d.DuskmantleSeer.class)); + cards.add(new SetCardInfo("Echo Storm", 7, Rarity.RARE, mage.cards.e.EchoStorm.class)); + cards.add(new SetCardInfo("Eel Umbra", 89, Rarity.COMMON, mage.cards.e.EelUmbra.class)); + cards.add(new SetCardInfo("Eidolon of Blossoms", 140, Rarity.RARE, mage.cards.e.EidolonOfBlossoms.class)); + cards.add(new SetCardInfo("Elderwood Scion", 177, Rarity.RARE, mage.cards.e.ElderwoodScion.class)); + cards.add(new SetCardInfo("Empyrial Storm", 2, Rarity.RARE, mage.cards.e.EmpyrialStorm.class)); cards.add(new SetCardInfo("Enchanter's Bane", 21, Rarity.RARE, mage.cards.e.EnchantersBane.class)); + cards.add(new SetCardInfo("Enchantress's Presence", 141, Rarity.RARE, mage.cards.e.EnchantresssPresence.class)); + cards.add(new SetCardInfo("Endless Atlas", 55, Rarity.RARE, mage.cards.e.EndlessAtlas.class)); + cards.add(new SetCardInfo("Enigma Sphinx", 178, Rarity.RARE, mage.cards.e.EnigmaSphinx.class)); + cards.add(new SetCardInfo("Entreat the Angels", 67, Rarity.MYTHIC, mage.cards.e.EntreatTheAngels.class)); + cards.add(new SetCardInfo("Entreat the Dead", 15, Rarity.RARE, mage.cards.e.EntreatTheDead.class)); + cards.add(new SetCardInfo("Epic Proportions", 142, Rarity.RARE, mage.cards.e.EpicProportions.class)); + cards.add(new SetCardInfo("Esper Charm", 179, Rarity.UNCOMMON, mage.cards.e.EsperCharm.class)); + cards.add(new SetCardInfo("Estrid's Invocation", 8, Rarity.RARE, mage.cards.e.EstridsInvocation.class)); + cards.add(new SetCardInfo("Estrid, the Masked", 40, Rarity.MYTHIC, mage.cards.e.EstridTheMasked.class)); + cards.add(new SetCardInfo("Etherium Sculptor", 90, Rarity.COMMON, mage.cards.e.EtheriumSculptor.class)); + cards.add(new SetCardInfo("Ever-Watching Threshold", 9, Rarity.RARE, mage.cards.e.EverWatchingThreshold.class)); + cards.add(new SetCardInfo("Evolving Wilds", 245, Rarity.COMMON, mage.cards.e.EvolvingWilds.class)); + cards.add(new SetCardInfo("Explore", 143, Rarity.COMMON, mage.cards.e.Explore.class)); + cards.add(new SetCardInfo("Explosive Vegetation", 144, Rarity.UNCOMMON, mage.cards.e.ExplosiveVegetation.class)); + cards.add(new SetCardInfo("Far Wanderings", 145, Rarity.COMMON, mage.cards.f.FarWanderings.class)); + cards.add(new SetCardInfo("Farhaven Elf", 146, Rarity.COMMON, mage.cards.f.FarhavenElf.class)); + cards.add(new SetCardInfo("Fertile Ground", 147, Rarity.COMMON, mage.cards.f.FertileGround.class)); + cards.add(new SetCardInfo("Finest Hour", 180, Rarity.RARE, mage.cards.f.FinestHour.class)); + cards.add(new SetCardInfo("Flameblast Dragon", 123, Rarity.RARE, mage.cards.f.FlameblastDragon.class)); + cards.add(new SetCardInfo("Forest", 305, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 306, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 307, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forge of Heroes", 58, Rarity.COMMON, mage.cards.f.ForgeOfHeroes.class)); + cards.add(new SetCardInfo("Forgotten Cave", 246, Rarity.COMMON, mage.cards.f.ForgottenCave.class)); + cards.add(new SetCardInfo("Forsaken Sanctuary", 247, Rarity.UNCOMMON, mage.cards.f.ForsakenSanctuary.class)); + cards.add(new SetCardInfo("Foundry of the Consuls", 248, Rarity.UNCOMMON, mage.cards.f.FoundryOfTheConsuls.class)); + cards.add(new SetCardInfo("Fury Storm", 22, Rarity.RARE, mage.cards.f.FuryStorm.class)); + cards.add(new SetCardInfo("Gaze of Granite", 181, Rarity.RARE, mage.cards.g.GazeOfGranite.class)); + cards.add(new SetCardInfo("Genesis Storm", 30, Rarity.RARE, mage.cards.g.GenesisStorm.class)); + cards.add(new SetCardInfo("Golgari Rot Farm", 249, Rarity.UNCOMMON, mage.cards.g.GolgariRotFarm.class)); + cards.add(new SetCardInfo("Grapple with the Past", 148, Rarity.COMMON, mage.cards.g.GrappleWithThePast.class)); + cards.add(new SetCardInfo("Great Furnace", 250, Rarity.COMMON, mage.cards.g.GreatFurnace.class)); + cards.add(new SetCardInfo("Grim Backwoods", 251, Rarity.RARE, mage.cards.g.GrimBackwoods.class)); + cards.add(new SetCardInfo("Grisly Salvage", 182, Rarity.COMMON, mage.cards.g.GrislySalvage.class)); + cards.add(new SetCardInfo("Ground Seal", 149, Rarity.RARE, mage.cards.g.GroundSeal.class)); + cards.add(new SetCardInfo("Gruul Turf", 252, Rarity.UNCOMMON, mage.cards.g.GruulTurf.class)); + cards.add(new SetCardInfo("Gyrus, Waker of Corpses", 41, Rarity.MYTHIC, mage.cards.g.GyrusWakerOfCorpses.class)); + cards.add(new SetCardInfo("Halimar Depths", 253, Rarity.COMMON, mage.cards.h.HalimarDepths.class)); + cards.add(new SetCardInfo("Harrow", 150, Rarity.COMMON, mage.cards.h.Harrow.class)); + cards.add(new SetCardInfo("Haunted Fengraf", 254, Rarity.COMMON, mage.cards.h.HauntedFengraf.class)); + cards.add(new SetCardInfo("Heavenly Blademaster", 3, Rarity.RARE, mage.cards.h.HeavenlyBlademaster.class)); + cards.add(new SetCardInfo("Hedron Archive", 206, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class)); + cards.add(new SetCardInfo("Hellkite Igniter", 124, Rarity.RARE, mage.cards.h.HellkiteIgniter.class)); + cards.add(new SetCardInfo("Herald of the Pantheon", 151, Rarity.RARE, mage.cards.h.HeraldOfThePantheon.class)); + cards.add(new SetCardInfo("High Priest of Penance", 183, Rarity.RARE, mage.cards.h.HighPriestOfPenance.class)); + cards.add(new SetCardInfo("Highland Lake", 255, Rarity.UNCOMMON, mage.cards.h.HighlandLake.class)); + cards.add(new SetCardInfo("Hunting Wilds", 152, Rarity.UNCOMMON, mage.cards.h.HuntingWilds.class)); + cards.add(new SetCardInfo("Hydra Omnivore", 153, Rarity.MYTHIC, mage.cards.h.HydraOmnivore.class)); + cards.add(new SetCardInfo("Inkwell Leviathan", 91, Rarity.RARE, mage.cards.i.InkwellLeviathan.class)); + cards.add(new SetCardInfo("Into the Roil", 92, Rarity.COMMON, mage.cards.i.IntoTheRoil.class)); + cards.add(new SetCardInfo("Island", 296, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 297, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 298, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Isolated Watchtower", 59, Rarity.RARE, mage.cards.i.IsolatedWatchtower.class)); + cards.add(new SetCardInfo("Izzet Boilerworks", 256, Rarity.UNCOMMON, mage.cards.i.IzzetBoilerworks.class)); + cards.add(new SetCardInfo("Izzet Guildgate", 257, Rarity.COMMON, mage.cards.i.IzzetGuildgate.class)); + cards.add(new SetCardInfo("Izzet Signet", 207, Rarity.UNCOMMON, mage.cards.i.IzzetSignet.class)); + cards.add(new SetCardInfo("Jeskai Infiltrator", 93, Rarity.RARE, mage.cards.j.JeskaiInfiltrator.class)); + cards.add(new SetCardInfo("Jund Panorama", 258, Rarity.COMMON, mage.cards.j.JundPanorama.class)); + cards.add(new SetCardInfo("Jungle Hollow", 259, Rarity.COMMON, mage.cards.j.JungleHollow.class)); + cards.add(new SetCardInfo("Jwar Isle Refuge", 260, Rarity.UNCOMMON, mage.cards.j.JwarIsleRefuge.class)); + cards.add(new SetCardInfo("Kazandu Refuge", 261, Rarity.UNCOMMON, mage.cards.k.KazanduRefuge.class)); + cards.add(new SetCardInfo("Kestia, the Cultivator", 42, Rarity.MYTHIC, mage.cards.k.KestiaTheCultivator.class)); + cards.add(new SetCardInfo("Khalni Garden", 262, Rarity.COMMON, mage.cards.k.KhalniGarden.class)); + cards.add(new SetCardInfo("Khalni Heart Expedition", 154, Rarity.COMMON, mage.cards.k.KhalniHeartExpedition.class)); + cards.add(new SetCardInfo("Krosan Verge", 263, Rarity.UNCOMMON, mage.cards.k.KrosanVerge.class)); + cards.add(new SetCardInfo("Kruphix's Insight", 155, Rarity.COMMON, mage.cards.k.KruphixsInsight.class)); + cards.add(new SetCardInfo("Lavalanche", 184, Rarity.RARE, mage.cards.l.Lavalanche.class)); + cards.add(new SetCardInfo("Lightform", 68, Rarity.UNCOMMON, mage.cards.l.Lightform.class)); + cards.add(new SetCardInfo("Lonely Sandbar", 264, Rarity.COMMON, mage.cards.l.LonelySandbar.class)); + cards.add(new SetCardInfo("Lord Windgrace", 43, Rarity.MYTHIC, mage.cards.l.LordWindgrace.class)); + cards.add(new SetCardInfo("Loyal Apprentice", 23, Rarity.UNCOMMON, mage.cards.l.LoyalApprentice.class)); cards.add(new SetCardInfo("Loyal Drake", 10, Rarity.UNCOMMON, mage.cards.l.LoyalDrake.class)); + cards.add(new SetCardInfo("Loyal Guardian", 31, Rarity.UNCOMMON, mage.cards.l.LoyalGuardian.class)); + cards.add(new SetCardInfo("Loyal Subordinate", 16, Rarity.UNCOMMON, mage.cards.l.LoyalSubordinate.class)); + cards.add(new SetCardInfo("Loyal Unicorn", 4, Rarity.UNCOMMON, mage.cards.l.LoyalUnicorn.class)); + cards.add(new SetCardInfo("Magmaquake", 125, Rarity.RARE, mage.cards.m.Magmaquake.class)); + cards.add(new SetCardInfo("Magnifying Glass", 208, Rarity.UNCOMMON, mage.cards.m.MagnifyingGlass.class)); + cards.add(new SetCardInfo("Magus of the Balance", 5, Rarity.RARE, mage.cards.m.MagusOfTheBalance.class)); + cards.add(new SetCardInfo("Martial Coup", 69, Rarity.RARE, mage.cards.m.MartialCoup.class)); + cards.add(new SetCardInfo("Maverick Thopterist", 185, Rarity.UNCOMMON, mage.cards.m.MaverickThopterist.class)); + cards.add(new SetCardInfo("Meandering River", 265, Rarity.UNCOMMON, mage.cards.m.MeanderingRiver.class)); + cards.add(new SetCardInfo("Mimic Vat", 209, Rarity.RARE, mage.cards.m.MimicVat.class)); + cards.add(new SetCardInfo("Mind Stone", 210, Rarity.COMMON, mage.cards.m.MindStone.class)); + cards.add(new SetCardInfo("Mirrorworks", 211, Rarity.RARE, mage.cards.m.Mirrorworks.class)); + cards.add(new SetCardInfo("Moldgraf Monstrosity", 156, Rarity.RARE, mage.cards.m.MoldgrafMonstrosity.class)); + cards.add(new SetCardInfo("Moonlight Bargain", 114, Rarity.RARE, mage.cards.m.MoonlightBargain.class)); + cards.add(new SetCardInfo("Mortify", 186, Rarity.UNCOMMON, mage.cards.m.Mortify.class)); + cards.add(new SetCardInfo("Mortuary Mire", 266, Rarity.COMMON, mage.cards.m.MortuaryMire.class)); + cards.add(new SetCardInfo("Mosswort Bridge", 267, Rarity.RARE, mage.cards.m.MosswortBridge.class)); + cards.add(new SetCardInfo("Mountain Valley", 268, Rarity.UNCOMMON, mage.cards.m.MountainValley.class)); + cards.add(new SetCardInfo("Mountain", 302, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 303, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 304, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mulldrifter", 94, Rarity.UNCOMMON, mage.cards.m.Mulldrifter.class)); + cards.add(new SetCardInfo("Myr Battlesphere", 212, Rarity.RARE, mage.cards.m.MyrBattlesphere.class)); + cards.add(new SetCardInfo("Myriad Landscape", 269, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); + cards.add(new SetCardInfo("Myth Unbound", 32, Rarity.RARE, mage.cards.m.MythUnbound.class)); + cards.add(new SetCardInfo("Nesting Dragon", 24, Rarity.RARE, mage.cards.n.NestingDragon.class)); + cards.add(new SetCardInfo("New Benalia", 270, Rarity.UNCOMMON, mage.cards.n.NewBenalia.class)); + cards.add(new SetCardInfo("Night Incarnate", 17, Rarity.RARE, mage.cards.n.NightIncarnate.class)); + cards.add(new SetCardInfo("Ninja of the Deep Hours", 95, Rarity.COMMON, mage.cards.n.NinjaOfTheDeepHours.class)); + cards.add(new SetCardInfo("Nylea's Colossus", 33, Rarity.RARE, mage.cards.n.NyleasColossus.class)); + cards.add(new SetCardInfo("Octopus Umbra", 11, Rarity.RARE, mage.cards.o.OctopusUmbra.class)); + cards.add(new SetCardInfo("Orzhov Basilica", 271, Rarity.UNCOMMON, mage.cards.o.OrzhovBasilica.class)); + cards.add(new SetCardInfo("Orzhov Guildgate", 272, Rarity.COMMON, mage.cards.o.OrzhovGuildgate.class)); + cards.add(new SetCardInfo("Orzhov Signet", 213, Rarity.UNCOMMON, mage.cards.o.OrzhovSignet.class)); + cards.add(new SetCardInfo("Overgrowth", 157, Rarity.COMMON, mage.cards.o.Overgrowth.class)); + cards.add(new SetCardInfo("Phyrexian Delver", 115, Rarity.RARE, mage.cards.p.PhyrexianDelver.class)); + cards.add(new SetCardInfo("Phyrexian Rebirth", 70, Rarity.RARE, mage.cards.p.PhyrexianRebirth.class)); + cards.add(new SetCardInfo("Pilgrim's Eye", 214, Rarity.COMMON, mage.cards.p.PilgrimsEye.class)); + cards.add(new SetCardInfo("Plains", 293, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 294, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 295, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ponder", 96, Rarity.COMMON, mage.cards.p.Ponder.class)); + cards.add(new SetCardInfo("Portent", 97, Rarity.COMMON, mage.cards.p.Portent.class)); + cards.add(new SetCardInfo("Predict", 98, Rarity.UNCOMMON, mage.cards.p.Predict.class)); + cards.add(new SetCardInfo("Primordial Mist", 12, Rarity.RARE, mage.cards.p.PrimordialMist.class)); + cards.add(new SetCardInfo("Prismatic Lens", 215, Rarity.UNCOMMON, mage.cards.p.PrismaticLens.class)); + cards.add(new SetCardInfo("Prototype Portal", 216, Rarity.RARE, mage.cards.p.PrototypePortal.class)); + cards.add(new SetCardInfo("Psychosis Crawler", 217, Rarity.RARE, mage.cards.p.PsychosisCrawler.class)); + cards.add(new SetCardInfo("Putrefy", 187, Rarity.UNCOMMON, mage.cards.p.Putrefy.class)); + cards.add(new SetCardInfo("Rakdos Carnarium", 273, Rarity.COMMON, mage.cards.r.RakdosCarnarium.class)); + cards.add(new SetCardInfo("Rampaging Baloths", 158, Rarity.RARE, mage.cards.r.RampagingBaloths.class)); + cards.add(new SetCardInfo("Ravenous Slime", 34, Rarity.RARE, mage.cards.r.RavenousSlime.class)); + cards.add(new SetCardInfo("Reality Scramble", 25, Rarity.RARE, mage.cards.r.RealityScramble.class)); + cards.add(new SetCardInfo("Reclamation Sage", 159, Rarity.UNCOMMON, mage.cards.r.ReclamationSage.class)); + cards.add(new SetCardInfo("Retreat to Hagra", 116, Rarity.UNCOMMON, mage.cards.r.RetreatToHagra.class)); cards.add(new SetCardInfo("Retrofitter Foundry", 57, Rarity.RARE, mage.cards.r.RetrofitterFoundry.class)); + cards.add(new SetCardInfo("Return to Dust", 71, Rarity.UNCOMMON, mage.cards.r.ReturnToDust.class)); + cards.add(new SetCardInfo("Reverse Engineer", 99, Rarity.UNCOMMON, mage.cards.r.ReverseEngineer.class)); + cards.add(new SetCardInfo("Righteous Authority", 188, Rarity.RARE, mage.cards.r.RighteousAuthority.class)); + cards.add(new SetCardInfo("Rocky Tar Pit", 274, Rarity.UNCOMMON, mage.cards.r.RockyTarPit.class)); + cards.add(new SetCardInfo("Rubblehulk", 189, Rarity.RARE, mage.cards.r.Rubblehulk.class)); + cards.add(new SetCardInfo("Ruinous Path", 117, Rarity.RARE, mage.cards.r.RuinousPath.class)); + cards.add(new SetCardInfo("Sage's Reverie", 72, Rarity.UNCOMMON, mage.cards.s.SagesReverie.class)); + cards.add(new SetCardInfo("Saheeli's Artistry", 100, Rarity.RARE, mage.cards.s.SaheelisArtistry.class)); cards.add(new SetCardInfo("Saheeli's Directive", 26, Rarity.RARE, mage.cards.s.SaheelisDirective.class)); + cards.add(new SetCardInfo("Saheeli, the Gifted", 44, Rarity.MYTHIC, mage.cards.s.SaheeliTheGifted.class)); + cards.add(new SetCardInfo("Sakura-Tribe Elder", 160, Rarity.COMMON, mage.cards.s.SakuraTribeElder.class)); + cards.add(new SetCardInfo("Savage Lands", 275, Rarity.UNCOMMON, mage.cards.s.SavageLands.class)); + cards.add(new SetCardInfo("Savage Twister", 190, Rarity.UNCOMMON, mage.cards.s.SavageTwister.class)); + cards.add(new SetCardInfo("Scoured Barrens", 276, Rarity.COMMON, mage.cards.s.ScouredBarrens.class)); + cards.add(new SetCardInfo("Scrabbling Claws", 218, Rarity.UNCOMMON, mage.cards.s.ScrabblingClaws.class)); + cards.add(new SetCardInfo("Scute Mob", 161, Rarity.RARE, mage.cards.s.ScuteMob.class)); + cards.add(new SetCardInfo("Scuttling Doom Engine", 219, Rarity.RARE, mage.cards.s.ScuttlingDoomEngine.class)); + cards.add(new SetCardInfo("Seaside Citadel", 277, Rarity.UNCOMMON, mage.cards.s.SeasideCitadel.class)); + cards.add(new SetCardInfo("Seat of the Synod", 278, Rarity.COMMON, mage.cards.s.SeatOfTheSynod.class)); + cards.add(new SetCardInfo("Secluded Steppe", 279, Rarity.COMMON, mage.cards.s.SecludedSteppe.class)); + cards.add(new SetCardInfo("Seer's Lantern", 220, Rarity.COMMON, mage.cards.s.SeersLantern.class)); + cards.add(new SetCardInfo("Seer's Sundial", 221, Rarity.RARE, mage.cards.s.SeersSundial.class)); + cards.add(new SetCardInfo("Sejiri Refuge", 280, Rarity.UNCOMMON, mage.cards.s.SejiriRefuge.class)); + cards.add(new SetCardInfo("Selesnya Sanctuary", 281, Rarity.COMMON, mage.cards.s.SelesnyaSanctuary.class)); + cards.add(new SetCardInfo("Serra Avatar", 73, Rarity.MYTHIC, mage.cards.s.SerraAvatar.class)); + cards.add(new SetCardInfo("Sharding Sphinx", 101, Rarity.RARE, mage.cards.s.ShardingSphinx.class)); + cards.add(new SetCardInfo("Sigil of the Empty Throne", 74, Rarity.RARE, mage.cards.s.SigilOfTheEmptyThrone.class)); + cards.add(new SetCardInfo("Sigiled Starfish", 102, Rarity.COMMON, mage.cards.s.SigiledStarfish.class)); + cards.add(new SetCardInfo("Silent Sentinel", 75, Rarity.RARE, mage.cards.s.SilentSentinel.class)); + cards.add(new SetCardInfo("Silent-Blade Oni", 191, Rarity.RARE, mage.cards.s.SilentBladeOni.class)); + cards.add(new SetCardInfo("Simic Growth Chamber", 282, Rarity.UNCOMMON, mage.cards.s.SimicGrowthChamber.class)); + cards.add(new SetCardInfo("Skull Storm", 18, Rarity.RARE, mage.cards.s.SkullStorm.class)); + cards.add(new SetCardInfo("Snake Umbra", 162, Rarity.COMMON, mage.cards.s.SnakeUmbra.class)); + cards.add(new SetCardInfo("Sol Ring", 222, Rarity.UNCOMMON, mage.cards.s.SolRing.class)); + cards.add(new SetCardInfo("Soul Snare", 76, Rarity.UNCOMMON, mage.cards.s.SoulSnare.class)); + cards.add(new SetCardInfo("Soul of Innistrad", 118, Rarity.MYTHIC, mage.cards.s.SoulOfInnistrad.class)); + cards.add(new SetCardInfo("Soul of New Phyrexia", 223, Rarity.MYTHIC, mage.cards.s.SoulOfNewPhyrexia.class)); + cards.add(new SetCardInfo("Sower of Discord", 19, Rarity.RARE, mage.cards.s.SowerOfDiscord.class)); + cards.add(new SetCardInfo("Spawning Grounds", 163, Rarity.RARE, mage.cards.s.SpawningGrounds.class)); + cards.add(new SetCardInfo("Sphinx of Jwar Isle", 103, Rarity.RARE, mage.cards.s.SphinxOfJwarIsle.class)); + cards.add(new SetCardInfo("Sphinx of Uthuun", 104, Rarity.RARE, mage.cards.s.SphinxOfUthuun.class)); + cards.add(new SetCardInfo("Steel Hellkite", 224, Rarity.RARE, mage.cards.s.SteelHellkite.class)); + cards.add(new SetCardInfo("Stitch Together", 119, Rarity.UNCOMMON, mage.cards.s.StitchTogether.class)); + cards.add(new SetCardInfo("Submerged Boneyard", 283, Rarity.UNCOMMON, mage.cards.s.SubmergedBoneyard.class)); + cards.add(new SetCardInfo("Swamp", 299, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 300, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 301, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swiftfoot Boots", 225, Rarity.UNCOMMON, mage.cards.s.SwiftfootBoots.class)); + cards.add(new SetCardInfo("Swiftwater Cliffs", 284, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class)); cards.add(new SetCardInfo("Tawnos, Urza's Apprentice", 45, Rarity.MYTHIC, mage.cards.t.TawnosUrzasApprentice.class)); + cards.add(new SetCardInfo("Telling Time", 105, Rarity.COMMON, mage.cards.t.TellingTime.class)); + cards.add(new SetCardInfo("Temple of the False God", 285, Rarity.UNCOMMON, mage.cards.t.TempleOfTheFalseGod.class)); + cards.add(new SetCardInfo("Terminus", 77, Rarity.RARE, mage.cards.t.Terminus.class)); + cards.add(new SetCardInfo("Terramorphic Expanse", 286, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class)); + cards.add(new SetCardInfo("Thantis the Warweaver", 46, Rarity.MYTHIC, mage.cards.t.ThantisTheWarweaver.class)); + cards.add(new SetCardInfo("Thirst for Knowledge", 106, Rarity.UNCOMMON, mage.cards.t.ThirstForKnowledge.class)); + cards.add(new SetCardInfo("Thopter Assembly", 226, Rarity.RARE, mage.cards.t.ThopterAssembly.class)); + cards.add(new SetCardInfo("Thopter Engineer", 126, Rarity.UNCOMMON, mage.cards.t.ThopterEngineer.class)); cards.add(new SetCardInfo("Thopter Spy Network", 107, Rarity.RARE, mage.cards.t.ThopterSpyNetwork.class)); + cards.add(new SetCardInfo("Thornwood Falls", 287, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class)); + cards.add(new SetCardInfo("Tidings", 108, Rarity.UNCOMMON, mage.cards.t.Tidings.class)); + cards.add(new SetCardInfo("Tranquil Cove", 288, Rarity.COMMON, mage.cards.t.TranquilCove.class)); + cards.add(new SetCardInfo("Tranquil Expanse", 289, Rarity.UNCOMMON, mage.cards.t.TranquilExpanse.class)); + cards.add(new SetCardInfo("Tranquil Thicket", 290, Rarity.COMMON, mage.cards.t.TranquilThicket.class)); + cards.add(new SetCardInfo("Treasure Hunt", 109, Rarity.COMMON, mage.cards.t.TreasureHunt.class)); + cards.add(new SetCardInfo("Treasure Nabber", 27, Rarity.RARE, mage.cards.t.TreasureNabber.class)); + cards.add(new SetCardInfo("Turntimber Sower", 35, Rarity.RARE, mage.cards.t.TurntimberSower.class)); + cards.add(new SetCardInfo("Tuvasa the Sunlit", 47, Rarity.MYTHIC, mage.cards.t.TuvasaTheSunlit.class)); + cards.add(new SetCardInfo("Unflinching Courage", 192, Rarity.UNCOMMON, mage.cards.u.UnflinchingCourage.class)); + cards.add(new SetCardInfo("Unquestioned Authority", 78, Rarity.UNCOMMON, mage.cards.u.UnquestionedAuthority.class)); + cards.add(new SetCardInfo("Unstable Obelisk", 227, Rarity.UNCOMMON, mage.cards.u.UnstableObelisk.class)); + cards.add(new SetCardInfo("Unwinding Clock", 228, Rarity.RARE, mage.cards.u.UnwindingClock.class)); + cards.add(new SetCardInfo("Utter End", 193, Rarity.RARE, mage.cards.u.UtterEnd.class)); + cards.add(new SetCardInfo("Varchild, Betrayer of Kjeldor", 28, Rarity.RARE, mage.cards.v.VarchildBetrayerOfKjeldor.class)); + cards.add(new SetCardInfo("Varina, Lich Queen", 48, Rarity.MYTHIC, mage.cards.v.VarinaLichQueen.class)); + cards.add(new SetCardInfo("Vedalken Humiliator", 13, Rarity.RARE, mage.cards.v.VedalkenHumiliator.class)); + cards.add(new SetCardInfo("Vessel of Endless Rest", 229, Rarity.UNCOMMON, mage.cards.v.VesselOfEndlessRest.class)); + cards.add(new SetCardInfo("Vow of Flight", 110, Rarity.UNCOMMON, mage.cards.v.VowOfFlight.class)); + cards.add(new SetCardInfo("Vow of Wildness", 164, Rarity.UNCOMMON, mage.cards.v.VowOfWildness.class)); + cards.add(new SetCardInfo("Warped Landscape", 291, Rarity.COMMON, mage.cards.w.WarpedLandscape.class)); + cards.add(new SetCardInfo("Whiptongue Hydra", 36, Rarity.RARE, mage.cards.w.WhiptongueHydra.class)); + cards.add(new SetCardInfo("Whirler Rogue", 111, Rarity.UNCOMMON, mage.cards.w.WhirlerRogue.class)); + cards.add(new SetCardInfo("Whitewater Naiads", 112, Rarity.UNCOMMON, mage.cards.w.WhitewaterNaiads.class)); + cards.add(new SetCardInfo("Wild Growth", 165, Rarity.COMMON, mage.cards.w.WildGrowth.class)); + cards.add(new SetCardInfo("Windgrace's Judgment", 49, Rarity.RARE, mage.cards.w.WindgracesJudgment.class)); + cards.add(new SetCardInfo("Winds of Rath", 79, Rarity.RARE, mage.cards.w.WindsOfRath.class)); + cards.add(new SetCardInfo("Woodland Stream", 292, Rarity.COMMON, mage.cards.w.WoodlandStream.class)); + cards.add(new SetCardInfo("Worm Harvest", 194, Rarity.RARE, mage.cards.w.WormHarvest.class)); + cards.add(new SetCardInfo("Worn Powerstone", 230, Rarity.UNCOMMON, mage.cards.w.WornPowerstone.class)); + cards.add(new SetCardInfo("Yavimaya Elder", 166, Rarity.COMMON, mage.cards.y.YavimayaElder.class)); + cards.add(new SetCardInfo("Yavimaya Enchantress", 167, Rarity.COMMON, mage.cards.y.YavimayaEnchantress.class)); + cards.add(new SetCardInfo("Yennet, Crypt Sovereign", 51, Rarity.MYTHIC, mage.cards.y.YennetCryptSovereign.class)); + cards.add(new SetCardInfo("Zendikar Incarnate", 195, Rarity.UNCOMMON, mage.cards.z.ZendikarIncarnate.class)); } } diff --git a/Mage.Sets/src/mage/sets/Dominaria.java b/Mage.Sets/src/mage/sets/Dominaria.java index 6e48f762fed..98d533f1954 100644 --- a/Mage.Sets/src/mage/sets/Dominaria.java +++ b/Mage.Sets/src/mage/sets/Dominaria.java @@ -26,7 +26,7 @@ public final class Dominaria extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; - this.needsLegends = true; + this.needsLegendCreature = true; this.maxCardNumberInBooster = 269; cards.add(new SetCardInfo("Academy Drake", 40, Rarity.COMMON, mage.cards.a.AcademyDrake.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java index 43d8fb89d3a..d37caa01a5f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java @@ -23,7 +23,7 @@ public class MetalcraftTest extends CardTestPlayerBase { public void testMetalcraftFromBlinkmoth() { addCard(Zone.BATTLEFIELD, playerA, "Darksteel Citadel",1); - // Metalcraft - {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts + // Metalcraft — {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts addCard(Zone.BATTLEFIELD, playerA, "Rusted Relic", 1); // {T}: Add {C}to your mana pool. diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NaturesWillTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NaturesWillTest.java new file mode 100644 index 00000000000..2c8d49e50ae --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NaturesWillTest.java @@ -0,0 +1,67 @@ +package org.mage.test.cards.abilities.other; + +import mage.constants.MultiplayerAttackOption; +import mage.constants.PhaseStep; +import mage.constants.RangeOfInfluence; +import mage.constants.Zone; +import mage.game.FreeForAll; +import mage.game.Game; +import mage.game.GameException; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +import java.io.FileNotFoundException; + +public class NaturesWillTest extends CardTestPlayerBase { + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, 0, 20); + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + playerC = createPlayer(game, playerC, "PlayerC"); + return game; + } + + + @Test + public void testAttackMultiplePlayers() { + addCard(Zone.HAND, playerA, "Nature's Will"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + addCard(Zone.BATTLEFIELD, playerA, "Suntail Hawk"); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerC, "Island", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nature's Will"); + attack(1, playerA, "Grizzly Bears", playerB); + attack(1, playerA, "Suntail Hawk", playerC); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTappedCount("Forest", false, 4); + assertTappedCount("Mountain", true, 4); + assertTappedCount("Island", true, 4); + } + + @Test + public void testAttackOnePlayer() { + addCard(Zone.HAND, playerA, "Nature's Will"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerC, "Island", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nature's Will"); + attack(1, playerA, "Grizzly Bears", playerB); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTappedCount("Forest", false, 4); + assertTappedCount("Mountain", true, 4); + assertTappedCount("Island", false, 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/StormTheVaultTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/StormTheVaultTest.java new file mode 100644 index 00000000000..75f279af428 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/StormTheVaultTest.java @@ -0,0 +1,59 @@ +package org.mage.test.cards.abilities.other; + +import mage.constants.MultiplayerAttackOption; +import mage.constants.PhaseStep; +import mage.constants.RangeOfInfluence; +import mage.constants.Zone; +import mage.game.FreeForAll; +import mage.game.Game; +import mage.game.GameException; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +import java.io.FileNotFoundException; + +public class StormTheVaultTest extends CardTestPlayerBase { + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, 0, 20); + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + playerC = createPlayer(game, playerC, "PlayerC"); + return game; + } + + + @Test + public void testAttackMultiplePlayers() { + addCard(Zone.BATTLEFIELD, playerA, "Storm the Vault"); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + addCard(Zone.BATTLEFIELD, playerA, "Nessian Courser"); + addCard(Zone.BATTLEFIELD, playerA, "Suntail Hawk"); + addCard(Zone.BATTLEFIELD, playerA, "Lantern Kami"); + + attack(1, playerA, "Grizzly Bears", playerB); + attack(1, playerA, "Nessian Courser", playerB); + attack(1, playerA, "Suntail Hawk", playerC); + attack(1, playerA, "Lantern Kami", playerC); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Treasure", 2); + } + + @Test + public void testAttackOnePlayer() { + addCard(Zone.BATTLEFIELD, playerA, "Storm the Vault"); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + addCard(Zone.BATTLEFIELD, playerA, "Suntail Hawk"); + + attack(1, playerA, "Grizzly Bears", playerB); + attack(1, playerA, "Suntail Hawk", playerB); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Treasure", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java index 4f3e24dc01f..1cb16170ab4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java @@ -191,4 +191,34 @@ public class CleverImpersonatorTest extends CardTestPlayerBase { assertType(dReflection, CardType.ENCHANTMENT, true); } + @Test + public void testKindredDiscovery() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + addCard(Zone.HAND, playerA, "Kindred Discovery"); + + + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + // Skip your draw step. + addCard(Zone.BATTLEFIELD, playerB, "Dragon Appeasement"); + addCard(Zone.HAND, playerB, "Clever Impersonator"); + addCard(Zone.HAND, playerB, "Ornithopter", 2); + addCard(Zone.HAND, playerB, "Memnite"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kindred Discovery"); + setChoice(playerA, "Construct"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Clever Impersonator"); + setChoice(playerB, "Kindred Discovery"); + setChoice(playerB, "Thopter"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Ornithopter"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Ornithopter"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Memnite"); + + setStopAt(2, PhaseStep.END_COMBAT); + execute(); + + assertHandCount(playerB, 2); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java index 2bb567fbbd5..202b360e987 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java @@ -4,6 +4,7 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectsList; import mage.cards.Card; import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.StaticFilters; @@ -89,7 +90,7 @@ public class CloneTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Llanowar Elves"); addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Public Executio", "Llanowar Elves"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Public Execution", "Llanowar Elves"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Clone"); setStopAt(1, PhaseStep.END_TURN); @@ -199,4 +200,32 @@ public class CloneTest extends CardTestPlayerBase { Assert.assertTrue("There should be a white and a blue Silvercoat Lion be on the battlefield", blueLion && whiteLion); } + @Test + public void testAdaptiveAutomaton() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.HAND, playerA, "Adaptive Automaton"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 4); + addCard(Zone.HAND, playerB, "Clone"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Adaptive Automaton"); + setChoice(playerA, "Elf"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Clone"); + setChoice(playerB, "Adaptive Automaton"); + setChoice(playerB, "Goblin"); + + setStopAt(2, PhaseStep.END_COMBAT); + execute(); + + assertPermanentCount(playerA, "Adaptive Automaton", 1); + Permanent original = getPermanent("Adaptive Automaton", playerA); + Assert.assertTrue("The original Adaptive Automaton should be an Elf", original.hasSubtype(SubType.ELF, currentGame)); + + assertPermanentCount(playerB, "Adaptive Automaton", 1); + Permanent clone = getPermanent("Adaptive Automaton", playerB); + Assert.assertFalse("The cloned Adaptive Automaton should not be as Elf", clone.hasSubtype(SubType.ELF, currentGame)); + Assert.assertTrue("The cloned Adaptive Automaton should be a Goblin", clone.hasSubtype(SubType.GOBLIN, currentGame)); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/CantCastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/CantCastTest.java index 21e26f9570c..7fb74ea1ada 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/rules/CantCastTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/CantCastTest.java @@ -127,7 +127,7 @@ public class CantCastTest extends CardTestPlayerBase { // Your opponents can't block with creatures with even converted mana costs. addCard(Zone.BATTLEFIELD, playerB, "Void Winnower"); - // Metalcraft - {T}: Add one mana of any color. Activate this ability only if you control three or more artifacts. + // Metalcraft — {T}: Add one mana of any color. Activate this ability only if you control three or more artifacts. addCard(Zone.HAND, playerA, "Mox Opal", 1); // {0} castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mox Opal"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/AbilityOwnershipTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/AbilityOwnershipTest.java new file mode 100644 index 00000000000..e1970fa1919 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/AbilityOwnershipTest.java @@ -0,0 +1,56 @@ +package org.mage.test.cards.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AbilityOwnershipTest extends CardTestPlayerBase { + + @Test + public void testOwned() { + addCard(Zone.GRAVEYARD, playerB, "Soul Snuffers"); + addCard(Zone.GRAVEYARD, playerB, "Minister of Pain"); + + addCard(Zone.HAND, playerA, "Rise of the Dark Realms"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 9); + addCard(Zone.BATTLEFIELD, playerA, "Obelisk Spider"); + + setLife(playerA, 20); + setLife(playerB, 20); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rise of the Dark Realms"); + setChoice(playerA, "Yes"); + addTarget(playerA, "Soul Snuffers"); // sacrifice to Exploit + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + // Obelisk Spider Triggers twice once for the counter on Obelisk Spider. Once for the counter on Minister of Pain. + assertLife(playerA, 22); + assertLife(playerB, 18); + } + + @Test + public void testToGraveyard() { + addCard(Zone.GRAVEYARD, playerB, "Soul Snuffers"); + addCard(Zone.GRAVEYARD, playerB, "Minister of Pain"); + addCard(Zone.BATTLEFIELD, playerB, "Obelisk Spider"); + + addCard(Zone.HAND, playerA, "Rise of the Dark Realms"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 9); + + setLife(playerA, 20); + setLife(playerB, 20); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rise of the Dark Realms"); + setChoice(playerA, "Yes"); + addTarget(playerA, "Soul Snuffers"); // sacrifice to Exploit + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java b/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java index b6da9a2d9e5..85f8da3236c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java @@ -9,10 +9,8 @@ import mage.cards.repository.CardInfo; import mage.cards.repository.CardScanner; import mage.constants.CardType; import mage.constants.Rarity; -import mage.sets.CoreSet2019; -import mage.sets.FateReforged; -import mage.sets.MastersEditionII; -import mage.sets.MastersEditionIV; +import mage.sets.*; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -115,6 +113,15 @@ public class BoosterGenerationTest extends MageTestBase { assertTrue(allCards.stream().anyMatch(card -> card.getCardType().contains(CardType.LAND) && card.getRarity().equals(Rarity.COMMON))); } + @Test + public void testDominaria_EveryBoosterContainsLegendaryCreature() { + for (int i = 0; i < 10; i++) { + List booster = Dominaria.getInstance().createBooster(); + // check that booster contains legendary creature + assertTrue(booster.stream().anyMatch(card -> card.isCreature() && card.isLegendary())); + } + } + private static String str(List cards) { StringBuilder sb = new StringBuilder("["); Iterator iterator = cards.iterator(); diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java index 3b47bc1c16f..c290a39cc2e 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common; import java.util.UUID; @@ -102,7 +101,10 @@ public class AttacksAllTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a " + filter.getMessage() + " attacks" + (attacksYouOrYourPlaneswalker ? " you or a planeswalker you control" : "") + ", " + super.getRule(); + return "Whenever " + (filter.getMessage().startsWith("an") ? "" : "a ") + + filter.getMessage() + " attacks" + + (attacksYouOrYourPlaneswalker ? " you or a planeswalker you control" : "") + + ", " + super.getRule(); } } diff --git a/Mage/src/main/java/mage/abilities/common/ControlledCreaturesDealCombatDamagePlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ControlledCreaturesDealCombatDamagePlayerTriggeredAbility.java index 71c0d50bde7..9aa3611a459 100644 --- a/Mage/src/main/java/mage/abilities/common/ControlledCreaturesDealCombatDamagePlayerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ControlledCreaturesDealCombatDamagePlayerTriggeredAbility.java @@ -4,6 +4,7 @@ package mage.abilities.common; import java.util.HashSet; import java.util.Set; import java.util.UUID; + import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; @@ -11,8 +12,8 @@ import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; /** * @@ -20,22 +21,25 @@ import mage.game.permanent.Permanent; */ public class ControlledCreaturesDealCombatDamagePlayerTriggeredAbility extends TriggeredAbilityImpl { - private boolean madeDamage = false; private Set damagedPlayerIds = new HashSet<>(); + private boolean setTargetPointer; public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Effect effect) { this(Zone.BATTLEFIELD, effect); } public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Zone zone, Effect effect) { + this(zone, effect, false); + } + + public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Zone zone, Effect effect, boolean setTargetPointer) { super(zone, effect, false); + this.setTargetPointer = setTargetPointer; } public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(final ControlledCreaturesDealCombatDamagePlayerTriggeredAbility ability) { super(ability); - this.madeDamage = ability.madeDamage; this.damagedPlayerIds = new HashSet<>(); - this.damagedPlayerIds.addAll(ability.damagedPlayerIds); } @Override @@ -55,28 +59,19 @@ public class ControlledCreaturesDealCombatDamagePlayerTriggeredAbility extends T if (event.getType() == EventType.DAMAGED_PLAYER) { DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; Permanent p = game.getPermanent(event.getSourceId()); - if (damageEvent.isCombatDamage() && p != null && p.isControlledBy(this.getControllerId())) { - madeDamage = true; + if (damageEvent.isCombatDamage() && p != null && p.isControlledBy(this.getControllerId()) && !damagedPlayerIds.contains(event.getPlayerId())) { damagedPlayerIds.add(event.getPlayerId()); - } - } - if (event.getType() == EventType.COMBAT_DAMAGE_STEP_PRIORITY) { - if (madeDamage) { - Set damagedPlayersCopy = new HashSet<>(); - damagedPlayersCopy.addAll(damagedPlayerIds); - for (Effect effect : this.getEffects()) { - effect.setValue("damagedPlayers", damagedPlayersCopy); + if (setTargetPointer) { + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getPlayerId())); + } } - damagedPlayerIds.clear(); - madeDamage = false; return true; } } - if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(getSourceId())) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.GRAVEYARD) { - damagedPlayerIds.clear(); - } + if (event.getType() == EventType.COMBAT_DAMAGE_STEP_PRIORITY || + (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(getSourceId()))) { + damagedPlayerIds.clear(); } return false; } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaSpentToCastCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaSpentToCastCount.java index d2e841f7206..767c185e7a6 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaSpentToCastCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaSpentToCastCount.java @@ -29,7 +29,7 @@ public class ManaSpentToCastCount implements DynamicValue { } if (spell != null) { // NOT the cmc of the spell on the stack - return spell.getSpellAbility().getManaCostsToPay().convertedManaCost() + spell.getSpellAbility().getManaCostsToPay().getX(); + return spell.getSpellAbility().getManaCostsToPay().convertedManaCost(); } return 0; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index 40936884ef9..092c8ec92cb 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import java.util.ArrayList; @@ -8,6 +7,8 @@ import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.Mode; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; @@ -17,6 +18,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.SuperType; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.EmptyToken; @@ -124,14 +126,6 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { this.isntLegendary = effect.isntLegendary; } - public void setBecomesArtifact(boolean becomesArtifact) { - this.becomesArtifact = becomesArtifact; - } - - public void setIsntLegendary(boolean isntLegendary) { - this.isntLegendary = isntLegendary; - } - @Override public boolean apply(Game game, Ability source) { UUID targetId; @@ -282,4 +276,33 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { public void setUseLKI(boolean useLKI) { this.useLKI = useLKI; } + + public void setBecomesArtifact(boolean becomesArtifact) { + this.becomesArtifact = becomesArtifact; + } + + public void setIsntLegendary(boolean isntLegendary) { + this.isntLegendary = isntLegendary; + } + + public void setHasHaste(boolean hasHaste) { + this.hasHaste = hasHaste; + } + + public void exileTokensCreatedAtNextEndStep(Game game, Ability source) { + for (Permanent tokenPermanent : addedTokenPermanents) { + ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); + exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source); + } + } + + public void exileTokensCreatedAtEndOfCombat(Game game, Ability source) { + for (Permanent tokenPermanent : addedTokenPermanents) { + + ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); + exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); + game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source); + } + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddChosenSubtypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddChosenSubtypeEffect.java new file mode 100644 index 00000000000..bb86dce4a5d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddChosenSubtypeEffect.java @@ -0,0 +1,37 @@ +package mage.abilities.effects.common.continuous; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.ChooseCreatureTypeEffect; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; + +public class AddChosenSubtypeEffect extends ContinuousEffectImpl { + + public AddChosenSubtypeEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); + staticText = "{this} is the chosen type in addition to its other types"; + } + + public AddChosenSubtypeEffect(final AddChosenSubtypeEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + SubType subType = ChooseCreatureTypeEffect.getChoosenCreatureType(permanent.getId(), game); + if (subType != null && !permanent.hasSubtype(subType, game)) { + permanent.getSubtype(game).add(subType); + } + } + return true; + } + + @Override + public AddChosenSubtypeEffect copy() { + return new AddChosenSubtypeEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessEnchantedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessEnchantedEffect.java index e33d30a36da..a5f1cf72e30 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessEnchantedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessEnchantedEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common.continuous; import mage.abilities.Ability; @@ -16,13 +15,24 @@ import mage.game.permanent.Permanent; */ public class SetPowerToughnessEnchantedEffect extends ContinuousEffectImpl { + private final int power; + private final int toughness; + public SetPowerToughnessEnchantedEffect() { + this(0, 2); + } + + public SetPowerToughnessEnchantedEffect(int power, int toughness) { super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.BoostCreature); - staticText = "Enchanted creature has base power and toughness 0/2"; + staticText = "Enchanted creature has base power and toughness " + power + "/" + toughness; + this.power = power; + this.toughness = toughness; } public SetPowerToughnessEnchantedEffect(final SetPowerToughnessEnchantedEffect effect) { super(effect); + this.power = effect.power; + this.toughness = effect.toughness; } @Override @@ -36,8 +46,8 @@ public class SetPowerToughnessEnchantedEffect extends ContinuousEffectImpl { if (enchantment != null && enchantment.getAttachedTo() != null) { Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); if (enchanted != null) { - enchanted.getPower().setValue(0); - enchanted.getToughness().setValue(2); + enchanted.getPower().setValue(power); + enchanted.getToughness().setValue(toughness); } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java index edf8f95a1ed..b70c8bb1815 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java @@ -11,6 +11,8 @@ import mage.game.permanent.Permanent; /** * + * IMPORTANT: This only adds the chosen subtype while the source permanent is entering the battlefield. + * You should also use @link{mage.abilities.effects.common.continuous.AddChosenSubtypeEffect} to make the subtype persist. * @author LevelX2 */ public class EnterAttributeAddChosenSubtypeEffect extends OneShotEffect { @@ -34,12 +36,8 @@ public class EnterAttributeAddChosenSubtypeEffect extends OneShotEffect { Permanent permanent = game.getPermanentEntering(source.getSourceId()); SubType subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type"); if (permanent != null && subtype != null) { - MageObject mageObject = permanent.getBasicMageObject(game); - if (!mageObject.getSubtype(null).contains(subtype)) { - mageObject.getSubtype(null).add(subtype); - } - if (!permanent.getSubtype(null).contains(subtype)) { - permanent.getSubtype(null).add(subtype); + if (!permanent.getSubtype(game).contains(subtype)) { + permanent.getSubtype(game).add(subtype); } return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java b/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java new file mode 100644 index 00000000000..a40d3de2f53 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java @@ -0,0 +1,107 @@ +package mage.abilities.keyword; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.players.Player; + +/** + * + * @author Plopman + */ +public class CommanderStormAbility extends TriggeredAbilityImpl { + + public CommanderStormAbility() { + super(Zone.STACK, new CommanderStormEffect()); + this.ruleAtTheTop = true; + } + + private CommanderStormAbility(final CommanderStormAbility ability) { + super(ability); + } + + @Override + public CommanderStormAbility copy() { + return new CommanderStormAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getSourceId().equals(getSourceId())) { + StackObject spell = game.getStack().getStackObject(getSourceId()); + if (spell instanceof Spell) { + for (Effect effect : this.getEffects()) { + effect.setValue("StormSpell", spell); + effect.setValue("StormSpellRef", new MageObjectReference(spell.getId(), game)); + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "When you cast this spell, copy it for each time you've " + + "cast your commander from the command zone this game. " + + "You may choose new targets for the copies."; + } +} + +class CommanderStormEffect extends OneShotEffect { + + public CommanderStormEffect() { + super(Outcome.Copy); + } + + public CommanderStormEffect(final CommanderStormEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObjectReference spellRef = (MageObjectReference) this.getValue("StormSpellRef"); + if (spellRef == null) { + return false; + } + int stormCount = 0; + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + stormCount = player.getCommandersIds().stream().map( + (commanderId) -> (Integer) game.getState().getValue(commanderId + "_castCount") + ).map((castCount) -> castCount).reduce(stormCount, Integer::sum); + if (stormCount == 0) { + return true; + } + Spell spell = (Spell) this.getValue("StormSpell"); + if (spell == null) { + return false; + } + game.informPlayers(spell.getLogName() + " will be copied " + stormCount + " time" + (stormCount > 1 ? "s" : "")); + for (int i = 0; i < stormCount; i++) { + spell.createCopyOnStack(game, source, source.getControllerId(), true); + } + return true; + } + + @Override + public CommanderStormEffect copy() { + return new CommanderStormEffect(this); + } +} diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index c682915fd5e..dd77793fc55 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -779,7 +779,15 @@ public abstract class CardImpl extends MageObjectImpl implements Card { @Override public boolean addCounters(Counter counter, Ability source, Game game, List appliedEffects, boolean isEffect) { boolean returnCode = true; - UUID sourceId = (source == null ? getId() : source.getSourceId()); + UUID sourceId = getId(); + if (source != null) { + MageObject object = game.getObject(source.getId()); + if (object instanceof StackObject) { + sourceId = source.getId(); + } else { + sourceId = source.getSourceId(); + } + } GameEvent countersEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTERS, objectId, sourceId, getControllerOrOwner(), counter.getName(), counter.getCount()); countersEvent.setAppliedEffects(appliedEffects); countersEvent.setFlag(isEffect); diff --git a/Mage/src/main/java/mage/cards/ExpansionSet.java b/Mage/src/main/java/mage/cards/ExpansionSet.java index 2e0750d00e1..f58e7c41c98 100644 --- a/Mage/src/main/java/mage/cards/ExpansionSet.java +++ b/Mage/src/main/java/mage/cards/ExpansionSet.java @@ -1,6 +1,7 @@ package mage.cards; +import mage.ObjectColor; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; @@ -96,7 +97,11 @@ public abstract class ExpansionSet implements Serializable { protected int numBoosterRare; protected int numBoosterDoubleFaced; // -1 = include normally 0 = exclude 1-n = include explicit protected int ratioBoosterMythic; - protected boolean needsLegends = false; + + protected boolean needsLegendCreature = false; + protected boolean validateBoosterColors = true; + protected double rejectMissingColorProbability = 0.8; + protected double rejectSameColorUncommonsProbability = 0.8; protected int maxCardNumberInBooster; // used to omit cards with collector numbers beyond the regular cards in a set for boosters @@ -186,17 +191,83 @@ public abstract class ExpansionSet implements Serializable { } public List createBooster() { - if (needsLegends) { - for (int i = 0; i < 100000; i++) {//don't want to somehow loop forever - List booster = tryBooster(); - for (Card card : booster) { - if (card.isLegendary() && card.isCreature()) {// Dominaria packs must contain at least one legendary creature. - return booster; + for (int i = 0; i < 100; i++) {//don't want to somehow loop forever + List booster = tryBooster(); + if (boosterIsValid(booster)) { + return booster; + } + } + return tryBooster(); + } + + protected boolean boosterIsValid(List booster) { + if (validateBoosterColors) { + if (!validateColors(booster)) { + return false; + } + } + + if (needsLegendCreature) { + if (booster.stream().noneMatch(card -> card.isLegendary() && card.isCreature())) { + return false; + } + } + + return true; + } + + protected boolean validateColors(List booster) { + List magicColors = + Arrays.asList(ObjectColor.WHITE, ObjectColor.BLUE, ObjectColor.BLACK, ObjectColor.RED, ObjectColor.GREEN); + + // all cards colors + Map colorWeight = new HashMap<>(); + // uncommon/rare/mythic cards colors + Map uncommonWeight = new HashMap<>(); + + for (ObjectColor color : magicColors) { + colorWeight.put(color, 0); + uncommonWeight.put(color, 0); + } + + // count colors in the booster + for (Card card : booster) { + ObjectColor cardColor = card.getColor(null); + if (cardColor != null) { + List colors = cardColor.getColors(); + // todo: do we need gold color? + colors.remove(ObjectColor.GOLD); + if (!colors.isEmpty()) { + // 60 - full card weight + // multicolored cards add part of the weight to each color + int cardColorWeight = 60 / colors.size(); + for (ObjectColor color : colors) { + colorWeight.put(color, colorWeight.get(color) + cardColorWeight); + if (card.getRarity() != Rarity.COMMON) { + uncommonWeight.put(color, uncommonWeight.get(color) + cardColorWeight); + } } } } } - return tryBooster(); + + // check that all colors are present + if (magicColors.stream().anyMatch(color -> colorWeight.get(color) < 60)) { + // reject only part of the boosters + if (RandomUtil.nextDouble() < rejectMissingColorProbability) { + return false; + } + } + + // check that we don't have 3 or more uncommons/rares of the same color + if (magicColors.stream().anyMatch(color -> uncommonWeight.get(color) >= 180)) { + // reject only part of the boosters + if (RandomUtil.nextDouble() < rejectSameColorUncommonsProbability) { + return false; + } + } + + return true; } public List tryBooster() { diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 9b8c203a031..fef2c75e130 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -1,12 +1,12 @@ package mage.constants; +import mage.util.SubTypeList; + import java.util.Arrays; import java.util.EnumSet; import java.util.Set; import java.util.stream.Collectors; -import mage.util.SubTypeList; - public enum SubType { //205.3k Instants and sorceries share their lists of subtypes; these subtypes are called spell types. @@ -367,6 +367,7 @@ public enum SubType { ZUBERA("Zubera", SubTypeSet.CreatureType), // Planeswalker AJANI("Ajani", SubTypeSet.PlaneswalkerType), + AMINATOU("Aminatou", SubTypeSet.PlaneswalkerType), ANGRATH("Angrath", SubTypeSet.PlaneswalkerType), ARLINN("Arlinn", SubTypeSet.PlaneswalkerType), ASHIOK("Ashiok", SubTypeSet.PlaneswalkerType), @@ -379,6 +380,7 @@ public enum SubType { DOOKU("Dooku", SubTypeSet.PlaneswalkerType, true), // Star Wars DOVIN("Dovin", SubTypeSet.PlaneswalkerType), ELSPETH("Elspeth", SubTypeSet.PlaneswalkerType), + ESTRID("Estrid", SubTypeSet.PlaneswalkerType), FREYALISE("Freyalise", SubTypeSet.PlaneswalkerType), GARRUK("Garruk", SubTypeSet.PlaneswalkerType), GIDEON("Gideon", SubTypeSet.PlaneswalkerType), @@ -410,6 +412,7 @@ public enum SubType { VIVIEN("Vivien", SubTypeSet.PlaneswalkerType), VRASKA("Vraska", SubTypeSet.PlaneswalkerType), WILL("Will", SubTypeSet.PlaneswalkerType), + WINDGRACE("Windgrace", SubTypeSet.PlaneswalkerType), XENAGOS("Xenagos", SubTypeSet.PlaneswalkerType), YANGGU("Yanggu", SubTypeSet.PlaneswalkerType), YANLING("Yanling", SubTypeSet.PlaneswalkerType), @@ -460,8 +463,6 @@ public enum SubType { return null; } - ; - public SubTypeSet getSubTypeSet() { return subTypeSet; } diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index 7b3dc6ae5fd..2ded9a796bc 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -106,6 +106,7 @@ public enum CounterType { SHIELD("shield"), SHRED("shred"), SLIME("slime"), + SLUMBER("slumber"), SOOT("soot"), SPITE("spite"), SPORE("spore"), diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/ChosenSubtypePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/ChosenSubtypePredicate.java index dd122607790..21a8c11bf8b 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/ChosenSubtypePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/ChosenSubtypePredicate.java @@ -1,29 +1,26 @@ package mage.filter.predicate.mageobject; -import java.util.UUID; import mage.MageObject; import mage.abilities.effects.common.ChooseCreatureTypeEffect; import mage.constants.SubType; -import mage.filter.predicate.Predicate; +import mage.filter.predicate.ObjectPlayerPredicate; +import mage.filter.predicate.ObjectSourcePlayer; import mage.game.Game; /** * * @author LoneFox */ -public class ChosenSubtypePredicate implements Predicate { +public class ChosenSubtypePredicate implements ObjectPlayerPredicate> { - private final UUID cardID; - - public ChosenSubtypePredicate(UUID cardID) { - this.cardID = cardID; + public ChosenSubtypePredicate() { } @Override - public boolean apply(MageObject input, Game game) { - SubType subType = ChooseCreatureTypeEffect.getChoosenCreatureType(cardID, game); - return input.hasSubtype(subType, game); + public boolean apply(ObjectSourcePlayer input, Game game) { + SubType subType = ChooseCreatureTypeEffect.getChoosenCreatureType(input.getSourceId(), game); + return input.getObject().hasSubtype(subType, game); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/BrudicladTelchorMyrToken.java b/Mage/src/main/java/mage/game/permanent/token/BrudicladTelchorMyrToken.java new file mode 100644 index 00000000000..33de29d06fa --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/BrudicladTelchorMyrToken.java @@ -0,0 +1,43 @@ +package mage.game.permanent.token; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +public final class BrudicladTelchorMyrToken extends TokenImpl { + + final static private List tokenImageSets = new ArrayList<>(); + + static { + tokenImageSets.addAll(Arrays.asList("C18")); + } + + public BrudicladTelchorMyrToken() { + this((String)null); + } + + public BrudicladTelchorMyrToken(String expansionSetCode) { + super("Myr", "2/1 blue Myr artifact creature token"); + this.setOriginalExpansionSetCode(expansionSetCode); + cardType.add(CardType.CREATURE); + cardType.add(CardType.ARTIFACT); + subtype.add(SubType.MYR); + color.setBlue(true); + power = new MageInt(2); + toughness = new MageInt(1); + + availableImageSetCodes = tokenImageSets; + } + + public BrudicladTelchorMyrToken(final BrudicladTelchorMyrToken token) { + super(token); + } + + public BrudicladTelchorMyrToken copy() { + return new BrudicladTelchorMyrToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/MaskToken.java b/Mage/src/main/java/mage/game/permanent/token/MaskToken.java new file mode 100644 index 00000000000..e133169b7ec --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/MaskToken.java @@ -0,0 +1,43 @@ +package mage.game.permanent.token; + +import mage.constants.CardType; +import mage.constants.SubType; +import mage.abilities.Ability; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.TotemArmorAbility; +import mage.constants.Outcome; +import mage.target.TargetPermanent; + +/** + * + * @author TheElk801 + */ +public final class MaskToken extends TokenImpl { + + public MaskToken() { + super( + "Mask", "white Aura enchantment token named Mask " + + "attached to another target permanent. " + + "The token has enchant permanent and totem armor." + ); + cardType.add(CardType.ENCHANTMENT); + color.setWhite(true); + subtype.add(SubType.AURA); + + TargetPermanent auraTarget = new TargetPermanent(); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + ability.addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(ability); + + this.addAbility(new TotemArmorAbility()); + } + + public MaskToken(final MaskToken token) { + super(token); + } + + public MaskToken copy() { + return new MaskToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java b/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java new file mode 100644 index 00000000000..e33ce893297 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java @@ -0,0 +1,45 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * + * @author spjspj + */ +public final class NestingDragonToken extends TokenImpl { + + public NestingDragonToken() { + super( + "Dragon Egg", + "0/2 red Dragon Egg creature token with defender and " + + "\"" + + "When this creature dies, " + + "create a 2/2 red Dragon creature token with flying and " + + "'{R}: This creature gets +1/+0 until end of turn.'" + + "\"" + ); + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add(SubType.DRAGON); + subtype.add(SubType.EGG); + power = new MageInt(0); + toughness = new MageInt(2); + addAbility(DefenderAbility.getInstance()); + this.addAbility(new DiesTriggeredAbility( + new CreateTokenEffect(new DragonEggDragonToken()), false + )); + } + + public NestingDragonToken(final NestingDragonToken token) { + super(token); + } + + public NestingDragonToken copy() { + return new NestingDragonToken(this); + } +} diff --git a/Mage/src/main/java/mage/util/RandomUtil.java b/Mage/src/main/java/mage/util/RandomUtil.java index aa08d609386..29307983419 100644 --- a/Mage/src/main/java/mage/util/RandomUtil.java +++ b/Mage/src/main/java/mage/util/RandomUtil.java @@ -26,4 +26,8 @@ public final class RandomUtil { public static boolean nextBoolean() { return ThreadLocalRandom.current().nextBoolean(); } + + public static double nextDouble() { + return ThreadLocalRandom.current().nextDouble(); + } } diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index f284f50f146..2266d8210e0 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -33925,23 +33925,313 @@ Cleansing Screech|Global Series: Jiang Yanggu & Mu Yanling|37|C|{4}{R}|Sorcery|| Timber Gorge|Global Series: Jiang Yanggu & Mu Yanling|38|C||Land|||Timber Gorge enters the battlefield tapped.${T}: Add {R} or {G}.| Mountain|Global Series: Jiang Yanggu & Mu Yanling|39|C||Basic Land - Mountain|||({T}: Add {R}.)| Forest|Global Series: Jiang Yanggu & Mu Yanling|40|C||Basic Land - Forest|||({T}: Add {G}.)| +Boreas Charger|Commander 2018|1|R|{2}{W}|Creature - Pegasus|2|1|Flying$When Boreas Charger leaves the battlefield, choose an opponent who controls more lands than you. Search your library for a number of Plains cards equal to the difference and reveal them. Put one of them onto the battlefield tapped and the rest into your hand. Then shuffle your library.| +Empyrial Storm|Commander 2018|2|R|{4}{W}{W}|Sorcery|||When you cast this spell, copy it for each time you've cast your commander from the command zone this game.$Create a 4/4 white Angel creature token with flying.| +Heavenly Blademaster|Commander 2018|3|R|{5}{W}|Creature - Angel|3|6|Flying, double strike$When Heavenly Blademaster enters the battlefield, you may attach any number of Auras and Equipment you control to it.$Other creatures you control get +1/+1 for each Aura and Equipment attached to Heavenly Blademaster.| +Loyal Unicorn|Commander 2018|4|U|{3}{W}|Creature - Unicorn|3|4|Vigilance$Lieutenant — At the beginning of combat on your turn, if you control your commander, prevent all combat damage that would be dealt to creatures you control this turn. Other creatures you control gain vigilance until end of turn.| +Magus of the Balance|Commander 2018|5|R|{1}{W}|Creature - Human Wizard|2|2|{4}{W}, {T}, Sacrifice Magus of the Balance: Each player chooses a number of lands they control equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players discard cards and sacrifice creatures the same way.| +Aminatou's Augury|Commander 2018|6|R|{6}{U}{U}|Sorcery|||Exile the top eight cards of your library. You may put a land card from among them onto the battlefield. Until end of turn, for each nonland card type, you may cast a card of that type from among the exiled cards without paying its mana cost.| Echo Storm|Commander 2018|7|R|{3}{U}{U}|Sorcery|||When you cast this spell, copy it for each time you've cast your commander from the command zone this game. You may choose new targets for the copies.$Create a token that's a copy of target artifact.| +Estrid's Invocation|Commander 2018|8|R|{2}{U}|Enchantment|||You may have Estrid's Invocation enter the battlefield as a copy of any enchantment you control, except it gains "At the beginning of your upkeep, you may exile this enchantment. If you do, return it to the battlefield under its owner's control."| +Ever-Watching Threshold|Commander 2018|9|R|{2}{U}|Enchantment|||Whenever an opponent attacks you and/or a planeswalker you control with one or more creatures, draw a card.| Loyal Drake|Commander 2018|10|U|{2}{U}|Creature - Drake|2|2|Flying$Lieutenant — At the beginning of combat on your turn, if you control your commander, draw a card.| +Octopus Umbra|Commander 2018|11|R|{3}{U}{U}|Enchantment - Aura|||Enchant creature$Enchanted creature has base power and toughness 8/8 and has "Whenever this creature attacks, you may tap target creature with power 8 or less."$Totem armor| +Primordial Mist|Commander 2018|12|R|{4}{U}|Enchantment|||At the beginning of your end step, you may manifest the top card of your library.| Vedalken Humiliator|Commander 2018|13|R|{3}{U}|Creature - Vedalken Wizard|3|4|Metalcraft — Whenever Vedalken Humiliator attacks, if you control three or more artifacts, creatures your opponents control lose all abilities and have base power and toughness 1/1 until end of turn.| +Bloodtracker|Commander 2018|14|R|{3}{B}|Creature - Vampire Wizard|2|2|Flying${B}, Pay 2 life: Put a +1/+1 counter on Bloodtracker.$When Bloodtracker leaves the battlefield, draw a card for each +1/+1 counter on it.| +Entreat the Dead|Commander 2018|15|R|{X}{X}{B}{B}{B}|Sorcery|||Return X target creature cards from your graveyard to the battlefield.$Miracle {X}{B}{B}| +Loyal Subordinate|Commander 2018|16|U|{2}{B}|Creature - Zombie|3|1|Menace$Lieutenant — At the beginning of combat on your turn, if you control your commander, each opponent loses 3 life.| +Night Incarnate|Commander 2018|17|R|{4}{B}|Creature - Elemental|3|4|Deathtouch$When Night Incarnate leaves the battlefield, all creatures get -3/-3 until end of turn.$Evoke {3}{B}| +Skull Storm|Commander 2018|18|R|{7}{B}{B}|Sorcery|||When you cast this spell, copy it for each time you've cast your commander from the command zone this game.$Each opponent sacrifices a creature. Each opponent who can't loses half their life, rounded up.| +Sower of Discord|Commander 2018|19|R|{4}{B}{B}|Creature - Demon|6|6|Flying$As Sower of Discord enters the battlefield, choose two players.$Whenever damage is dealt to one of the chosen players, the other chosen player also loses that much life.| +Emissary of Grudges|Commander 2018|20|R|{5}{R}|Creature - Efreet|6|5|Flying, haste$As Emissary of Grudges enters the battlefield, secretly choose an opponent.$Reveal the player you chose: Choose new targets for target spell or ability if it's controlled by the chosen player and if it targets you or a permanent you control. Activate this ability only once.| Enchanter's Bane|Commander 2018|21|R|{1}{R}|Enchantment|||At the beginning of your end step, target enchantment deals damage equal to its converted mana cost to its controller unless that player sacrifices it.| +Fury Storm|Commander 2018|22|R|{2}{R}{R}|Instant|||When you cast this spell, copy it for each time you've cast your commander from the command zone this game. You may choose new targets for the copies.$Copy target instant or sorcery spell. You may choose new targets for the copy.| +Loyal Apprentice|Commander 2018|23|U|{1}{R}|Creature - Human Artificer|2|1|Haste$Lieutenant — At the beginning of combat on your turn, if you control your commander, create a 1/1 colorless Thopter artifact creature token with flying. That token gains haste until end of turn.| +Nesting Dragon|Commander 2018|24|R|{3}{R}{R}|Creature - Dragon|5|4|Flying$Landfall — Whenever a land enters the battlefield under your control, create a 0/2 red Dragon Egg creature token with defender and "When this creature dies, create a 2/2 red Dragon creature token with flying and '{R}: This creature gets +1/+0 until end of turn.'"| +Reality Scramble|Commander 2018|25|R|{2}{R}{R}|Sorcery|||Put target permanent you own on the bottom of your library. Reveal cards from the top of your library until you reveal a card that shares a card type with that permanent. Put that card onto the battlefield and the rest on the bottom of your library in a random order.$Retrace| Saheeli's Directive|Commander 2018|26|R|{X}{R}{R}{R}|Sorcery|||Improvise$Reveal the top X cards of your library. You may put any number of artifact cards with converted mana cost X or less from among them onto the battlefield. Then put all cards revealed this way that weren't put onto the battlefield into your graveyard.| Treasure Nabber|Commander 2018|27|R|{2}{R}|Creature - Goblin Rogue|3|2|Whenever an opponent taps an artifact for mana, gain control of that artifact until the end of your next turn.| Varchild, Betrayer of Kjeldor|Commander 2018|28|R|{2}{R}|Legendary Creature - Human Knight|3|3|Whenever Varchild, Betrayer of Kjeldor deals combat damage to a player, that player creates that many 1/1 red Survivor creature tokens.$Survivors your opponents control can't block, and they can't attack you or a planeswalker you control.$When Varchild leaves the battlefield, gain control of all Survivors.| +Crash of Rhino Beetles|Commander 2018|29|R|{4}{G}|Creature - Insect|5|5|Trample$Crash of Rhino Beetles gets +10/+10 as long as you control ten or more lands.| +Genesis Storm|Commander 2018|30|R|{4}{G}{G}|Sorcery|||When you cast this spell, copy it for each time you've cast your commander from the command zone this game.$Reveal cards from the top of your library until you reveal a nonland permanent card. You may put that card onto the battlefield. Then put all cards revealed this way that weren't put onto the battlefield on the bottom of your library in a random order.| +Loyal Guardian|Commander 2018|31|U|{4}{G}|Creature - Rhino|4|4|Trample$Lieutenant — At the beginning of combat on your turn, if you control your commander, put a +1/+1 counter on each creature you control.| +Myth Unbound|Commander 2018|32|R|{2}{G}|Enchantment|||Your commander costs {1} less to cast for each time it's been cast from the command zone this game.$Whenever your commander is put into the command zone from anywhere, draw a card.| +Nylea's Colossus|Commander 2018|33|R|{6}{G}|Enchantment Creature - Giant|6|6|Constellation — Whenever Nylea's Colossus or another enchantment enters the battlefield under your control, double target creature's power and toughness until end of turn.| +Ravenous Slime|Commander 2018|34|R|{2}{G}|Creature - Ooze|1|1|Ravenous Slime can't be blocked by creatures with power 2 or less.$If a creature an opponent controls would die, instead exile it and put a number of +1/+1 counters equal to that creature's power on Ravenous Slime.| +Turntimber Sower|Commander 2018|35|R|{2}{G}|Creature - Elf Druid|3|3|Whenever one or more land cards are put into your graveyard from anywhere, create a 0/1 green Plant creature token.${G}, Sacrifice three creatures: Return target land card from your graveyard to your hand.| +Whiptongue Hydra|Commander 2018|36|R|{5}{G}|Creature - Lizard Hydra|4|4|Reach$When Whiptongue Hydra enters the battlefield, destroy all creatures with flying. Put a +1/+1 counter on Whiptongue Hydra for each creature destroyed this way.| +Aminatou, the Fateshifter|Commander 2018|37|M|{W}{U}{B}|Legendary Planeswalker - Aminatou|3|+1: Draw a card, then put a card from your hand on top of your library.$-1: Exile another target permanent you own, then return it to the battlefield under your control.$-6: Choose left or right. Each player gains control of all nonland permanents other than Aminatou, the Fateshifter controlled by the next player in the chosen direction.$Aminatou, the Fateshifter can be your commander.| +Arixmethes, Slumbering Isle|Commander 2018|38|R|{2}{G}{U}|Legendary Creature - Kraken|12|12|Arixmethes, Slumbering Isle enters the battlefield tapped with five slumber counters on it.$As long as Arixmethes has a slumber counter on it, it's a land.$Whenever you cast a spell, you may remove a slumber counter from Arixmethes.${T}: Add {G}{U}.| Brudiclad, Telchor Engineer|Commander 2018|39|M|{4}{U}{R}|Legendary Artifact Creature - Artificer|4|4|Creature tokens you control have haste.$At the beginning of combat on your turn, create a 2/1 blue Myr artifact creature token. Then you may choose a token you control. If you do, each other token you control becomes a copy of that token.| +Estrid, the Masked|Commander 2018|40|M|{1}{G}{W}{U}|Legendary Planeswalker - Estrid|3|+2: Untap each enchanted permanent you control.$-1: Create a white Aura enchantment token named Mask attached to another target permanent. The token has enchant permanent and totem armor.$-7: Put the top seven cards of your library into your graveyard. Return all non-Aura enchantment cards from your graveyard to the battlefield, then do the same for Aura cards.$Estrid, the Masked can be your commander.| +Gyrus, Waker of Corpses|Commander 2018|41|M|{X}{B}{R}{G}|Legendary Creature - Hydra|0|0|Gyrus, Walker of Corpses enters the battlefield with a number of +1/+1 counters on it equal to the amount of mana spent to cast it.$Whenever Gyrus attacks, you may exile target creature card with lesser power from your graveyard. If you do, create a token that's a copy of that card and that's tapped and attacking. Exile the token at the end of combat.| +Kestia, the Cultivator|Commander 2018|42|M|{1}{G}{W}{U}|Legendary Enchantment Creature - Nymph|4|4|Bestow {3}{G}{W}{U}$Enchanted creature gets +4/+4.$Whenever an enchanted creature or enchantment creature you control attacks, draw a card.| +Lord Windgrace|Commander 2018|43|M|{2}{B}{R}{G}|Legendary Planeswalker - Windgrace|5|+2: Discard a card, then draw a card. If a land card is discarded this way, draw an additional card.$-3: Return up to two target land cards from your graveyard to the battlefield.$-11: Destroy up to six target nonland permanents, then create six 2/2 green Cat Warrior creature tokens with forestwalk.$Lord Windgrace can be your commander.| Saheeli, the Gifted|Commander 2018|44|M|{2}{U}{R}|Legendary Planeswalker - Saheeli|4|+1: Create a 1/1 colorless Servo artifact creature token.$+1: The next spell you cast this turn costs {1} less to cast for each artifact you control as you cast it.$-7: For each artifact you control, create a token that's a copy of it. Those tokens gain haste. Exile those tokens at the beginning of the next end step.$Saheeli, the Gifted can be your commander.| Tawnos, Urza's Apprentice|Commander 2018|45|M|{U}{R}|Legendary Creature - Human Artificer|1|3|Haste${U}{R}, {T}: Copy target activated or triggered ability you control from an artifact source. You may choose new targets for the copy.| -Ancient Stone Idol|Commander 2018|53|R|{10}|Artifact Creature - Golem|12|12|Flash$This spell costs {1} less to cast for each attacking creature.$Trample$When Ancient Stone Idol dies, create a colorless 6/12 Construct artifact creature token with trample.| +Thantis the Warweaver|Commander 2018|46|M|{3}{B}{R}{G}|Legendary Creature - Spider|5|5|Vigilance, reach$All creatures attack each combat if able.$Whenever a creature attacks you or a planeswalker you control, put a +1/+1 counter on Thantis the Warweaver.| +Tuvasa the Sunlit|Commander 2018|47|M|{G}{W}{U}|Legendary Creature - Merfolk Shaman|1|1|Tuvasa the Sunlit gets +1/+1 for each enchantment you control.$Whenever you cast your first enchantment spell each turn, draw a card.| +Varina, Lich Queen|Commander 2018|48|M|{1}{W}{U}{B}|Legendary Creature - Zombie Wizard|4|4|Whenever you attack with one or more Zombies, draw that many cards, then discard that many cards. You gain that much life.${2}, Exile two cards from your graveyard: Create a tapped 2/2 black Zombie creature token.| +Windgrace's Judgment|Commander 2018|49|R|{3}{B}{G}|Instant|||For any number of opponents, destroy target nonland permanent that player controls.| +Xantcha, Sleeper Agent|Commander 2018|50|R|{1}{B}{R}|Legendary Creature - Minion|5|5|As Xantcha, Sleeper Agent enters the battlefield, an opponent of your choice gains control of it.$Xantcha attacks each combat if able and can't attack its owner or planeswalkers its owner controls.${3}: Xantcha's controller loses 2 life and you draw a card. Any player may activate this ability.| +Yennet, Crypt Sovereign|Commander 2018|51|M|{2}{W}{U}{B}|Legendary Creature - Sphinx|3|5|Flying, vigilance, menace$Whenever Yennet, Crypt Sovereign attacks, reveal the top card of your library. If that card's converted mana cost is odd, you may cast it without paying its mana cost. Otherwise, draw a card.| +Yuriko, the Tiger's Shadow|Commander 2018|52|R|{1}{U}{B}|Legendary Creature - Human Ninja|1|3|Commander ninjutsu {U}{B}$Whenever a Ninja you control deals combat damage to a player, reveal the top card of your library and put that card into your hand. Each opponent loses life equal to that card's converted mana cost.| +Ancient Stone Idol|Commander 2018|53|R|{10}|Artifact Creature - Golem|12|12|Flash$This spell costs {1} less to cast for each attacking creature.$Trample$When Ancient Stone Idol dies, create a 6/12 colorless Construct artifact creature token with trample.| Coveted Jewel|Commander 2018|54|R|{6}|Artifact|||When Coveted Jewel enters the battlefield, draw three cards.${T}: Add three mana of any one color.$Whenever one or more creatures an opponent controls attack you and aren't blocked, that player draws three cards and gains control of Coveted Jewel. Untap it.| +Endless Atlas|Commander 2018|55|R|{2}|Artifact|||{2}, {T}: Draw a card. Activate this ability only if you control three or more lands with the same name.| Geode Golem|Commander 2018|56|U|{5}|Artifact Creature - Golem|5|3|Trample$Whenever Geode Golem deals combat damage to a player, you may cast your commander from the command zone without paying its mana cost.| Retrofitter Foundry|Commander 2018|57|R|{1}|Artifact|||{3}: Untap Retrofitter Foundry.${2}, {T}: Create a 1/1 colorless Servo artifact creature token.${1}, {T}, Sacrifice a Servo: Create a 1/1 colorless Thopter artifact creature token with flying.${T}, Sacrifice a Thopter: Create a 4/4 colorless Construct artifact creature token.| Forge of Heroes|Commander 2018|58|C||Land|||{T}: Add {C}.${T}: Choose target commander that entered the battlefield this turn. Put a +1/+1 counter on it if it's a creature and a loyalty counter on it if it's a planeswalker.| +Isolated Watchtower|Commander 2018|59|R||Land|||{T}: Add {C}.${2}, {T}: Scry 1, then you may reveal the top card of your library. If a basic land card is revealed this way, put it onto the battlefield tapped. Activate this ability only if an opponent controls at least two more lands than you.| +Adarkar Valkyrie|Commander 2018|60|R|{4}{W}{W}|Snow Creature - Angel|4|5|Flying, vigilance${T}: When target creature other than Adarkar Valkyrie dies this turn, return that card to the battlefield under your control.| +Ajani's Chosen|Commander 2018|61|R|{2}{W}{W}|Creature - Cat Soldier|3|3|Whenever an enchantment enters the battlefield under your control, create a 2/2 white Cat creature token. If that enchantment is an Aura, you may attach it to the token.| +Akroma's Vengeance|Commander 2018|62|R|{4}{W}{W}|Sorcery|||Destroy all artifacts, creatures, and enchantments.$Cycling {3}| +Banishing Stroke|Commander 2018|63|U|{5}{W}|Instant|||Put target artifact, creature, or enchantment on the bottom of its owner's library.$Miracle {W}| +Celestial Archon|Commander 2018|64|R|{3}{W}{W}|Enchantment Creature - Archon|4|4|Bestow {5}{W}{W}$Flying, first strike$Enchanted creature gets +4/+4 and has flying and first strike.| +Crib Swap|Commander 2018|65|U|{2}{W}|Tribal Instant - Shapeshifter|||Changeling$Exile target creature. Its controller creates a 1/1 colorless Shapeshifter creature token with changeling.| +Dismantling Blow|Commander 2018|66|C|{2}{W}|Instant|||Kicker {2}{U}$Destroy target artifact or enchantment. If this spell was kicked, draw two cards.| +Entreat the Angels|Commander 2018|67|M|{X}{X}{W}{W}{W}|Sorcery|||Create X 4/4 white Angel creature tokens with flying.$Miracle {X}{W}{W}| +Lightform|Commander 2018|68|U|{1}{W}{W}|Enchantment|||When Lightform enters the battlefield, it becomes an Aura with enchant creature. Manifest the top card of your library and attach Lightform to it.$Enchanted creature has flying and lifelink.| +Martial Coup|Commander 2018|69|R|{X}{W}{W}|Sorcery|||Create X 1/1 white Soldier creature tokens. If X is 5 or more, destroy all other creatures.| +Phyrexian Rebirth|Commander 2018|70|R|{4}{W}{W}|Sorcery|||Destroy all creatures, then create an X/X colorless Horror artifact creature token, where X is the number of creatures destroyed this way.| +Return to Dust|Commander 2018|71|U|{2}{W}{W}|Instant|||Exile target artifact or enchantment. If you cast this spell during your main phase, you may exile up to one other target artifact or enchantment.| +Sage's Reverie|Commander 2018|72|U|{3}{W}|Enchantment - Aura|||Enchant creature$When Sage's Reverie enters the battlefield, draw a card for each Aura you control that's attached to a creature.$Enchanted creature gets +1/+1 for each Aura you control that's attached to a creature.| +Serra Avatar|Commander 2018|73|M|{4}{W}{W}{W}|Creature - Avatar|*|*|Serra Avatar's power and toughness are each equal to your life total.$When Serra Avatar is put into a graveyard from anywhere, shuffle it into its owner's library.| +Sigil of the Empty Throne|Commander 2018|74|R|{3}{W}{W}|Enchantment|||Whenever you cast an enchantment spell, create a 4/4 white Angel creature token with flying.| +Silent Sentinel|Commander 2018|75|R|{5}{W}{W}|Creature - Archon|4|6|Flying$Whenever Silent Sentinel attacks, you may return target enchantment card from your graveyard to the battlefield.| +Soul Snare|Commander 2018|76|U|{W}|Enchantment|||{W}, Sacrifice Soul Snare: Exile target creature that's attacking you or a planeswalker you control.| +Terminus|Commander 2018|77|R|{4}{W}{W}|Sorcery|||Put all creatures on the bottom of their owners' libraries.$Miracle {W}| +Unquestioned Authority|Commander 2018|78|U|{2}{W}|Enchantment - Aura|||Enchant creature$When Unquestioned Authority enters the battlefield, draw a card.$Enchanted creature has protection from creatures.| +Winds of Rath|Commander 2018|79|R|{3}{W}{W}|Sorcery|||Destroy all creatures that aren't enchanted. They can't be regenerated.| +Aether Gale|Commander 2018|80|R|{3}{U}{U}|Sorcery|||Return six target nonland permanents to their owners' hands.| +Archetype of Imagination|Commander 2018|81|U|{4}{U}{U}|Enchantment Creature - Human Wizard|3|2|Creatures you control have flying.$Creatures your opponents control lose flying and can't have or gain flying.| +Brainstorm|Commander 2018|82|U|{U}|Instant|||Draw three cards, then put two cards from your hand on top of your library in any order.| +Cloudform|Commander 2018|83|U|{1}{U}{U}|Enchantment|||When Cloudform enters the battlefield, it becomes an Aura with enchant creature. Manifest the top card of your library and attach Cloudform to it.$Enchanted creature has flying and hexproof.| +Conundrum Sphinx|Commander 2018|84|R|{2}{U}{U}|Creature - Sphinx|4|4|Flying$Whenever Conundrum Sphinx attacks, each player chooses a card name. Then each player reveals the top card of their library. If the card a player revealed has the name they chose, that player puts it into their hand. If it doesn't, that player puts it on the bottom of their library.| +Devastation Tide|Commander 2018|85|R|{3}{U}{U}|Sorcery|||Return all nonland permanents to their owners' hands.$Miracle {1}{U}| +Dictate of Kruphix|Commander 2018|86|R|{1}{U}{U}|Enchantment|||Flash$At the beginning of each player's draw step, that player draws an additional card.| +Djinn of Wishes|Commander 2018|87|R|{3}{U}{U}|Creature - Djinn|4|4|Flying$Djinn of Wishes enters the battlefield with three wish counters on it.${2}{U}{U}, Remove a wish counter from Djinn of Wishes: Reveal the top card of your library. You may play that card without paying its mana cost. If you don't, exile it.| +Dream Cache|Commander 2018|88|C|{2}{U}|Sorcery|||Draw three cards, then put two cards from your hand both on top of your library or both on the bottom of your library.| +Eel Umbra|Commander 2018|89|C|{1}{U}|Enchantment - Aura|||Flash| +Etherium Sculptor|Commander 2018|90|C|{1}{U}|Artifact Creature - Vedalken Artificer|1|2|Artifact spells you cast cost {1} less to cast.| +Inkwell Leviathan|Commander 2018|91|R|{7}{U}{U}|Artifact Creature - Leviathan|7|11|Islandwalk| +Into the Roil|Commander 2018|92|C|{1}{U}|Instant|||Kicker {1}{U}$Return target nonland permanent to its owner's hand. If this spell was kicked, draw a card.| +Jeskai Infiltrator|Commander 2018|93|R|{2}{U}|Creature - Human Monk|2|3|Jeskai Infiltrator can't be blocked as long as you control no other creatures.$When Jeskai Infiltrator deals combat damage to a player, exile it and the top card of your library in a face-down pile, shuffle that pile, then manifest those cards.| +Mulldrifter|Commander 2018|94|U|{4}{U}|Creature - Elemental|2|2|Flying$When Mulldrifter enters the battlefield, draw two cards.$Evoke {2}{U}| +Ninja of the Deep Hours|Commander 2018|95|C|{3}{U}|Creature - Human Ninja|2|2|Ninjutsu {1}{U}$Whenever Ninja of the Deep Hours deals combat damage to a player, you may draw a card.| +Ponder|Commander 2018|96|C|{U}|Sorcery|||Look at the top three cards of your library, then put them back in any order. You may shuffle your library.$Draw a card.| +Portent|Commander 2018|97|C|{U}|Sorcery|||Look at the top three cards of target player's library, then put them back in any order. You may have that player shuffle their library.$Draw a card at the beginning of the next turn's upkeep.| +Predict|Commander 2018|98|U|{1}{U}|Instant|||Choose a card name, then target player puts the top card of their library into their graveyard. If that card has the chosen name, you draw two cards. Otherwise, you draw a card.| +Reverse Engineer|Commander 2018|99|U|{3}{U}{U}|Sorcery|||Improvise$Draw three cards.| +Saheeli's Artistry|Commander 2018|100|R|{4}{U}{U}|Sorcery|||Choose one or both —$• Create a token that's a copy of target artifact.$• Create a token that's a copy of target creature, except it's an artifact in addition to its other types.| +Sharding Sphinx|Commander 2018|101|R|{4}{U}{U}|Artifact Creature - Sphinx|4|4|Flying$Whenever an artifact creature you control deals combat damage to a player, you may create a 1/1 blue Thopter artifact creature token with flying.| +Sigiled Starfish|Commander 2018|102|C|{1}{U}|Creature - Starfish|0|3|{T}: Scry 1.| +Sphinx of Jwar Isle|Commander 2018|103|R|{4}{U}{U}|Creature - Sphinx|5|5|Flying$Shroud| +Sphinx of Uthuun|Commander 2018|104|R|{5}{U}{U}|Creature - Sphinx|5|6|Flying$When Sphinx of Uthuun enters the battlefield, reveal the top five cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard.| +Telling Time|Commander 2018|105|C|{1}{U}|Instant|||Look at the top three cards of your library. Put one of those cards into your hand, one on top of your library, and one on the bottom of your library.| +Thirst for Knowledge|Commander 2018|106|U|{2}{U}|Instant|||Draw three cards. Then discard two cards unless you discard an artifact card.| Thopter Spy Network|Commander 2018|107|R|{2}{U}{U}|Enchantment|||At the beginning of your upkeep, if you control an artifact, create a 1/1 colorless Thopter artifact creature token with flying.$Whenever one or more artifact creatures you control deal combat damage to a player, draw a card.| +Tidings|Commander 2018|108|U|{3}{U}{U}|Sorcery|||Draw four cards.| +Treasure Hunt|Commander 2018|109|C|{1}{U}|Sorcery|||Reveal cards from the top of your library until you reveal a nonland card, then put all cards revealed this way into your hand.| +Vow of Flight|Commander 2018|110|U|{2}{U}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2, has flying, and can't attack you or a planeswalker you control.| +Whirler Rogue|Commander 2018|111|U|{2}{U}{U}|Creature - Human Rogue Artificer|2|2|When Whirler Rogue enters the battlefield, create two 1/1 colorless Thopter artifact creature tokens with flying.$Tap two untapped artifacts you control: Target creature can't be blocked this turn.| +Whitewater Naiads|Commander 2018|112|U|{3}{U}{U}|Enchantment Creature - Nymph|4|4|Constellation — Whenever Whitewater Naiads or another enchantment enters the battlefield under your control, target creature can't be blocked this turn.| +Army of the Damned|Commander 2018|113|M|{5}{B}{B}{B}|Sorcery|||Create thirteen tapped 2/2 black Zombie creature tokens.$Flashback {7}{B}{B}{B}| +Moonlight Bargain|Commander 2018|114|R|{3}{B}{B}|Instant|||Look at the top five cards of your library. For each card, put that card into your graveyard unless you pay 2 life. Then put the rest into your hand.| +Phyrexian Delver|Commander 2018|115|R|{3}{B}{B}|Creature - Zombie|3|2|When Phyrexian Delver enters the battlefield, return target creature card from your graveyard to the battlefield. You lose life equal to that card's converted mana cost.| +Retreat to Hagra|Commander 2018|116|U|{2}{B}|Enchantment|||Landfall — Whenever a land enters the battlefield under your control, choose one —$• Target creature gets +1/+0 and gains deathtouch until end of turn.$• Each opponent loses 1 life and you gain 1 life.| +Ruinous Path|Commander 2018|117|R|{1}{B}{B}|Sorcery|||Destroy target creature or planeswalker.$Awaken 4—{5}{B}{B}| +Soul of Innistrad|Commander 2018|118|M|{4}{B}{B}|Creature - Avatar|6|6|Deathtouch${3}{B}{B}: Return up to three target creature cards from your graveyard to your hand.${3}{B}{B}, Exile Soul of Innistrad from your graveyard: Return up to three target creature cards from your graveyard to your hand.| +Stitch Together|Commander 2018|119|U|{B}{B}|Sorcery|||Return target creature card from your graveyard to your hand.$Threshold — Return that card from your graveyard to the battlefield instead if seven or more cards are in your graveyard.| +Blasphemous Act|Commander 2018|120|R|{8}{R}|Sorcery|||This spell costs {1} less to cast for each creature on the battlefield.$Blasphemous Act deals 13 damage to each creature.| +Chain Reaction|Commander 2018|121|R|{2}{R}{R}|Sorcery|||Chain Reaction deals X damage to each creature, where X is the number of creatures on the battlefield.| Chaos Warp|Commander 2018|122|R|{2}{R}|Instant|||The owner of target permanent shuffles it into their library, then reveals the top card of their library. If it's a permanent card, they put it onto the battlefield.| +Flameblast Dragon|Commander 2018|123|R|{4}{R}{R}|Creature - Dragon|5|5|Flying$Whenever Flameblast Dragon attacks, you may pay {X}{R}. If you do, Flameblast Dragon deals X damage to any target.| +Hellkite Igniter|Commander 2018|124|R|{5}{R}{R}|Creature - Dragon|5|5|Flying, haste${1}{R}: Hellkite Igniter gets +X/+0 until end of turn, where X is the number of artifacts you control.| +Magmaquake|Commander 2018|125|R|{X}{R}{R}|Instant|||Magmaquake deals X damage to each creature without flying and each planeswalker.| +Thopter Engineer|Commander 2018|126|U|{2}{R}|Creature - Human Artificer|1|3|When Thopter Engineer enters the battlefield, create a 1/1 colorless Thopter artifact creature token with flying.$Artifact creatures you control have haste.| +Acidic Slime|Commander 2018|127|U|{3}{G}{G}|Creature - Ooze|2|2|Deathtouch$When Acidic Slime enters the battlefield, destroy target artifact, enchantment, or land.| +Aura Gnarlid|Commander 2018|128|C|{2}{G}|Creature - Beast|2|2|Creatures with power less than Aura Gnarlid's power can't block it.$Aura Gnarlid gets +1/+1 for each Aura on the battlefield.| +Avenger of Zendikar|Commander 2018|129|M|{5}{G}{G}|Creature - Elemental|5|5|When Avenger of Zendikar enters the battlefield, create a 0/1 green Plant creature token for each land you control.$Landfall — Whenever a land enters the battlefield under your control, you may put a +1/+1 counter on each Plant creature you control.| +Baloth Woodcrasher|Commander 2018|130|U|{4}{G}{G}|Creature - Beast|4|4|Landfall — Whenever a land enters the battlefield under your control, Baloth Woodcrasher gets +4/+4 and gains trample until end of turn.| +Bear Umbra|Commander 2018|131|R|{2}{G}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and has "Whenever this creature attacks, untap all lands you control."$Totem armor| +Boon Satyr|Commander 2018|132|R|{1}{G}{G}|Enchantment Creature - Satyr|4|2|Flash$Bestow {3}{G}{G}$Enchanted creature gets +4/+2.| +Borderland Explorer|Commander 2018|133|C|{1}{G}|Creature - Elf Scout|3|1|When Borderland Explorer enters the battlefield, each player may discard a card. Each player who discarded a card this way may search their library for a basic land card, reveal it, put it into their hand, then shuffle their library.| +Budoka Gardener|Commander 2018|134|R|{1}{G}|Creature - Human Monk|2|1|{T}: You may put a land card from your hand onto the battlefield. If you control ten or more lands, flip Budoka Gardener.| +Centaur Vinecrasher|Commander 2018|135|R|{3}{G}|Creature - Plant Centaur|1|1|Trample$Centaur Vinecrasher enters the battlefield with a number of +1/+1 counters on it equal to the number of land cards in all graveyards.$Whenever a land card is put into a graveyard from anywhere, you may pay {G}{G}. If you do, return Centaur Vinecrasher from your graveyard to your hand.| +Consign to Dust|Commander 2018|136|U|{2}{G}|Instant|||Strive — This spell costs {2}{G} more to cast for each target beyond the first.$Destroy any number of target artifacts and/or enchantments.| +Creeping Renaissance|Commander 2018|137|R|{3}{G}{G}|Sorcery|||Choose a permanent type. Return all cards of the chosen type from your graveyard to your hand.$Flashback {5}{G}{G}| +Cultivate|Commander 2018|138|C|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.| +Dawn's Reflection|Commander 2018|139|C|{3}{G}|Enchantment - Aura|||Enchant land$Whenever enchanted land is tapped for mana, its controller adds an additional two mana in any combination of colors.| +Eidolon of Blossoms|Commander 2018|140|R|{2}{G}{G}|Enchantment Creature - Spirit|2|2|Constellation — Whenever Eidolon of Blossoms or another enchantment enters the battlefield under your control, draw a card.| +Enchantress's Presence|Commander 2018|141|R|{2}{G}|Enchantment|||Whenever you cast an enchantment spell, draw a card.| +Epic Proportions|Commander 2018|142|R|{4}{G}{G}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature gets +5/+5 and has trample.| +Explore|Commander 2018|143|C|{1}{G}|Sorcery|||You may play an additional land this turn.$Draw a card.| +Explosive Vegetation|Commander 2018|144|U|{3}{G}|Sorcery|||Search your library for up to two basic land cards and put them onto the battlefield tapped. Then shuffle your library.| +Far Wanderings|Commander 2018|145|C|{2}{G}|Sorcery|||Search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$Threshold — If seven or more cards are in your graveyard, instead search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle your library.| +Farhaven Elf|Commander 2018|146|C|{2}{G}|Creature - Elf Druid|1|1|When Farhaven Elf enters the battlefield, you may search your library for a basic land card and put it onto the battlefield tapped. If you do, shuffle your library.| +Fertile Ground|Commander 2018|147|C|{1}{G}|Enchantment - Aura|||Enchant land$Whenever enchanted land is tapped for mana, its controller adds an additional one mana of any color.| +Grapple with the Past|Commander 2018|148|C|{1}{G}|Instant|||Put the top three cards of your library into your graveyard, then you may return a creature or land card from your graveyard to your hand.| +Ground Seal|Commander 2018|149|R|{1}{G}|Enchantment|||When Ground Seal enters the battlefield, draw a card.$Cards in graveyards can't be the targets of spells or abilities.| +Harrow|Commander 2018|150|C|{2}{G}|Instant|||As an additional cost to cast this spell, sacrifice a land.$Search your library for up to two basic land cards and put them onto the battlefield. Then shuffle your library.| +Herald of the Pantheon|Commander 2018|151|R|{1}{G}|Creature - Centaur Shaman|2|2|Enchantment spells you cast cost {1} less to cast.$Whenever you cast an enchantment spell, you gain 1 life.| +Hunting Wilds|Commander 2018|152|U|{3}{G}|Sorcery|||Kicker {3}{G}$Search your library for up to two Forest cards and put them onto the battlefield tapped. Then shuffle your library.$If this spell was kicked, untap all Forests put onto the battlefield this way. They become 3/3 green creatures with haste that are still lands.| +Hydra Omnivore|Commander 2018|153|M|{4}{G}{G}|Creature - Hydra|8|8|Whenever Hydra Omnivore deals combat damage to an opponent, it deals that much damage to each other opponent.| +Khalni Heart Expedition|Commander 2018|154|C|{1}{G}|Enchantment|||Landfall — Whenever a land enters the battlefield under your control, you may put a quest counter on Khalni Heart Expedition.$Remove three quest counters from Khalni Heart Expedition and sacrifice it: Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library.| +Kruphix's Insight|Commander 2018|155|C|{2}{G}|Sorcery|||Reveal the top six cards of your library. Put up to three enchantment cards from among them into your hand and the rest of the revealed cards into your graveyard.| +Moldgraf Monstrosity|Commander 2018|156|R|{4}{G}{G}{G}|Creature - Insect|8|8|Trample$When Moldgraf Monstrosity dies, exile it, then return two creature cards at random from your graveyard to the battlefield.| +Overgrowth|Commander 2018|157|C|{2}{G}|Enchantment - Aura|||Enchant land$Whenever enchanted land is tapped for mana, its controller adds an additional {G}{G}.| +Rampaging Baloths|Commander 2018|158|R|{4}{G}{G}|Creature - Beast|6|6|Trample$Landfall — Whenever a land enters the battlefield under your control, you may create a 4/4 green Beast creature token.| +Reclamation Sage|Commander 2018|159|U|{2}{G}|Creature - Elf Shaman|2|1|When Reclamation Sage enters the battlefield, you may destroy target artifact or enchantment.| +Sakura-Tribe Elder|Commander 2018|160|C|{1}{G}|Creature - Snake Shaman|1|1|Sacrifice Sakura-Tribe Elder: Search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.| +Scute Mob|Commander 2018|161|R|{G}|Creature - Insect|1|1|At the beginning of your upkeep, if you control five or more lands, put four +1/+1 counters on Scute Mob.| +Snake Umbra|Commander 2018|162|C|{2}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and has "Whenever this creature deals damage to an opponent, you may draw a card."$Totem armor| +Spawning Grounds|Commander 2018|163|R|{6}{G}{G}|Enchantment - Aura|||Enchant land$Enchanted land has "{T}: Create a 5/5 green Beast creature token with trample."| +Vow of Wildness|Commander 2018|164|U|{2}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3, has trample, and can't attack you or a planeswalker you control.| +Wild Growth|Commander 2018|165|C|{G}|Enchantment - Aura|||Enchant land$Whenever enchanted land is tapped for mana, its controller adds an additional {G}.| +Yavimaya Elder|Commander 2018|166|C|{1}{G}{G}|Creature - Human Druid|2|1|When Yavimaya Elder dies, you may search your library for up to two basic land cards, reveal them, and put them into your hand. If you do, shuffle your library.${2}, Sacrifice Yavimaya Elder: Draw a card.| +Yavimaya Enchantress|Commander 2018|167|C|{2}{G}|Creature - Human Druid|2|2|Yavimaya Enchantress gets +1/+1 for each enchantment on the battlefield.| +Aethermage's Touch|Commander 2018|168|R|{2}{W}{U}|Instant|||Reveal the top four cards of your library. You may put a creature card from among them onto the battlefield. It gains "At the beginning of your end step, return this creature to its owner's hand." Then put the rest of the cards revealed this way on the bottom of your library in any order.| +Bant Charm|Commander 2018|169|U|{G}{W}{U}|Instant|||Choose one —$• Destroy target artifact.$• Put target creature on the bottom of its owner's library.$• Counter target instant spell.| +Bruna, Light of Alabaster|Commander 2018|170|M|{3}{W}{W}{U}|Legendary Creature - Angel|5|5|Flying, vigilance$Whenever Bruna, Light of Alabaster attacks or blocks, you may attach to it any number of Auras on the battlefield and you may put onto the battlefield attached to it any number of Aura cards that could enchant it from your graveyard and/or hand.| +Charnelhoard Wurm|Commander 2018|171|R|{4}{B}{R}{G}|Creature - Wurm|6|6|Trample$Whenever Charnelhoard Wurm deals damage to an opponent, you may return target card from your graveyard to your hand.| +Cold-Eyed Selkie|Commander 2018|172|R|{1}{G/U}{G/U}|Creature - Merfolk Rogue|1|1|Islandwalk$Whenever Cold-Eyed Selkie deals combat damage to a player, you may draw that many cards.| +Daxos of Meletis|Commander 2018|173|R|{1}{W}{U}|Legendary Creature - Human Soldier|2|2|Daxos of Meletis can't be blocked by creatures with power 3 or greater.$Whenever Daxos of Meletis deals combat damage to a player, exile the top card of that player's library. You gain life equal to that card's converted mana cost. Until end of turn, you may cast that card and you may spend mana as though it were mana of any color to cast that spell.| +Deathreap Ritual|Commander 2018|174|U|{2}{B}{G}|Enchantment|||Morbid — At the beginning of each end step, if a creature died this turn, you may draw a card.| +Decimate|Commander 2018|175|R|{2}{R}{G}|Sorcery|||Destroy target artifact, target creature, target enchantment, and target land.| +Duskmantle Seer|Commander 2018|176|R|{2}{U}{B}|Creature - Vampire Wizard|4|4|Flying$At the beginning of your upkeep, each player reveals the top card of their library, loses life equal to that card's converted mana cost, then puts it into their hand.| +Elderwood Scion|Commander 2018|177|R|{3}{G}{W}|Creature - Elemental|4|4|Trample, lifelink$Spells you cast that target Elderwood Scion cost {2} less to cast.$Spells your opponents cast that target Elderwood Scion cost {2} more to cast.| +Enigma Sphinx|Commander 2018|178|R|{4}{W}{U}{B}|Artifact Creature - Sphinx|5|4|Flying$When Enigma Sphinx is put into your graveyard from the battlefield, put it into your library third from the top.$Cascade| +Esper Charm|Commander 2018|179|U|{W}{U}{B}|Instant|||Choose one —$• Destroy target enchantment.$• Draw two cards.$• Target player discards two cards.| +Finest Hour|Commander 2018|180|R|{2}{G}{W}{U}|Enchantment|||Exalted$Whenever a creature you control attacks alone, if it's the first combat phase of the turn, untap that creature. After this phase, there is an additional combat phase.| +Gaze of Granite|Commander 2018|181|R|{X}{B}{B}{G}|Sorcery|||Destroy each nonland permanent with converted mana cost X or less.| +Grisly Salvage|Commander 2018|182|C|{B}{G}|Instant|||Reveal the top five cards of your library. You may put a creature or land card from among them into your hand. Put the rest into your graveyard.| +High Priest of Penance|Commander 2018|183|R|{W}{B}|Creature - Human Cleric|1|1|Whenever High Priest of Penance is dealt damage, you may destroy target nonland permanent.| +Lavalanche|Commander 2018|184|R|{X}{B}{R}{G}|Sorcery|||Lavalanche deals X damage to target player or planeswalker and each creature that player or that planeswalker's controller controls.| +Maverick Thopterist|Commander 2018|185|U|{3}{U}{R}|Creature - Human Artificer|2|2|Improvise$When Maverick Thopterist enters the battlefield, create two 1/1 colorless Thopter artifact creature tokens with flying.| +Mortify|Commander 2018|186|U|{1}{W}{B}|Instant|||Destroy target creature or enchantment.| +Putrefy|Commander 2018|187|U|{1}{B}{G}|Instant|||Destroy target artifact or creature. It can't be regenerated.| +Righteous Authority|Commander 2018|188|R|{3}{W}{U}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 for each card in its controller's hand.$At the beginning of the draw step of enchanted creature's controller, that player draws an additional card.| +Rubblehulk|Commander 2018|189|R|{4}{R}{G}|Creature - Elemental|*|*|Rubblehulk's power and toughness are each equal to the number of lands you control.$Bloodrush — {1}{R}{G}, Discard Rubblehulk: Target attacking creature gets +X/+X until end of turn, where X is the number of lands you control.| +Savage Twister|Commander 2018|190|U|{X}{R}{G}|Sorcery|||Savage Twister deals X damage to each creature.| +Silent-Blade Oni|Commander 2018|191|R|{3}{U}{U}{B}{B}|Creature - Demon Ninja|6|5|Ninjutsu {4}{U}{B}$Whenever Silent-Blade Oni deals combat damage to a player, look at that player's hand. You may cast a nonland card in it without paying that card's mana cost.| +Unflinching Courage|Commander 2018|192|U|{1}{G}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and has trample and lifelink.| +Utter End|Commander 2018|193|R|{2}{W}{B}|Instant|||Exile target nonland permanent.| +Worm Harvest|Commander 2018|194|R|{2}{B/G}{B/G}{B/G}|Sorcery|||Create a 1/1 black and green Worm creature token for each land card in your graveyard.$Retrace| +Zendikar Incarnate|Commander 2018|195|U|{2}{R}{G}|Creature - Elemental|*|4|Zendikar Incarnate's power is equal to the number of lands you control.| +Azorius Signet|Commander 2018|196|U|{2}|Artifact|||{1}, {T}: Add {W}{U}.| +Blinkmoth Urn|Commander 2018|197|R|{5}|Artifact|||At the beginning of each player's precombat main phase, if Blinkmoth Urn is untapped, that player adds {C} for each artifact they control.| +Bosh, Iron Golem|Commander 2018|198|R|{8}|Legendary Artifact Creature - Golem|6|7|Trample${3}{R}, Sacrifice an artifact: Bosh, Iron Golem deals damage equal to the sacrificed artifact's converted mana cost to any target.| +Chief of the Foundry|Commander 2018|199|U|{3}|Artifact Creature - Construct|2|3|Other artifact creatures you control get +1/+1.| +Commander's Sphere|Commander 2018|200|C|{3}|Artifact|||{T}: Add one mana of any color in your commander's color identity.$Sacrifice Commander's Sphere: Draw a card.| +Crystal Ball|Commander 2018|201|U|{3}|Artifact|||{1}, {T}: Scry 2.| +Darksteel Juggernaut|Commander 2018|202|R|{5}|Artifact Creature - Juggernaut|*|*|Indestructible$Darksteel Juggernaut's power and toughness are each equal to the number of artifacts you control.$Darksteel Juggernaut attacks each combat if able.| +Dimir Signet|Commander 2018|203|U|{2}|Artifact|||{1}, {T}: Add {U}{B}.| +Dreamstone Hedron|Commander 2018|204|U|{6}|Artifact|||{T}: Add {C}{C}{C}.${3}, {T}, Sacrifice Dreamstone Hedron: Draw three cards.| +Duplicant|Commander 2018|205|R|{6}|Artifact Creature - Shapeshifter|2|4|Imprint — When Duplicant enters the battlefield, you may exile target nontoken creature.$As long as a card exiled with Duplicant is a creature card, Duplicant has the power, toughness, and creature types of the last creature card exiled with Duplicant. It's still a Shapeshifter.| +Hedron Archive|Commander 2018|206|U|{4}|Artifact|||{T}: Add {C}{C}.${2}, {T}, Sacrifice Hedron Archive: Draw two cards.| +Izzet Signet|Commander 2018|207|U|{2}|Artifact|||{1}, {T}: Add {U}{R}.| +Magnifying Glass|Commander 2018|208|U|{3}|Artifact|||{T}: Add {C}.${4}, {T}: Investigate.| +Mimic Vat|Commander 2018|209|R|{3}|Artifact|||Imprint — Whenever a nontoken creature dies, you may exile that card. If you do, return each other card exiled with Mimic Vat to its owner's graveyard.${3}, {T}: Create a token that's a copy of a card exiled with Mimic Vat. It gains haste. Exile it at the beginning of the next end step.| +Mind Stone|Commander 2018|210|C|{2}|Artifact|||{T}: Add {C}.${1}, {T}, Sacrifice Mind Stone: Draw a card.| +Mirrorworks|Commander 2018|211|R|{5}|Artifact|||Whenever another nontoken artifact enters the battlefield under your control, you may pay {2}. If you do, create a token that's a copy of that artifact.| +Myr Battlesphere|Commander 2018|212|R|{7}|Artifact Creature - Myr Construct|4|7|When Myr Battlesphere enters the battlefield, create four 1/1 colorless Myr artifact creature tokens.$Whenever Myr Battlesphere attacks, you may tap X untapped Myr you control. If you do, Myr Battlesphere gets +X/+0 until end of turn and deals X damage to the player or planeswalker it's attacking.| +Orzhov Signet|Commander 2018|213|U|{2}|Artifact|||{1}, {T}: Add {W}{B}.| +Pilgrim's Eye|Commander 2018|214|C|{3}|Artifact Creature - Thopter|1|1|Flying$When Pilgrim's Eye enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.| +Prismatic Lens|Commander 2018|215|U|{2}|Artifact|||{T}: Add {C}.${1}, {T}: Add one mana of any color.| +Prototype Portal|Commander 2018|216|R|{4}|Artifact|||Imprint — When Prototype Portal enters the battlefield, you may exile an artifact card from your hand.${X}, {T}: Create a token that's a copy of the exiled card. X is the converted mana cost of that card.| +Psychosis Crawler|Commander 2018|217|R|{5}|Artifact Creature - Horror|*|*|Psychosis Crawler's power and toughness are each equal to the number of cards in your hand.$Whenever you draw a card, each opponent loses 1 life.| +Scrabbling Claws|Commander 2018|218|U|{1}|Artifact|||{T}: Target player exiles a card from their graveyard.${1}, Sacrifice Scrabbling Claws: Exile target card from a graveyard. Draw a card.| +Scuttling Doom Engine|Commander 2018|219|R|{6}|Artifact Creature - Construct|6|6|Scuttling Doom Engine can't be blocked by creatures with power 2 or less.$When Scuttling Doom Engine dies, it deals 6 damage to target opponent or planeswalker.| +Seer's Lantern|Commander 2018|220|C|{3}|Artifact|||{T}: Add {C}.| +Seer's Sundial|Commander 2018|221|R|{4}|Artifact|||Landfall — Whenever a land enters the battlefield under your control, you may pay {2}. If you do, draw a card.| +Sol Ring|Commander 2018|222|U|{1}|Artifact|||{T}: Add {C}{C}.| +Soul of New Phyrexia|Commander 2018|223|M|{6}|Artifact Creature - Avatar|6|6|Trample${5}: Permanents you control gain indestructible until end of turn.${5}, Exile Soul of New Phyrexia from your graveyard: Permanents you control gain indestructible until end of turn.| +Steel Hellkite|Commander 2018|224|R|{6}|Artifact Creature - Dragon|5|5|Flying${2}: Steel Hellkite gets +1/+0 until end of turn.${X}: Destroy each nonland permanent with converted mana cost X whose controller was dealt combat damage by Steel Hellkite this turn. Activate this ability only once each turn.| +Swiftfoot Boots|Commander 2018|225|U|{2}|Artifact - Equipment|||Equipped creature has hexproof and haste.$Equip {1}| +Thopter Assembly|Commander 2018|226|R|{6}|Artifact Creature - Thopter|5|5|Flying$At the beginning of your upkeep, if you control no Thopters other than Thopter Assembly, return Thopter Assembly to its owner's hand and create five 1/1 colorless Thopter artifact creature tokens with flying.| +Unstable Obelisk|Commander 2018|227|U|{3}|Artifact|||{T}: Add {C}.${7}, {T}, Sacrifice Unstable Obelisk: Destroy target permanent.| +Unwinding Clock|Commander 2018|228|R|{4}|Artifact|||Untap all artifacts you control during each other player's untap step.| +Vessel of Endless Rest|Commander 2018|229|U|{3}|Artifact|||When Vessel of Endless Rest enters the battlefield, put target card from a graveyard on the bottom of its owner's library.${T}: Add one mana of any color.| +Worn Powerstone|Commander 2018|230|U|{3}|Artifact|||Worn Powerstone enters the battlefield tapped.${T}: Add {C}{C}.| +Akoum Refuge|Commander 2018|231|U||Land|||Akoum Refuge enters the battlefield tapped.$When Akoum Refuge enters the battlefield, you gain 1 life.${T}: Add {B} or {R}.| +Arcane Sanctum|Commander 2018|232|U||Land|||Arcane Sanctum enters the battlefield tapped.${T}: Add {W}, {U}, or {B}.| +Azorius Chancery|Commander 2018|233|U||Land|||Azorius Chancery enters the battlefield tapped.$When Azorius Chancery enters the battlefield, return a land you control to its owner's hand.${T}: Add {W}{U}.| +Azorius Guildgate|Commander 2018|234|C||Land - Gate|||Azorius Guildgate enters the battlefield tapped.${T}: Add {W} or {U}.| +Barren Moor|Commander 2018|235|C||Land|||Barren Moor enters the battlefield tapped.${T}: Add {B}.$Cycling {B}| +Blighted Woodland|Commander 2018|236|U||Land|||{T}: Add {C}.${3}{G}, {T}, Sacrifice Blighted Woodland: Search your library for up to two basic land cards and put them onto the battlefield tapped. Then shuffle your library.| +Blossoming Sands|Commander 2018|237|C||Land|||Blossoming Sands enters the battlefield tapped.$When Blossoming Sands enters the battlefield, you gain 1 life.${T}: Add {G} or {W}.| +Bojuka Bog|Commander 2018|238|C||Land|||Bojuka Bog enters the battlefield tapped.$When Bojuka Bog enters the battlefield, exile all cards from target player's graveyard.${T}: Add {B}.| +Buried Ruin|Commander 2018|239|U||Land|||{T}: Add {C}.${2}, {T}, Sacrifice Buried Ruin: Return target artifact card from your graveyard to your hand.| +Command Tower|Commander 2018|240|C||Land|||{T}: Add one mana of any color in your commander's color identity.| +Darksteel Citadel|Commander 2018|241|U||Artifact Land|||Indestructible${T}: Add {C}.| +Dimir Aqueduct|Commander 2018|242|U||Land|||Dimir Aqueduct enters the battlefield tapped.$When Dimir Aqueduct enters the battlefield, return a land you control to its owner's hand.${T}: Add {U}{B}.| +Dimir Guildgate|Commander 2018|243|C||Land - Gate|||Dimir Guildgate enters the battlefield tapped.${T}: Add {U} or {B}.| +Dismal Backwater|Commander 2018|244|C||Land|||Dismal Backwater enters the battlefield tapped.$When Dismal Backwater enters the battlefield, you gain 1 life.${T}: Add {U} or {B}.| +Evolving Wilds|Commander 2018|245|C||Land|||{T}, Sacrifice Evolving Wilds: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.| +Forgotten Cave|Commander 2018|246|C||Land|||Forgotten Cave enters the battlefield tapped.${T}: Add {R}.$Cycling {R}| +Forsaken Sanctuary|Commander 2018|247|U||Land|||Forsaken Sanctuary enters the battlefield tapped.${T}: Add {W} or {B}.| +Foundry of the Consuls|Commander 2018|248|U||Land|||{T}: Add {C}.${5}, {T}, Sacrifice Foundry of the Consuls: Create two 1/1 colorless Thopter artifact creature tokens with flying.| +Golgari Rot Farm|Commander 2018|249|U||Land|||Golgari Rot Farm enters the battlefield tapped.$When Golgari Rot Farm enters the battlefield, return a land you control to its owner's hand.${T}: Add {B}{G}.| +Great Furnace|Commander 2018|250|C||Artifact Land|||{T}: Add {R}.| +Grim Backwoods|Commander 2018|251|R||Land|||{T}: Add {C}.${2}{B}{G}, {T}, Sacrifice a creature: Draw a card.| +Gruul Turf|Commander 2018|252|U||Land|||Gruul Turf enters the battlefield tapped.$When Gruul Turf enters the battlefield, return a land you control to its owner's hand.${T}: Add {R}{G}.| +Halimar Depths|Commander 2018|253|C||Land|||Halimar Depths enters the battlefield tapped.$When Halimar Depths enters the battlefield, look at the top three cards of your library, then put them back in any order.${T}: Add {U}.| +Haunted Fengraf|Commander 2018|254|C||Land|||{T}: Add {C}.${3}, {T}, Sacrifice Haunted Fengraf: Return a creature card at random from your graveyard to your hand.| +Highland Lake|Commander 2018|255|U||Land|||Highland Lake enters the battlefield tapped.${T}: Add {U} or {R}.| +Izzet Boilerworks|Commander 2018|256|U||Land|||Izzet Boilerworks enters the battlefield tapped.$When Izzet Boilerworks enters the battlefield, return a land you control to its owner's hand.${T}: Add {U}{R}.| +Izzet Guildgate|Commander 2018|257|C||Land - Gate|||Izzet Guildgate enters the battlefield tapped.${T}: Add {U} or {R}.| +Jund Panorama|Commander 2018|258|C||Land|||{T}: Add {C}.${1}, {T}, Sacrifice Jund Panorama: Search your library for a basic Swamp, Mountain, or Forest card and put it onto the battlefield tapped. Then shuffle your library.| +Jungle Hollow|Commander 2018|259|C||Land|||Jungle Hollow enters the battlefield tapped.$When Jungle Hollow enters the battlefield, you gain 1 life.${T}: Add {B} or {G}.| +Jwar Isle Refuge|Commander 2018|260|U||Land|||Jwar Isle Refuge enters the battlefield tapped.$When Jwar Isle Refuge enters the battlefield, you gain 1 life.${T}: Add {U} or {B}.| +Kazandu Refuge|Commander 2018|261|U||Land|||Kazandu Refuge enters the battlefield tapped.$When Kazandu Refuge enters the battlefield, you gain 1 life.${T}: Add {R} or {G}.| +Khalni Garden|Commander 2018|262|C||Land|||Khalni Garden enters the battlefield tapped.$When Khalni Garden enters the battlefield, create a 0/1 green Plant creature token.${T}: Add {G}.| +Krosan Verge|Commander 2018|263|U||Land|||Krosan Verge enters the battlefield tapped.${T}: Add {C}.${2}, {T}, Sacrifice Krosan Verge: Search your library for a Forest card and a Plains card and put them onto the battlefield tapped. Then shuffle your library.| +Lonely Sandbar|Commander 2018|264|C||Land|||Lonely Sandbar enters the battlefield tapped.${T}: Add {U}.$Cycling {U}| +Meandering River|Commander 2018|265|U||Land|||Meandering River enters the battlefield tapped.${T}: Add {W} or {U}.| +Mortuary Mire|Commander 2018|266|C||Land|||Mortuary Mire enters the battlefield tapped.$When Mortuary Mire enters the battlefield, you may put target creature card from your graveyard on top of your library.${T}: Add {B}.| +Mosswort Bridge|Commander 2018|267|R||Land|||Hideaway${T}: Add {G}.${G}, {T}: You may play the exiled card without paying its mana cost if creatures you control have total power 10 or greater.| +Mountain Valley|Commander 2018|268|U||Land|||Mountain Valley enters the battlefield tapped.${T}, Sacrifice Mountain Valley: Search your library for a Mountain or Forest card and put it onto the battlefield. Then shuffle your library.| +Myriad Landscape|Commander 2018|269|U||Land|||Myriad Landscape enters the battlefield tapped.${T}: Add {C}.${2}, {T}, Sacrifice Myriad Landscape: Search your library for up to two basic land cards that share a land type, put them onto the battlefield tapped, then shuffle your library.| +New Benalia|Commander 2018|270|U||Land|||New Benalia enters the battlefield tapped.$When New Benalia enters the battlefield, scry 1.${T}: Add {W}.| +Orzhov Basilica|Commander 2018|271|U||Land|||Orzhov Basilica enters the battlefield tapped.$When Orzhov Basilica enters the battlefield, return a land you control to its owner's hand.${T}: Add {W}{B}.| +Orzhov Guildgate|Commander 2018|272|C||Land - Gate|||Orzhov Guildgate enters the battlefield tapped.${T}: Add {W} or {B}.| +Rakdos Carnarium|Commander 2018|273|C||Land|||Rakdos Carnarium enters the battlefield tapped.$When Rakdos Carnarium enters the battlefield, return a land you control to its owner's hand.${T}: Add {B}{R}.| +Rocky Tar Pit|Commander 2018|274|U||Land|||Rocky Tar Pit enters the battlefield tapped.${T}, Sacrifice Rocky Tar Pit: Search your library for a Swamp or Mountain card and put it onto the battlefield. Then shuffle your library.| +Savage Lands|Commander 2018|275|U||Land|||Savage Lands enters the battlefield tapped.${T}: Add {B}, {R}, or {G}.| +Scoured Barrens|Commander 2018|276|C||Land|||Scoured Barrens enters the battlefield tapped.$When Scoured Barrens enters the battlefield, you gain 1 life.${T}: Add {W} or {B}.| +Seaside Citadel|Commander 2018|277|U||Land|||Seaside Citadel enters the battlefield tapped.${T}: Add {G}, {W}, or {U}.| +Seat of the Synod|Commander 2018|278|C||Artifact Land|||(Seat of the Synod isn't a spell.)${T}: Add {U}.| +Secluded Steppe|Commander 2018|279|C||Land|||Secluded Steppe enters the battlefield tapped.${T}: Add {W}.$Cycling {W}| +Sejiri Refuge|Commander 2018|280|U||Land|||Sejiri Refuge enters the battlefield tapped.$When Sejiri Refuge enters the battlefield, you gain 1 life.${T}: Add {W} or {U}.| +Selesnya Sanctuary|Commander 2018|281|C||Land|||Selesnya Sanctuary enters the battlefield tapped.$When Selesnya Sanctuary enters the battlefield, return a land you control to its owner's hand.${T}: Add {G}{W}.| +Simic Growth Chamber|Commander 2018|282|U||Land|||Simic Growth Chamber enters the battlefield tapped.$When Simic Growth Chamber enters the battlefield, return a land you control to its owner's hand.${T}: Add {G}{U}.| +Submerged Boneyard|Commander 2018|283|U||Land|||Submerged Boneyard enters the battlefield tapped.${T}: Add {U} or {B}.| +Swiftwater Cliffs|Commander 2018|284|C||Land|||Swiftwater Cliffs enters the battlefield tapped.$When Swiftwater Cliffs enters the battlefield, you gain 1 life.${T}: Add {U} or {R}.| +Temple of the False God|Commander 2018|285|U||Land|||{T}: Add {C}{C}. Activate this ability only if you control five or more lands.| +Terramorphic Expanse|Commander 2018|286|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card and put it onto the battlefield tapped. Then shuffle your library.| +Thornwood Falls|Commander 2018|287|C||Land|||Thornwood Falls enters the battlefield tapped.$When Thornwood Falls enters the battlefield, you gain 1 life.${T}: Add {G} or {U}.| +Tranquil Cove|Commander 2018|288|C||Land|||Tranquil Cove enters the battlefield tapped.$When Tranquil Cove enters the battlefield, you gain 1 life.${T}: Add {W} or {U}.| +Tranquil Expanse|Commander 2018|289|U||Land|||Tranquil Expanse enters the battlefield tapped.${T}: Add {G} or {W}.| +Tranquil Thicket|Commander 2018|290|C||Land|||Tranquil Thicket enters the battlefield tapped.${T}: Add {G}.$Cycling {G}| +Warped Landscape|Commander 2018|291|C||Land|||{T}: Add {C}.${2}, {T}, Sacrifice Warped Landscape: Search your library for a basic land card and put it onto the battlefield tapped. Then shuffle your library.| +Woodland Stream|Commander 2018|292|C||Land|||Woodland Stream enters the battlefield tapped.${T}: Add {G} or {U}.| +Plains|Commander 2018|293|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Commander 2018|294|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Commander 2018|295|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Commander 2018|296|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Commander 2018|297|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Commander 2018|298|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Commander 2018|299|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Commander 2018|300|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Commander 2018|301|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Commander 2018|302|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Commander 2018|303|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Commander 2018|304|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Commander 2018|305|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Commander 2018|306|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Commander 2018|307|C||Basic Land - Forest|||({T}: Add {G}.)| Bludgeoning Pain|Star Wars|300|C|{1}{B}|Instant|||Target creature gets -2/-2 until end of turn. Tap that creature.| Bor Gullet|Star Wars|301|U|{3}{U}{B}|Creature - Horror Cephalid|4|4|When Bor Gullet enters the battlefield, target opponent reveals his or her hand. You choose a card from it. That player discards that card.| Chirrut Imwe|Star Wars|302|R|{G}{W}{U}|Legendary Creature - Human Monk|3|3|Chirrut Imwe can block up to two additional creatures.${1}{W}: Prevent all combat damage that would be dealt to Chirrut Imwe until end of turn.|