diff --git a/Mage.Sets/src/mage/cards/v/ValgavothTerrorEater.java b/Mage.Sets/src/mage/cards/v/ValgavothTerrorEater.java new file mode 100644 index 00000000000..f24eae3c3bc --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/ValgavothTerrorEater.java @@ -0,0 +1,221 @@ +package mage.cards.v; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardWithHalves; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class ValgavothTerrorEater extends CardImpl { + + public ValgavothTerrorEater(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{B}{B}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDER); + this.subtype.add(SubType.DEMON); + this.power = new MageInt(9); + this.toughness = new MageInt(9); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Ward--Sacrifice three nonland permanents. + this.addAbility(new WardAbility(new SacrificeTargetCost(3, StaticFilters.FILTER_PERMANENTS_NON_LAND))); + + // If a card you didn't control would be put into an opponent's graveyard from anywhere, exile it instead. + this.addAbility(new SimpleStaticAbility(new ValgavothTerrorEaterReplacementEffect())); + + // During your turn, you may play cards exiled with Valgavoth. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost. + this.addAbility(new SimpleStaticAbility(new ValgavothTerrorEaterPlayEffect()).setIdentifier(MageIdentifier.ValgavothTerrorEaterAlternateCast)); + } + + private ValgavothTerrorEater(final ValgavothTerrorEater card) { + super(card); + } + + @Override + public ValgavothTerrorEater copy() { + return new ValgavothTerrorEater(this); + } +} + +class ValgavothTerrorEaterReplacementEffect extends GraveyardFromAnywhereExileReplacementEffect { + + ValgavothTerrorEaterReplacementEffect() { + super(StaticFilters.FILTER_CARD_A, false); + staticText = "If a card you didn't control would be put into an opponent's graveyard from anywhere, exile it instead"; + } + + private ValgavothTerrorEaterReplacementEffect(final ValgavothTerrorEaterReplacementEffect effect) { + super(effect); + } + + @Override + public ValgavothTerrorEaterReplacementEffect copy() { + return new ValgavothTerrorEaterReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + + // TODO: part of #13594 refactor to replaceEvent (add exile zone info in ZoneChangeEvent?) + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null) { + return controller.moveCardsToExile( + permanent, source, game, true, + CardUtil.getCardExileZoneId(game, source), + CardUtil.createObjectRelatedWindowTitle(source, game, "(may be played using life)") + ); + } + + Card card = game.getCard(event.getTargetId()); + if (card != null) { + return controller.moveCardsToExile( + card, source, game, true, + CardUtil.getCardExileZoneId(game, source), + CardUtil.createObjectRelatedWindowTitle(source, game, "(may be played using life)")); + } + + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + // checks that zce movement is to graveyard + if (!super.applies(event, source, game)) { + return false; + } + + ZoneChangeEvent zce = (ZoneChangeEvent) event; + UUID controllerId = source.getControllerId(); + Player controller = game.getPlayer(controllerId); + if (controller == null) { + return false; + } + + Card card = game.getCard(event.getTargetId()); + if (card == null) { + return false; + } + + if (zce.getFromZone() == Zone.BATTLEFIELD) { + Permanent permanent = zce.getTarget(); + // is the permanent being moved controlled by someone that's not you? + if (permanent == null || permanent.getControllerId().equals(controllerId)) { + return false; + } + } else if (zce.getFromZone() == Zone.STACK) { + Spell spell = game.getSpellOrLKIStack(event.getTargetId()); + if (spell == null) { + // there is no direct link between moving a split/mdfc card and the stack part that was cast. + // so we try them both and see if we find anything. + if (card instanceof CardWithHalves) { + CardWithHalves cwh = (CardWithHalves) card; + spell = game.getSpellOrLKIStack(cwh.getLeftHalfCard().getId()); + if (spell == null) { + spell = game.getSpellOrLKIStack(cwh.getRightHalfCard().getId()); + } + } + } + // is the spell being moved controlled by someone that's not you? + if (spell == null || spell.getControllerId().equals(controllerId)) { + return false; + } + } + + // is the card going to an opponent's graveyard? + return controller.hasOpponent(card.getOwnerId(), game); + } +} + +class ValgavothTerrorEaterPlayEffect extends AsThoughEffectImpl { + + ValgavothTerrorEaterPlayEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.AIDontUseIt); // AI will need help with this + staticText = "during your turn, you may play cards exiled with {this}. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost"; + } + + private ValgavothTerrorEaterPlayEffect(final ValgavothTerrorEaterPlayEffect effect) { + super(effect); + } + + @Override + public ValgavothTerrorEaterPlayEffect copy() { + return new ValgavothTerrorEaterPlayEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (!source.isControlledBy(affectedControllerId) || !game.isActivePlayer(affectedControllerId)) { + return false; + } + Player player = game.getPlayer(affectedControllerId); + if (player == null) { + return false; + } + + Card card = game.getCard(objectId); + if (card == null) { + return false; + } + UUID mainId = card.getMainCard().getId(); // for split cards + + MageObject sourceObject = source.getSourceObject(game); + UUID exileZoneId = CardUtil.getExileZoneId(game, sourceObject.getId(), sourceObject.getZoneChangeCounter(game)); + ExileZone exileZone = game.getExile().getExileZone(exileZoneId); + if (exileZone == null || !exileZone.contains(mainId)) { + return false; + } + + // allows to play/cast with alternative life cost + if (!card.isLand(game)) { + PayLifeCost lifeCost = new PayLifeCost(card.getSpellAbility().getManaCosts().manaValue()); + Costs newCosts = new CostsImpl(); + newCosts.add(lifeCost); + newCosts.addAll(card.getSpellAbility().getCosts()); + player.setCastSourceIdWithAlternateMana(card.getId(), null, newCosts, MageIdentifier.ValgavothTerrorEaterAlternateCast); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java index e5f56b7f4bc..bbcb0411918 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java @@ -366,6 +366,7 @@ public final class DuskmournHouseOfHorror extends ExpansionSet { cards.add(new SetCardInfo("Valgavoth's Lair", 327, Rarity.RARE, mage.cards.v.ValgavothsLair.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Valgavoth's Onslaught", 204, Rarity.RARE, mage.cards.v.ValgavothsOnslaught.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Valgavoth's Onslaught", 324, Rarity.RARE, mage.cards.v.ValgavothsOnslaught.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Valgavoth, Terror Eater", 120, Rarity.MYTHIC, mage.cards.v.ValgavothTerrorEater.class)); cards.add(new SetCardInfo("Vanish from Sight", 82, Rarity.COMMON, mage.cards.v.VanishFromSight.class)); cards.add(new SetCardInfo("Vengeful Possession", 162, Rarity.UNCOMMON, mage.cards.v.VengefulPossession.class)); cards.add(new SetCardInfo("Veteran Survivor", 40, Rarity.UNCOMMON, mage.cards.v.VeteranSurvivor.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dsk/ValgavothTerrorEaterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dsk/ValgavothTerrorEaterTest.java new file mode 100644 index 00000000000..dfa61239f2b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dsk/ValgavothTerrorEaterTest.java @@ -0,0 +1,369 @@ +package org.mage.test.cards.single.dsk; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class ValgavothTerrorEaterTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.v.ValgavothTerrorEater Valgavoth, Terror Eater} {6}{B}{B}{B} + * Legendary Creature — Elder Demon + * Flying, lifelink + * Ward—Sacrifice three nonland permanents. + * If a card you didn’t control would be put into an opponent’s graveyard from anywhere, exile it instead. + * During your turn, you may play cards exiled with Valgavoth. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost. + * 9/9 + */ + private static final String valgavoth = "Valgavoth, Terror Eater"; + + /** + * Donate {2}{U} Sorcery + * Target player gains control of target permanent you control. + */ + private static final String donate = "Donate"; + private static final String vanguard = "Elite Vanguard"; + private static final String piker = "Goblin Piker"; + private static final String bear = "Grizzly Bears"; + /** + * Lightning Bolt {R} Instant + * Lightning Bolt deals 3 damage to any target. + */ + private static final String bolt = "Lightning Bolt"; + /** + * Fire // Ice + * Fire {1}{R} Instant + * Fire deals 2 damage divided as you choose among one or two targets. + * Ice {1}{U} Instant + * Tap target permanent. + * Draw a card. + */ + private static final String fireice = "Fire // Ice"; + private static final String fire = "Fire"; + private static final String ice = "Ice"; + /** + * Cloudshift {W} Instant + * Exile target creature you control, then return that card to the battlefield under your control. + */ + private static final String cloudshift = "Cloudshift"; + /** + * Pyroclasm {1}{R} Sorcery + * Pyroclasm deals 2 damage to each creature. + */ + private static final String pyroclasm = "Pyroclasm"; + /** + * Legerdemain {2}{U}{U} Sorcery + * Exchange control of target artifact or creature and another target permanent that shares one of those types with it. + */ + private static final String legerdemain = "Legerdemain"; + /** + * Tome Scour {U} Sorcery + * Target player mills five cards. + */ + private static final String scour = "Tome Scour"; + + /** + * Akoum Warrior // Akoum Teeth + * Akoum Warrior {5}{R} + * Creature — Minotaur Warrior + * Trample + * 4/5 + * Akoum Teeth + * Land + * This land enters tapped. + * {T}: Add {R}. + */ + private static final String akoum = "Akoum Warrior // Akoum Teeth"; + private static final String warrior = "Akoum Warrior"; + private static final String teeth = "Akoum Teeth"; + + @Test + public void testOwnBolts() { + addCard(Zone.BATTLEFIELD, playerA, valgavoth, 1); + addCard(Zone.HAND, playerA, bolt, 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerB, vanguard, 1); + addCard(Zone.BATTLEFIELD, playerA, piker, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, vanguard); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, piker); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertExileCount(playerB, vanguard, 1); + assertGraveyardCount(playerA, bolt, 3); + assertGraveyardCount(playerA, piker, 1); + assertLife(playerB, 20 - 3); + } + + @Test + public void testOpponentBolts() { + addCard(Zone.BATTLEFIELD, playerA, valgavoth, 1); + addCard(Zone.HAND, playerB, bolt, 3); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerB, vanguard, 1); + addCard(Zone.BATTLEFIELD, playerA, piker, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, vanguard); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, piker); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertExileCount(playerB, vanguard, 1); + assertExileCount(playerB, bolt, 3); + assertGraveyardCount(playerA, piker, 1); + assertLife(playerB, 20 - 3); + } + + @Test + public void testAfterLegerdemainOthersA() { + addCard(Zone.BATTLEFIELD, playerA, valgavoth, 1); + addCard(Zone.HAND, playerA, pyroclasm, 1); + addCard(Zone.HAND, playerA, legerdemain, 1); + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 6); + addCard(Zone.BATTLEFIELD, playerB, vanguard, 1); + addCard(Zone.BATTLEFIELD, playerA, piker, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, legerdemain, vanguard + "^" + piker); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroclasm); + // playerA controls vanguard, so it is not exiled + // piker goes into playerA's graveyard, so it is not exiled either + // pyroclasm and legerdemain have been cast by playerA, so they are not exiled. + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertExileCount(playerA, 0); + assertExileCount(playerB, 0); + assertGraveyardCount(playerA, 3); + assertGraveyardCount(playerB, 1); + } + + @Test + public void testAfterLegerdemainOthersB() { + addCard(Zone.BATTLEFIELD, playerB, valgavoth, 1); + addCard(Zone.HAND, playerA, pyroclasm, 1); + addCard(Zone.HAND, playerA, legerdemain, 1); + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 6); + addCard(Zone.BATTLEFIELD, playerB, vanguard, 1); + addCard(Zone.BATTLEFIELD, playerA, piker, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, legerdemain, vanguard + "^" + piker); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroclasm); + // playerB controls piker, so it is not exiled + // vanguard goes into playerB's graveyard, so it is not exiled either + // pyroclasm and legerdemain have been cast by playerA, so they are exiled. + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertExileCount(playerA, 2); + assertExileCount(playerB, 0); + assertGraveyardCount(playerA, 1); + assertGraveyardCount(playerB, 1); + } + + @Test + public void testAfterLegerdemainOthersDonate() { + addCard(Zone.BATTLEFIELD, playerA, valgavoth, 1); + addCard(Zone.HAND, playerA, donate, 1); + addCard(Zone.HAND, playerA, pyroclasm, 1); + addCard(Zone.HAND, playerA, legerdemain, 1); + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 9); + addCard(Zone.BATTLEFIELD, playerB, vanguard, 1); + addCard(Zone.BATTLEFIELD, playerA, piker, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, donate, playerB); + addTarget(playerA, valgavoth); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + // Donate ends up exiled as valgavoth is in control of playerB as it finishes resolving + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, legerdemain, vanguard + "^" + piker); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroclasm); + // playerB controls piker, so it is not exiled + // vanguard goes into playerB's graveyard, so it is not exiled either + // pyroclasm and legerdemain have been cast by playerA, so they are exiled. + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertExileCount(playerA, 3); + assertExileCount(playerB, 0); + assertGraveyardCount(playerA, 1); + assertGraveyardCount(playerB, 1); + } + + @Test + public void testMillSelf() { + addCard(Zone.BATTLEFIELD, playerA, valgavoth, 1); + addCard(Zone.HAND, playerA, scour, 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, scour, playerA); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertExileCount(playerA, 0); + assertExileCount(playerB, 0); + assertGraveyardCount(playerA, 6); + } + + @Test + public void testMillOpponent() { + addCard(Zone.BATTLEFIELD, playerA, valgavoth, 1); + addCard(Zone.HAND, playerA, scour, 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, scour, playerB); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertExileCount(playerA, 0); + assertExileCount(playerB, 5); + assertGraveyardCount(playerA, 1); + } + + @Test + public void testCastTimingAndSplitCard() { + addCard(Zone.BATTLEFIELD, playerA, valgavoth, 1); + addCard(Zone.HAND, playerB, fireice); + addCard(Zone.HAND, playerB, bolt); + addCard(Zone.BATTLEFIELD, playerB, "Volcanic Island", 6); + + castSpell(1, PhaseStep.UPKEEP, playerB, bolt, playerA); + checkPlayableAbility("1: Can cast bolt PlA", 1, PhaseStep.END_TURN, playerA, "Cast " + bolt, true); + checkPlayableAbility("1: Can not cast bolt PlB", 1, PhaseStep.END_TURN, playerB, "Cast " + bolt, false); + checkPlayableAbility("1: Can not cast fire PlA", 1, PhaseStep.END_TURN, playerA, "Cast " + fire, false); + checkPlayableAbility("1: Can cast fire PlB", 1, PhaseStep.END_TURN, playerB, "Cast " + fire, true); + checkPlayableAbility("1: Can not cast ice PlA", 1, PhaseStep.END_TURN, playerA, "Cast " + ice, false); + checkPlayableAbility("1: Can cast ice PlB", 1, PhaseStep.END_TURN, playerB, "Cast " + ice, true); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, fire, playerA); + addTargetAmount(playerB, playerA, 2); + checkPlayableAbility("2: Can not cast bolt PlA", 2, PhaseStep.END_TURN, playerA, "Cast " + bolt, false); + checkPlayableAbility("2: Can not cast bolt PlB", 2, PhaseStep.END_TURN, playerB, "Cast " + bolt, false); + checkPlayableAbility("2: Can not cast fire PlA", 2, PhaseStep.END_TURN, playerA, "Cast " + fire, false); + checkPlayableAbility("2: Can not cast fire PlB", 2, PhaseStep.END_TURN, playerB, "Cast " + fire, false); + checkPlayableAbility("2: Can not cast ice PlA", 2, PhaseStep.END_TURN, playerA, "Cast " + ice, false); + checkPlayableAbility("2: Can not cast ice PlB", 2, PhaseStep.END_TURN, playerB, "Cast " + ice, false); + + waitStackResolved(3, PhaseStep.UPKEEP); + checkPlayableAbility("3: Can cast bolt PlA", 3, PhaseStep.END_TURN, playerA, "Cast " + bolt, true); + checkPlayableAbility("3: Can not cast bolt PlB", 2, PhaseStep.END_TURN, playerB, "Cast " + bolt, false); + checkPlayableAbility("3: Can cast fire PlA", 3, PhaseStep.END_TURN, playerA, "Cast " + fire, true); + checkPlayableAbility("3: Can not cast fire PlB", 2, PhaseStep.END_TURN, playerB, "Cast " + fire, false); + checkPlayableAbility("3: Can cast ice PlA", 3, PhaseStep.END_TURN, playerA, "Cast " + ice, true); + checkPlayableAbility("3: Can not cast ice PlB", 2, PhaseStep.END_TURN, playerB, "Cast " + ice, false); + castSpell(3, PhaseStep.END_TURN, playerA, bolt, playerB); + castSpell(3, PhaseStep.END_TURN, playerA, fire, playerB); + addTargetAmount(playerA, playerB, 2); + + setStopAt(4, PhaseStep.UPKEEP); + setStrictChooseMode(true); + execute(); + + assertGraveyardCount(playerA, 0); + assertGraveyardCount(playerB, 2); + assertExileCount(playerA, 0); + assertExileCount(playerB, 0); + + assertLife(playerA, 20 - 3 - 2 - 1 - 2); + assertLife(playerB, 20 - 3 - 2); + } + + @Test + public void testCastMDFC() { + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, valgavoth, 1); + addCard(Zone.LIBRARY, playerB, akoum, 5); + addCard(Zone.HAND, playerA, scour); + addCard(Zone.BATTLEFIELD, playerA, "Island"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, scour, playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPlayableAbility("1: Can cast Akoum Warrior", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + warrior, true); + checkPlayableAbility("1: Can play Akoum Teeth", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play " + teeth, true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, warrior); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, warrior); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, warrior); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + checkPlayableAbility("2: Can not cast Akoum Warrior (not enough life)", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + warrior, false); + checkPlayableAbility("2: Can play Akoum Teeth", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play " + teeth, true); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, teeth); + checkPlayableAbility("3: Can not cast Akoum Warrior (not enough life)", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + warrior, false); + checkPlayableAbility("3: Can not play Akoum Teeth (no land drop left)", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play " + teeth, false); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertExileCount(playerA, 0); + assertExileCount(playerB, 1); + + assertLife(playerA, 20 - 6 * 3); + assertPermanentCount(playerA, warrior, 3); + assertPermanentCount(playerA, teeth, 1); + } + + @Test + public void testWardAndBlink() { + addCard(Zone.BATTLEFIELD, playerA, valgavoth, 1); + addCard(Zone.BATTLEFIELD, playerB, bear, 1); + addCard(Zone.BATTLEFIELD, playerB, vanguard, 1); + addCard(Zone.BATTLEFIELD, playerB, piker, 1); + addCard(Zone.HAND, playerB, bolt, 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerA, cloudshift, 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, valgavoth); + setChoice(playerB, true); // pay for ward? + setChoice(playerB, bear + "^" + vanguard + "^" + piker); // ward sacrifices + + checkPlayableAbility("1: Can cast bear", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast " + bear, true); + checkPlayableAbility("1: Can cast vanguard", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast " + vanguard, true); + checkPlayableAbility("1: Can cast piker", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast " + piker, true); + checkPlayableAbility("1: Can cast bolt", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast " + bolt, true); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, cloudshift, valgavoth); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + + // after blink, nothing can be cast + checkPlayableAbility("2: Can not cast bear", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast " + bear, false); + checkPlayableAbility("2: Can not cast vanguard", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast " + vanguard, false); + checkPlayableAbility("2: Can not cast piker", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast " + piker, false); + checkPlayableAbility("2: Can not cast bolt", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast " + bolt, false); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertExileCount(playerA, 0); + assertExileCount(playerB, 4); + assertGraveyardCount(playerA, 1); + } +} diff --git a/Mage/src/main/java/mage/MageIdentifier.java b/Mage/src/main/java/mage/MageIdentifier.java index b7e9c8014f8..fd60b2e5524 100644 --- a/Mage/src/main/java/mage/MageIdentifier.java +++ b/Mage/src/main/java/mage/MageIdentifier.java @@ -79,7 +79,8 @@ public enum MageIdentifier { FiresOfMountDoomAlternateCast, PrimalPrayersAlternateCast, QuilledGreatwurmAlternateCast, - WickerfolkIndomitableAlternateCast; + WickerfolkIndomitableAlternateCast, + ValgavothTerrorEaterAlternateCast; /** * Additional text if there is need to differentiate two very similar effects diff --git a/Mage/src/main/java/mage/abilities/effects/common/replacement/GraveyardFromAnywhereExileReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/replacement/GraveyardFromAnywhereExileReplacementEffect.java index 979c26e60a6..36c1366d748 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/replacement/GraveyardFromAnywhereExileReplacementEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/replacement/GraveyardFromAnywhereExileReplacementEffect.java @@ -35,6 +35,7 @@ public class GraveyardFromAnywhereExileReplacementEffect extends ReplacementEffe public GraveyardFromAnywhereExileReplacementEffect(Duration duration) { this(duration, StaticFilters.FILTER_CARD_A, true, false); } + protected GraveyardFromAnywhereExileReplacementEffect(Duration duration, FilterCard filter, boolean onlyYou, boolean tokens) { super(duration, Outcome.Exile); this.filter = filter; @@ -43,7 +44,7 @@ public class GraveyardFromAnywhereExileReplacementEffect extends ReplacementEffe this.setText(); } - private GraveyardFromAnywhereExileReplacementEffect(final GraveyardFromAnywhereExileReplacementEffect effect) { + protected GraveyardFromAnywhereExileReplacementEffect(final GraveyardFromAnywhereExileReplacementEffect effect) { super(effect); this.filter = effect.filter; this.onlyYou = effect.onlyYou;