Implement [DSK] Valgavoth, Terror Eater (#13593)

This commit is contained in:
Susucre 2025-05-05 20:37:57 +02:00 committed by GitHub
parent 7cbd396e39
commit 507991b9e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 595 additions and 2 deletions

View file

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

View file

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

View file

@ -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
* WardSacrifice three nonland permanents.
* If a card you didnt control would be put into an opponents 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);
}
}

View file

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

View file

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