diff --git a/Mage.Sets/src/mage/cards/g/GristThePlagueSwarm.java b/Mage.Sets/src/mage/cards/g/GristThePlagueSwarm.java new file mode 100644 index 00000000000..41a53240019 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GristThePlagueSwarm.java @@ -0,0 +1,163 @@ +package mage.cards.g; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentCard; +import mage.game.permanent.token.IzoniInsectToken; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTargets; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author Susucr + */ +public final class GristThePlagueSwarm extends CardImpl { + + public GristThePlagueSwarm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, ""); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.GRIST); + this.setStartingLoyalty(3); + + this.color.setBlack(true); + this.color.setGreen(true); + this.nightCard = true; + + // +1: Create a 1/1 black and green Insect creature token, then mill two cards. Put a deathtouch counter on the token if a black card was milled this way. + this.addAbility(new LoyaltyAbility(new GristThePlagueSwarmPlus1Effect(), 1)); + + // -2: Destroy target artifact or enchantment. + Ability ability = new LoyaltyAbility(new DestroyTargetEffect(), -2); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + this.addAbility(ability); + + // -6: For each creature card in your graveyard, create a token that's a copy of it, except it's a 1/1 black and green Insect. + this.addAbility(new LoyaltyAbility(new GristThePlagueSwarmMinus6Effect(), -6)); + } + + private GristThePlagueSwarm(final GristThePlagueSwarm card) { + super(card); + } + + @Override + public GristThePlagueSwarm copy() { + return new GristThePlagueSwarm(this); + } +} + +class GristThePlagueSwarmPlus1Effect extends OneShotEffect { + + GristThePlagueSwarmPlus1Effect() { + super(Outcome.PutCreatureInPlay); + staticText = "Create a 1/1 black and green Insect creature token, then mill two cards. " + + "Put a deathtouch counter on the token if a black card was milled this way."; + } + + private GristThePlagueSwarmPlus1Effect(final GristThePlagueSwarmPlus1Effect effect) { + super(effect); + } + + @Override + public GristThePlagueSwarmPlus1Effect copy() { + return new GristThePlagueSwarmPlus1Effect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + + // Create a 1/1 black and green Insect creature token + CreateTokenEffect effect = new CreateTokenEffect(new IzoniInsectToken()); + effect.apply(game, source); + + // Then mill two cards + Cards cards = controller.millCards(2, source, game); + + // Put a deathtouch counter on the token if a black card was milled this way. + if (cards.getCards(game).stream().anyMatch(card -> card.getColor(game).isBlack())) { + List tokens = effect + .getLastAddedTokenIds() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (!tokens.isEmpty()) { + Effect addEffect = new AddCountersTargetEffect(CounterType.DEATHTOUCH.createInstance()); + addEffect.setTargetPointer(new FixedTargets(tokens, game)); + addEffect.apply(game, source); + } + } + return true; + } +} + +class GristThePlagueSwarmMinus6Effect extends OneShotEffect { + + GristThePlagueSwarmMinus6Effect() { + super(Outcome.PutCreatureInPlay); + staticText = "For each creature card in your graveyard, create a token that's a copy of it, " + + "except it's a 1/1 black and green Insect."; + } + + private GristThePlagueSwarmMinus6Effect(final GristThePlagueSwarmMinus6Effect effect) { + super(effect); + } + + @Override + public GristThePlagueSwarmMinus6Effect copy() { + return new GristThePlagueSwarmMinus6Effect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Set cards = controller.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game); + if (cards.isEmpty()) { + return false; + } + for (Card card : cards) { + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect( + null, null, false, 1, false, + false, null, 1, 1, false + ); + effect.setSavedPermanent(new PermanentCard(card, controller.getId(), game)); + effect.setOnlyColor(new ObjectColor("BG")); + effect.setOnlySubType(SubType.INSECT); + effect.apply(game, source); + } + return true; + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GristVoraciousLarva.java b/Mage.Sets/src/mage/cards/g/GristVoraciousLarva.java new file mode 100644 index 00000000000..c07a23e5162 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GristVoraciousLarva.java @@ -0,0 +1,103 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Pronoun; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ExileAndReturnSourceEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class GristVoraciousLarva extends CardImpl { + + public GristVoraciousLarva(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.INSECT); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + this.secondSideCardClazz = GristThePlagueSwarm.class; + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever Grist, Voracious Larva or another creature enters the battlefield under your control, if it entered from your graveyard or was cast from your graveyard, you may pay {G}. If you do, exile Grist, then return it to the battlefield transformed under its owner's control. + this.addAbility(new TransformAbility()); + this.addAbility(new GristVoraciousLarvaTriggeredAbility()); + } + + private GristVoraciousLarva(final GristVoraciousLarva card) { + super(card); + } + + @Override + public GristVoraciousLarva copy() { + return new GristVoraciousLarva(this); + } +} + +class GristVoraciousLarvaTriggeredAbility extends EntersBattlefieldThisOrAnotherTriggeredAbility { + + GristVoraciousLarvaTriggeredAbility() { + super( + new DoIfCostPaid( + new ExileAndReturnSourceEffect(PutCards.BATTLEFIELD_TRANSFORMED, Pronoun.IT), + new ManaCostsImpl<>("{G}") + ), StaticFilters.FILTER_PERMANENT_CREATURE, false, true); + setTriggerPhrase("Whenever {this} or another creature enters the battlefield under your control, " + + "if it entered from your graveyard or was cast from your graveyard, "); + } + + private GristVoraciousLarvaTriggeredAbility(final GristVoraciousLarvaTriggeredAbility ability) { + super(ability); + } + + @Override + public GristVoraciousLarvaTriggeredAbility copy() { + return new GristVoraciousLarvaTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + EntersTheBattlefieldEvent zEvent = (EntersTheBattlefieldEvent) event; + if (zEvent == null) { + return false; + } + Permanent permanent = zEvent.getTarget(); + if (permanent == null) { + return false; + } + Zone fromZone = zEvent.getFromZone(); + boolean fromGraveyard = false; + if (fromZone == Zone.GRAVEYARD) { + // Directly from the graveyard + fromGraveyard = true; + } else if (fromZone == Zone.STACK) { + // Get spell in the stack. + Spell spell = game.getSpellOrLKIStack(permanent.getId()); + if (spell != null && spell.getFromZone() == Zone.GRAVEYARD) { + // Creature was cast from graveyard + fromGraveyard = true; + } + } + return fromGraveyard && super.checkTrigger(event, game); + } +} + diff --git a/Mage.Sets/src/mage/sets/ModernHorizons3.java b/Mage.Sets/src/mage/sets/ModernHorizons3.java index c3c84f8b06e..6186f011b3b 100644 --- a/Mage.Sets/src/mage/sets/ModernHorizons3.java +++ b/Mage.Sets/src/mage/sets/ModernHorizons3.java @@ -46,6 +46,8 @@ public final class ModernHorizons3 extends ExpansionSet { cards.add(new SetCardInfo("Forest", 308, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Frogmyr Enforcer", 120, Rarity.UNCOMMON, mage.cards.f.FrogmyrEnforcer.class)); cards.add(new SetCardInfo("Grim Servant", 97, Rarity.UNCOMMON, mage.cards.g.GrimServant.class)); + cards.add(new SetCardInfo("Grist, the Plague Swarm", 251, Rarity.MYTHIC, mage.cards.g.GristThePlagueSwarm.class)); + cards.add(new SetCardInfo("Grist, Voracious Larva", 251, Rarity.MYTHIC, mage.cards.g.GristVoraciousLarva.class)); cards.add(new SetCardInfo("Island", 305, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("It That Heralds the End", 9, Rarity.UNCOMMON, mage.cards.i.ItThatHeraldsTheEnd.class)); cards.add(new SetCardInfo("Jet Medallion", 292, Rarity.RARE, mage.cards.j.JetMedallion.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/GristVoraciousLarvaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/GristVoraciousLarvaTest.java new file mode 100644 index 00000000000..ef4207f1a29 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/GristVoraciousLarvaTest.java @@ -0,0 +1,327 @@ +package org.mage.test.cards.single.mh3; + +import mage.ObjectColor; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class GristVoraciousLarvaTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.g.GristVoraciousLarva Grist, Voracious Larva} {G} + * Legendary Creature — Insect + * Deathtouch + * Whenever Grist, Voracious Larva or another creature enters the battlefield under your control, if it entered from your graveyard or you cast it from your graveyard, you may pay {G}. If you do, exile Grist, then return it to the battlefield transformed under its owner’s control. + * 1/2 + * // {@link mage.cards.g.GristThePlagueSwarm Grist, the Plague Swarm} + * Legendary Planeswalker — Grist + * +1: Create a 1/1 black and green Insect creature token, then mill two cards. Put a deathtouch counter on the token if a black card was milled this way. + * −2: Destroy target artifact or enchantment. + * −6: For each creature card in your graveyard, create a token that’s a copy of it, except it’s a 1/1 black and green Insect. + * Loyalty: 3 + */ + private static final String grist = "Grist, Voracious Larva"; + private static final String gristPW = "Grist, the Plague Swarm"; + + @Test + public void test_Unearth_Trigger_NoMana() { + setStrictChooseMode(true); + + addCard(Zone.GRAVEYARD, playerA, grist); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.HAND, playerA, "Unearth"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unearth", grist); + setChoice(playerA, false); // can't pay for the trigger + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, grist, 1); + } + + @Test + public void test_Unearth_Trigger_Pay() { + setStrictChooseMode(true); + + addCard(Zone.GRAVEYARD, playerA, grist); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.HAND, playerA, "Unearth"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unearth", grist); + setChoice(playerA, true); // pay for the trigger + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, gristPW, 1); + } + + @Test + public void test_Unearth_Trigger_NoPay() { + setStrictChooseMode(true); + + addCard(Zone.GRAVEYARD, playerA, grist); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.HAND, playerA, "Unearth"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unearth", grist); + setChoice(playerA, false); // don't pay for the trigger + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, grist, 1); + } + + @Test + public void test_Bloodghast_Trigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, grist); + addCard(Zone.HAND, playerA, "Forest"); + addCard(Zone.GRAVEYARD, playerA, "Bloodghast"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); + setChoice(playerA, true); // yes to Bloodghast's trigger + setChoice(playerA, true); // pay for Grist's trigger + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, gristPW, 1); + } + + @Test + public void test_CastBloodghast_NoTrigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, grist); + addCard(Zone.BATTLEFIELD, playerA, "Bayou", 3); + addCard(Zone.HAND, playerA, "Bloodghast"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodghast"); + // no Grist's trigger + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, grist, 1); + assertPermanentCount(playerA, "Bloodghast", 1); + } + + @Test + public void test_CastGravecrawler_Trigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, grist); + addCard(Zone.BATTLEFIELD, playerA, "Bayou", 2); + addCard(Zone.BATTLEFIELD, playerA, "Gravecrawler"); + addCard(Zone.GRAVEYARD, playerA, "Gravecrawler"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gravecrawler"); + setChoice(playerA, true); // pay for Grist's trigger + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, gristPW, 1); + assertPermanentCount(playerA, "Gravecrawler", 2); + } + + @Test + public void test_Cast_NonCreature_NoTrigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, grist); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Muldrotha, the Gravetide"); // During each of your turns, you may play a land and cast a permanent spell of each permanent type from your graveyard. + addCard(Zone.GRAVEYARD, playerA, "Mox Jet"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mox Jet"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, grist, 1); + assertPermanentCount(playerA, "Mox Jet", 1); + } + + @Test + public void test_Play_DryadArbor_FromGraveyard_Trigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, grist); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Muldrotha, the Gravetide"); // During each of your turns, you may play a land and cast a permanent spell of each permanent type from your graveyard. + addCard(Zone.GRAVEYARD, playerA, "Dryad Arbor"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dryad Arbor"); + setChoice(playerA, "Land"); // Choose to consume the Land part of Muldrotha, "Creature" would have not worked anyway since Dryad Arbor can not be cast. + setChoice(playerA, true); // pay for Grist's trigger + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, gristPW, 1); + assertPermanentCount(playerA, "Dryad Arbor", 1); + } + + @Test + public void test_PlusOne_NoLibrary() { + setStrictChooseMode(true); + removeAllCardsFromLibrary(playerA); + + addCard(Zone.BATTLEFIELD, playerA, gristPW); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, 0); + assertPermanentCount(playerA, gristPW, 1); + assertCounterCount(playerA, gristPW, CounterType.LOYALTY, 3 + 1); + assertPermanentCount(playerA, "Insect Token", 1); + assertCounterCount(playerA, "Insect Token", CounterType.DEATHTOUCH, 0); + } + + @Test + public void test_PlusOne_MillNonBlack() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, gristPW); + addCard(Zone.LIBRARY, playerA, "Grizzly Bears", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, 2); + assertPermanentCount(playerA, gristPW, 1); + assertCounterCount(playerA, gristPW, CounterType.LOYALTY, 3 + 1); + assertPermanentCount(playerA, "Insect Token", 1); + assertCounterCount(playerA, "Insect Token", CounterType.DEATHTOUCH, 0); + } + + @Test + public void test_PlusOne_MillBlack() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, gristPW); + addCard(Zone.LIBRARY, playerA, "Blood Artist", 1); + addCard(Zone.LIBRARY, playerA, "Grizzly Bears", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, 2); + assertPermanentCount(playerA, gristPW, 1); + assertCounterCount(playerA, gristPW, CounterType.LOYALTY, 3 + 1); + assertPermanentCount(playerA, "Insect Token", 1); + assertCounterCount(playerA, "Insect Token", CounterType.DEATHTOUCH, 1); + } + + @Test + public void test_PlusOne_MillBlack_Chatterfang() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, gristPW); + addCard(Zone.BATTLEFIELD, playerA, "Chatterfang, Squirrel General"); // If one or more tokens would be created under your control, those tokens plus that many 1/1 green Squirrel creature tokens are created instead. + addCard(Zone.LIBRARY, playerA, "Blood Artist", 1); + addCard(Zone.LIBRARY, playerA, "Grizzly Bears", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, 2); + assertPermanentCount(playerA, gristPW, 1); + assertCounterCount(playerA, gristPW, CounterType.LOYALTY, 3 + 1); + assertPermanentCount(playerA, "Insect Token", 1); + assertCounterCount(playerA, "Insect Token", CounterType.DEATHTOUCH, 1); + assertPermanentCount(playerA, "Squirrel Token", 1); + assertCounterCount(playerA, "Squirrel Token", CounterType.DEATHTOUCH, 1); + } + + @Test + public void test_Minus6() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, gristPW); + addCard(Zone.GRAVEYARD, playerA, "Dryad Arbor", 1); + addCard(Zone.GRAVEYARD, playerA, "Bitterblossom", 1); + addCard(Zone.GRAVEYARD, playerA, "Taiga", 1); + addCard(Zone.GRAVEYARD, playerA, "Baneslayer Angel", 1); + addCard(Zone.GRAVEYARD, playerA, "Keranos, God of Storms", 1); + addCard(Zone.GRAVEYARD, playerA, "Grist, the Hunger Tide", 1); + + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, gristPW, CounterType.LOYALTY, 4); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-6"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, 6); // cards are still there + assertPermanentCount(playerA, 1 + 4); + assertCounterCount(playerA, gristPW, CounterType.LOYALTY, 1); + + // None of those are creature in graveyard + assertPermanentCount(playerA, "Bitterblossom", 0); + assertPermanentCount(playerA, "Taiga", 0); + + // All of those are creature in graveyard + assertPermanentCount(playerA, "Dryad Arbor", 1); + assertPermanentCount(playerA, "Baneslayer Angel", 1); + assertPermanentCount(playerA, "Keranos, God of Storms", 1); + assertPermanentCount(playerA, "Grist, the Hunger Tide", 1); + + // Dryad Arbor is still a Land Forest, but no longer Dryad + assertPowerToughness(playerA, "Dryad Arbor", 1, 1); + assertType("Dryad Arbor", CardType.CREATURE, true); + assertType("Dryad Arbor", CardType.LAND, true); + assertSubtype("Dryad Arbor", SubType.INSECT); + assertNotSubtype("Dryad Arbor", SubType.DRYAD); + assertSubtype("Dryad Arbor", SubType.FOREST); + assertColor(playerA, "Dryad Arbor", "{G}{B}", true); + + // Baneslayer Angel is now a Baneslayer Insect + assertPowerToughness(playerA, "Baneslayer Angel", 1, 1); + assertType("Baneslayer Angel", CardType.CREATURE, true); + assertSubtype("Baneslayer Angel", SubType.INSECT); + assertNotSubtype("Baneslayer Angel", SubType.ANGEL); + assertColor(playerA, "Baneslayer Angel", "{G}{B}", true); + assertColor(playerA, "Baneslayer Angel", ObjectColor.WHITE, false); + + // Keranos token is just an Enchantment + assertType("Keranos, God of Storms", CardType.CREATURE, false); + assertType("Keranos, God of Storms", CardType.ENCHANTMENT, true); + assertNotSubtype("Keranos, God of Storms", SubType.INSECT); + assertColor(playerA, "Keranos, God of Storms", "{G}{B}", true); + assertColor(playerA, "Baneslayer Angel", "{R}{U}", false); + + // Grist, the Hunger Tide is not a Creature (It would become a 1/1 Insect if animated somehow with an effect preserving its p/t/subtype) + assertType("Grist, the Hunger Tide", CardType.CREATURE, false); + assertType("Grist, the Hunger Tide", CardType.PLANESWALKER, true); + assertNotSubtype("Grist, the Hunger Tide", SubType.INSECT); + assertSubtype("Grist, the Hunger Tide", SubType.GRIST); + assertColor(playerA, "Grist, the Hunger Tide", "{G}{B}", true); + } +}