[NCC] Implement several cards (#9328)

Many associated refactors too. See full PR for detail.
This commit is contained in:
Alex Vasile 2022-09-22 21:38:29 -04:00 committed by GitHub
parent b7151cfa58
commit fd16f2a16b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
104 changed files with 6091 additions and 1069 deletions

View file

@ -0,0 +1,110 @@
package org.mage.test.cards.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Alex-Vasile
*/
public class CasualtyTest extends CardTestPlayerBase {
// Instant
// {1}{U}
// Casualty 1
// Look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library.
private static final String aLittleChat = "A Little Chat";
// Planeswalker
// {1}{B}{R}
// Casualty X
// The copy isnt legendary and has starting loyalty X.
// 7: Target player draws seven cards and loses 7 life.
private static final String obNixilisTheAdversary = "Ob Nixilis, the Adversary";
// 7/7 used as casualty
private static final String aetherwindBasker = "Aetherwind Basker";
/**
* Test Casualty on sorcery/instant.
*/
@Test
public void testCasualtySorceryInstant() {
addCard(Zone.BATTLEFIELD, playerA, aetherwindBasker);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.HAND, playerA, aLittleChat);
addCard(Zone.LIBRARY, playerA, "Desert", 4);
setStrictChooseMode(true);
skipInitShuffling();
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, aLittleChat);
setChoice(playerA, "Yes");
setChoice(playerA, aetherwindBasker);
addTarget(playerA, "Desert");
addTarget(playerA, "Desert");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertHandCount(playerA, "Desert", 2);
assertGraveyardCount(playerA, aetherwindBasker, 1);
}
/**
* Test that casualty will only let you pay it once.
*/
@Test
public void testCanOnlyPayCasualtyOnce() {
addCard(Zone.BATTLEFIELD, playerA, aetherwindBasker, 2);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.HAND, playerA, aLittleChat);
addCard(Zone.LIBRARY, playerA, "Desert", 4);
setStrictChooseMode(true);
skipInitShuffling();
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, aLittleChat);
setChoice(playerA, "Yes");
setChoice(playerA, aetherwindBasker);
// If a second target was possible, it would have prompted us for another and this test would fail when strict choose mode was on
addTarget(playerA, "Desert");
addTarget(playerA, "Desert");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertHandCount(playerA, "Desert", 2);
assertGraveyardCount(playerA, aetherwindBasker, 1);
assertPermanentCount(playerA, aetherwindBasker, 1);
}
/**
* Test Casualty on a creature.
* Test variable casualty.
*/
@Test
public void testVariableCasualtyOnCreature() {
addCard(Zone.BATTLEFIELD, playerA, aetherwindBasker);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.HAND, playerA, obNixilisTheAdversary);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, obNixilisTheAdversary);
setChoice(playerA, aetherwindBasker);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertGraveyardCount(playerA, aetherwindBasker, 1);
assertPermanentCount(playerA, obNixilisTheAdversary, 2); // 2 were created, but the token died when using its -7 ability
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "-7:"); // -7 life and draw 7 cards
addTarget(playerA, playerA);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, obNixilisTheAdversary, 1);
assertLife(playerA, 20 - 7);
assertHandCount(playerA, 7);
}
}

View file

@ -0,0 +1,192 @@
package org.mage.test.cards.abilities.oneshot;
import mage.abilities.keyword.HasteAbility;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Alex-Vasile
*/
public class PutCardFromOneOfTwoZonesOntoBattlefieldEffectTest extends CardTestPlayerBase {
// 5: You may put a creature card with mana value less than or equal to the number of lands you control onto the battlefield from your hand or graveyard with two +1/+1 counters on it.
private static final String nissa = "Nissa of Shadowed Boughs";
// {4}{B}{R}
// When Swift Warkite enters the battlefield, you may put a creature card with mana value 3 or less from your hand or graveyard onto the battlefield.
// That creature gains haste.
// Return it to your hand at the beginning of the next end step.
private static final String swift = "Swift Warkite";
// Simple 1/1 for Swift Warkite to put on the battlefield with its ETB
private static final String sliver = "Metallic Sliver";
// 2: You may put an Equipment card from your hand or graveyard onto the battlefield.
private static final String nahiri = "Nahiri, the Lithomancer";
// Equipment cards for Nahiri
private static final String vorpal = "Vorpal Sword";
private static final String axe = "Bloodforged Battle-Axe";
/**
* Test with no matching cards in hand or graveyard.
*/
@Test
public void testNoMatches() {
addCard(Zone.BATTLEFIELD, playerA, nahiri);
setStrictChooseMode(true);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2");
// The player should not be prompted for a choice
setStopAt(1, PhaseStep.END_TURN);
execute();
}
/**
* Test with matching cards only in graveyard.
*/
@Test
public void testOnlyGraveyardHasMatches() {
addCard(Zone.BATTLEFIELD, playerA, nahiri);
addCard(Zone.GRAVEYARD, playerA, vorpal);
setStrictChooseMode(true);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2");
setChoice(playerA, vorpal);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, vorpal, 1);
}
/**
* Test with matching cards only in hand.
*/
@Test
public void testOnlyHandHasMatches() {
addCard(Zone.BATTLEFIELD, playerA, nahiri);
addCard(Zone.HAND, playerA, vorpal);
setStrictChooseMode(true);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2");
setChoice(playerA, vorpal);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, vorpal, 1);
}
/**
* Test with matching cards in both hand and graveyard.
*/
@Test
public void testBothHandAndGraveyardHaveMatches() {
addCard(Zone.BATTLEFIELD, playerA, nahiri);
addCard(Zone.HAND, playerA, vorpal);
addCard(Zone.GRAVEYARD, playerA, axe);
setStrictChooseMode(true);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2");
setChoice(playerA, "Yes"); // For player this choice is "Hand" but tests require "Yes"
setChoice(playerA, vorpal);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, vorpal, 1);
}
/**
* Test {@link mage.cards.n.NissaOfShadowedBoughs Nissa of Shadowed Boughs}
* Starting loyalty = 4
* You may put a creature card with mana value less than or equal to the number of lands you control
* onto the battlefield from your hand or graveyard
* with two +1/+1 counters on it.
*/
@Test
public void testNissaCanPlay() {
addCard(Zone.BATTLEFIELD, playerA, nissa);
addCard(Zone.HAND, playerA, swift); // {4}{B}{R}
addCard(Zone.HAND, playerA, "Mountain", 5);
addCard(Zone.HAND, playerA, "Mountain");
setStrictChooseMode(true);
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "-5");
setChoice(playerA, swift);
setChoice(playerA, "Yes"); // Say yes to Swift Warkite's ETB (no further choice needed since there are no possible options
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, nissa, 1);
assertPermanentCount(playerA, swift, 1);
assertCounterCount(swift, CounterType.P1P1, 2);
}
/**
* Test {@link mage.cards.n.NissaOfShadowedBoughs Nissa of Shadowed Boughs}
* Starting loyalty = 4
* You may put a creature card with mana value less than or equal to the number of lands you control
* onto the battlefield from your hand or graveyard
* with two +1/+1 counters on it.
*/
@Test
public void testNissaCantPlay() {
addCard(Zone.BATTLEFIELD, playerA, nissa);
addCard(Zone.HAND, playerA, swift); // {4}{B}{R}
addCard(Zone.HAND, playerA, "Mountain");
setStrictChooseMode(true);
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "-5");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, nissa, 1);
}
/**
* Test {@link mage.cards.s.SwiftWarkite Swift Warkite}
* When Swift Warkite enters the battlefield, you may put a creature card with converted mana cost 3 or less from your hand or graveyard onto the battlefield.
* That creature gains haste.
* Return it to your hand at the beginning of the next end step.
*/
@Test
public void testSwiftWarkite() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
addCard(Zone.HAND, playerA, swift);
addCard(Zone.HAND, playerA, sliver);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, swift);
setChoice(playerA, "Yes"); // Yes to activating Swift Warkite's ETB
setChoice(playerA, sliver); // Pick the sliver for the ETB
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, sliver, 1);
assertAbility(playerA, sliver, HasteAbility.getInstance(), true);
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
execute();
assertHandCount(playerA, sliver, 1);
}
}

View file

@ -0,0 +1,79 @@
package org.mage.test.cards.single.gpt;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* {@link mage.cards.d.DjinnIlluminatus Djinn Illuminatus}
* <p>
* Each instant and sorcery spell you cast has replicate.
* The replicate cost is equal to its mana cost.
* (When you cast it, copy it for each time you paid its replicate cost. You may choose new targets for the copies.)
*
* @author Alex-Vasile
*/
public class DjinnIlluminatusTest extends CardTestPlayerBase {
private static final String djinnIlluminatus = "Djinn Illuminatus";
private static final String lightningBolt = "Lightning Bolt";
private static final String mountain = "Mountain";
/**
* Test that it works for you spells on your turn.
*/
@Test
public void testYourSpellYourTurn() {
addCard(Zone.BATTLEFIELD, playerA, djinnIlluminatus);
addCard(Zone.BATTLEFIELD, playerA, mountain, 2);
addCard(Zone.HAND, playerA, lightningBolt);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, playerB);
setChoice(playerA, "Yes"); // Replicate
setChoice(playerA, "No"); // Only replicate once
setChoice(playerA, "No"); // Don't change the target
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertLife(playerB, 14);
}
/**
* Test that it works for your spell on other's turn.
*/
@Test
public void testYourSpellNotYourTurn() {
addCard(Zone.BATTLEFIELD, playerA, djinnIlluminatus);
addCard(Zone.BATTLEFIELD, playerA, mountain, 2);
addCard(Zone.HAND, playerA, lightningBolt);
setStrictChooseMode(true);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, playerB);
setChoice(playerA, "Yes"); // Replicate
setChoice(playerA, "No"); // Only replicate once
setChoice(playerA, "No"); // Don't change the target
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
execute();
assertLife(playerB, 14);
}
/**
* Test that it does not copy other's spell.
*/
@Test
public void testOthersSpell() {
addCard(Zone.BATTLEFIELD, playerA, djinnIlluminatus);
addCard(Zone.BATTLEFIELD, playerB, mountain, 2);
addCard(Zone.HAND, playerB, lightningBolt);
setStrictChooseMode(true);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, lightningBolt, playerA);
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
execute();
assertLife(playerA, 17);
}
}

View file

@ -0,0 +1,198 @@
package org.mage.test.cards.single.ncc;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import java.util.UUID;
/**
* {@link mage.cards.a.AnheloThePainter Anhelo, the Painter}
* The first instant or sorcery spell you cast each turn has casualty 2.
* (As you cast that spell, you may sacrifice a creature with power 2 or greater.
* When you do, copy the spell and you may choose new targets for the copy.)
*
* @author Alex-Vasile
*/
public class AnheloTest extends CardTestPlayerBase {
private static final String anhelo = "Anhelo, the Painter"; // {U}{B}{R}
private static final String lightningBolt = "Lightning Bolt"; // {R}
private static final String solRing = "Sol Ring"; // {1}
private static final String mountain = "Mountain";
// MDFC CreatureInstant
private static final String flamescrollCelebrant = "Flamescroll Celebrant"; // {1}{R}
private static final String revelInSilence = "Revel in Silence"; // {W}{W}
// 7/7 used as casualty
private static final String aetherwindBasker = "Aetherwind Basker";
// Instant
// {1}{U}
// Casualty 1
// Look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library.
private static final String aLittleChat = "A Little Chat";
/**
* Test that it works for sorcery, but only the first one.
*/
@Test
public void testWorksForFirstOnly() {
addCard(Zone.BATTLEFIELD, playerA, anhelo);
addCard(Zone.BATTLEFIELD, playerA, mountain, 2);
addCard(Zone.BATTLEFIELD, playerA, aetherwindBasker, 2);
addCard(Zone.HAND, playerA, lightningBolt, 2);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, playerB);
setChoice(playerA, "Yes"); // Cast with Casualty
setChoice(playerA, aetherwindBasker);
setChoice(playerA, "No"); // Don't change targets
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, playerB);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20 - 2*3 - 3);
assertPermanentCount(playerA, aetherwindBasker, 1);
}
/**
* Test that it does not trigger for non-sorcery/instant.
*/
@Test
public void testNonSorceryOrInstant() {
addCard(Zone.BATTLEFIELD, playerA, anhelo);
addCard(Zone.BATTLEFIELD, playerA, mountain);
addCard(Zone.BATTLEFIELD, playerA, aetherwindBasker);
addCard(Zone.HAND, playerA, solRing);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, solRing);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, solRing, 1);
assertPermanentCount(playerA, aetherwindBasker, 1);
}
/**
* Test that the instant side of an MDFC gains casualty
*/
@Test
public void testInstantSideMDFC() {
addCard(Zone.BATTLEFIELD, playerA, anhelo);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerA, aetherwindBasker);
addCard(Zone.HAND, playerA, flamescrollCelebrant);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, revelInSilence);
setChoice(playerA, "Yes"); // Cast with Casualty
setChoice(playerA, aetherwindBasker);
// Spell has no targets, so not prompted to change them
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, aetherwindBasker, 0);
}
/**
* Test that the non-instant side of an MDFC (one which has an instant on the other side) does NOT gain casualty.
*/
@Test
public void testNonInstantSideMDFC() {
addCard(Zone.BATTLEFIELD, playerA, anhelo);
addCard(Zone.BATTLEFIELD, playerA, mountain, 2);
addCard(Zone.BATTLEFIELD, playerA, aetherwindBasker);
addCard(Zone.HAND, playerA, flamescrollCelebrant);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flamescrollCelebrant);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, aetherwindBasker, 1);
assertPermanentCount(playerA, flamescrollCelebrant, 1);
}
/**
* Test that it works for one you cast on someone else's turn.
*/
@Test
public void testOnNotOwnTurn() {
addCard(Zone.BATTLEFIELD, playerA, anhelo);
addCard(Zone.BATTLEFIELD, playerA, mountain, 2);
addCard(Zone.BATTLEFIELD, playerA, aetherwindBasker, 2);
addCard(Zone.HAND, playerA, lightningBolt, 2);
setStrictChooseMode(true);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, playerB);
setChoice(playerA, "Yes"); // Cast with Casualty
setChoice(playerA, aetherwindBasker);
setChoice(playerA, "No"); // Don't change targets
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, playerB);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20 - 2*3 - 3);
assertPermanentCount(playerA, aetherwindBasker, 1);
}
/**
* Test that a card which already has Casualty will gain a second instance of Casualty and thus let you sacrifice twice in order to get 2 copies.
*/
@Test
public void testGainsSecondCasualty() {
addCard(Zone.BATTLEFIELD, playerA, anhelo);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerA, aetherwindBasker, 2);
addCard(Zone.HAND, playerA, aLittleChat);
addCard(Zone.LIBRARY, playerA, "Desert", 6);
setStrictChooseMode(true);
skipInitShuffling();
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, aLittleChat);
setChoice(playerA, "Yes"); // First instance of casualty
setChoice(playerA, "Yes"); // Second instance of casualty
setChoice(playerA, aetherwindBasker);
setChoice(playerA, aetherwindBasker);
setChoice(playerA, "When you do"); // Chose which of the two copies to put on the stack first
addTarget(playerA, "Desert");
addTarget(playerA, "Desert");
addTarget(playerA, "Desert");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertHandCount(playerA, 3);
assertPermanentCount(playerA, aetherwindBasker, 0);
}
/**
* Test that it does not work for opponents on your turn or on their.
*/
@Test
public void testOpponentCasts() {
addCard(Zone.BATTLEFIELD, playerA, anhelo);
addCard(Zone.BATTLEFIELD, playerB, mountain, 2);
addCard(Zone.BATTLEFIELD, playerB, aetherwindBasker, 2);
addCard(Zone.HAND, playerB, lightningBolt, 2);
setStrictChooseMode(true);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, lightningBolt, playerA);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, lightningBolt, playerA);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertLife(playerA, 20 - 3 - 3);
assertPermanentCount(playerB, aetherwindBasker, 2);
}
}

View file

@ -0,0 +1,83 @@
package org.mage.test.cards.single.ncc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* {@link mage.cards.b.BessSoulNourisher Bess, Soul Nourisher}
* <p>
* Whenever one or more other creatures with base power and toughness 1/1 enter the battlefield under your control,
* put a +1/+1 counter on Bess, Soul Nourisher.
* <p>
* Whenever Bess attacks, each other creature you control with base power and toughness 1/1 gets +X/+X until end of turn,
* where X is the number of +1/+1 counters on Bess.
*
* @author Alex-Vasile
*/
public class BessSoulNourisherTest extends CardTestPlayerBase {
// {1}{G}{W}
private static final String bessSoulNourisher = "Bess, Soul Nourisher";
// {3}{W}
// Create three 1/1 white Soldier creature tokens.
private static final String captainsCall = "Captain's Call";
/**
* Test that it only triggers once for a group entering
*/
@Test
public void testEntersGroup() {
addCard(Zone.BATTLEFIELD, playerA, bessSoulNourisher);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.HAND, playerA, captainsCall);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, captainsCall);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Soldier Token", 3);
assertCounterCount(playerA, bessSoulNourisher, CounterType.P1P1, 1); // Should only get one +1/+1 since all soldiers tokens enter at once
}
/**
* Test that the boosting corectly affects only creatures with BASE power and toughness of 1/1.
* Vodalian is a 1/1 so it should be buffed
* Artic merfolk is a 1/1 buffed to a 2/2, but it still has BASE PT of 1/1, so it should be buffed.
* Banewhip Punisher base 2/2 with a -1/-1 counter on it, so a 1/1, but its BASE is still 2/2, so it should not be buffed.
*/
@Test
public void testBoost() {
// 1/1
// Other Merfolk you control get +1/+1.
String vodalianHexcatcher = "Vodalian Hexcatcher";
// 1/1
String arcticMerfolk = "Arctic Merfolk";
// 2/2
// When Banewhip Punisher enters the battlefield, you may put a -1/-1 counter on target creature.
String banewhipPunisher = "Banewhip Punisher";
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 5);
addCard(Zone.BATTLEFIELD, playerA, bessSoulNourisher);
addCard(Zone.BATTLEFIELD, playerA, arcticMerfolk);
addCard(Zone.HAND, playerA, banewhipPunisher);
addCard(Zone.HAND, playerA, vodalianHexcatcher);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, banewhipPunisher, true);
setChoice(playerA, "Yes");
addTarget(playerA, banewhipPunisher);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vodalianHexcatcher);
attack(1, playerA, bessSoulNourisher);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertCounterCount(bessSoulNourisher, CounterType.P1P1, 1);
assertLife(playerB, 20 - 2);
assertPowerToughness(playerA, vodalianHexcatcher, 2, 2); // 1/1 + 1/1 from Bess
assertPowerToughness(playerA, arcticMerfolk, 3, 3); // 1/1 + (1/1 from Bess) + (1/1 Vodalian)
}
}

View file

@ -0,0 +1,82 @@
package org.mage.test.cards.single.ncc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* {@link mage.cards.m.MariTheKillingQuill Mari, the Killing Quill} {1}{B}{B}
* <p>
* Whenever a creature an opponent controls dies, exile it with a hit counter on it.
* <p>
* Assassins, Mercenaries, and Rogues you control have deathtouch and
* "Whenever this creature deals combat damage to a player, you may remove a hit counter from a card that player owns in exile.
* If you do, draw a card and create two Treasure tokens."
*
* @author Alex-Vasile
*/
public class MariTheKillingQuillTest extends CardTestPlayerBase {
private static final String mari = "Mari, the Killing Quill";
private static final String lightningBolt = "Lightning Bolt";
// Sliver with no ability 1/1
private static final String sliver = "Metallic Sliver";
// Changeling 1/1
private static final String automation = "Universal Automaton";
/**
* Test that an opponent's creature will get exiled with a hit counter.
* And that one of ours does not.
*/
@Test
public void testExiledWithCounter() {
addCard(Zone.HAND, playerA, lightningBolt, 2);
addCard(Zone.BATTLEFIELD, playerA, mari);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, automation);
addCard(Zone.BATTLEFIELD, playerB, sliver);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, sliver);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, automation);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertExileCount(playerA, 0);
assertGraveyardCount(playerA, automation, 1);
assertExileCount(playerB, sliver, 1);
assertCounterOnExiledCardCount(sliver, CounterType.HIT, 1);
}
/**
* Test that an opponent's creature will get exiled with a hit counter.
* And that one of ours does not.
*/
@Test
public void testDrawAndTreasure() {
addCard(Zone.HAND, playerA, lightningBolt);
addCard(Zone.BATTLEFIELD, playerA, mari);
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.BATTLEFIELD, playerA, automation);
addCard(Zone.BATTLEFIELD, playerB, sliver);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, sliver);
attack(1, playerA, automation, playerB);
setChoice(playerA, "Yes");
setChoice(playerA, sliver);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertHandCount(playerA, 1);
assertPermanentCount(playerA, "Treasure Token", 2);
assertExileCount(playerB, sliver, 1);
assertCounterOnExiledCardCount(sliver, CounterType.HIT, 0);
}
}

View file

@ -0,0 +1,159 @@
package org.mage.test.cards.single.ncc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* {@link mage.cards.r.ResourcefulDefense Resourceful Defense} {2}{W}
* <p>
* Whenever a permanent you control leaves the battlefield, if it had counters on it,
* put those counters on target permanent you control.
* <p>
* {4}{W}: Move any number of counters from target permanent you control to another target permanent you control.
*
* @author Alex-Vasile
*/
public class ResourcefulDefenseTest extends CardTestPlayerBase {
private static final String resourcefulDefense = "Resourceful Defense";
// Vivid Creek enters the battlefield tapped with two charge counters on it.
private static final String vividCreek = "Vivid Creek";
private static final String everflowingChalice = "Everflowing Chalice";
// Steelbane Hydra enters the battlefield with X +1/+1 counters on it.
private static final String steelbaneHydra = "Steelbane Hydra"; // {X}{G}{G}
private static final String lightningBolt = "Lightning Bolt";
/**
* Move counters from a creature that died.
*/
@Test
public void testMoveWhenDied() {
addCard(Zone.BATTLEFIELD, playerA, "Archway Commons", 9);
addCard(Zone.BATTLEFIELD, playerA, resourcefulDefense);
addCard(Zone.BATTLEFIELD, playerA, everflowingChalice);
addCard(Zone.HAND, playerA, steelbaneHydra);
addCard(Zone.HAND, playerA, lightningBolt);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, steelbaneHydra);
setChoice(playerA, "X=1");
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, lightningBolt, steelbaneHydra);
addTarget(playerA, everflowingChalice);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertCounterCount(everflowingChalice, CounterType.P1P1, 1);
}
/**
* Move all of one counter from one permanent to another when the source only has one coutner type.
*/
@Test
public void testMoveAllSingleCounters() {
addCard(Zone.BATTLEFIELD, playerA, "Archway Commons", 5);
addCard(Zone.BATTLEFIELD, playerA, resourcefulDefense);
addCard(Zone.BATTLEFIELD, playerA, vividCreek);
addCard(Zone.BATTLEFIELD, playerA, everflowingChalice);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{W}: ");
addTarget(playerA, vividCreek);
addTarget(playerA, everflowingChalice);
setChoiceAmount(playerA, 2);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertCounterCount(vividCreek, CounterType.CHARGE, 0);
assertCounterCount(everflowingChalice, CounterType.CHARGE, 2);
}
/**
* Move some of one counter from one permanent to another when the source only has one coutner type.
*/
@Test
public void testSomeAllSingleCounters() {
addCard(Zone.BATTLEFIELD, playerA, "Archway Commons", 5);
addCard(Zone.BATTLEFIELD, playerA, resourcefulDefense);
addCard(Zone.BATTLEFIELD, playerA, vividCreek);
addCard(Zone.BATTLEFIELD, playerA, everflowingChalice);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{W}: ");
addTarget(playerA, vividCreek);
addTarget(playerA, everflowingChalice);
setChoiceAmount(playerA, 1);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertCounterCount(vividCreek, CounterType.CHARGE, 1);
assertCounterCount(everflowingChalice, CounterType.CHARGE, 1);
}
/**
* Move multiple counter types from one permanent to another.
*
* Also tests that when a creature without counters dies that you won't be prompted.
* The hydra has no counters after the second activation and will die because toughtness==0, but we aren't prompted
* for targets when it dies.
*/
@Test
public void testMoveAllMultipleCounters() {
addCard(Zone.BATTLEFIELD, playerA, "Archway Commons", 8);
addCard(Zone.BATTLEFIELD, playerA, resourcefulDefense);
addCard(Zone.BATTLEFIELD, playerA, vividCreek);
addCard(Zone.HAND, playerA, steelbaneHydra);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, steelbaneHydra);
setChoice(playerA, "X=1");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{W}: ");
addTarget(playerA, vividCreek);
addTarget(playerA, steelbaneHydra);
setChoiceAmount(playerA, 2);
waitStackResolved(3, PhaseStep.PRECOMBAT_MAIN);
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{W}: ");
addTarget(playerA, steelbaneHydra);
addTarget(playerA, vividCreek);
setChoiceAmount(playerA, 2);
setChoiceAmount(playerA, 1);
setStopAt(3, PhaseStep.END_TURN);
execute();
assertCounterCount(vividCreek, CounterType.CHARGE, 2);
assertCounterCount(vividCreek, CounterType.P1P1, 1);
assertGraveyardCount(playerA, steelbaneHydra, 1);
}
/**
* Move multiple counter types from a creature that died.
*/
@Test
public void testMoveMultipleWhenDied() {
addCard(Zone.BATTLEFIELD, playerA, "Archway Commons", 9);
addCard(Zone.BATTLEFIELD, playerA, resourcefulDefense);
addCard(Zone.BATTLEFIELD, playerA, everflowingChalice);
addCard(Zone.BATTLEFIELD, playerA, vividCreek);
addCard(Zone.HAND, playerA, steelbaneHydra);
addCard(Zone.HAND, playerA, lightningBolt);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, steelbaneHydra);
setChoice(playerA, "X=1");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{W}: ");
addTarget(playerA, vividCreek);
addTarget(playerA, steelbaneHydra);
setChoiceAmount(playerA, 2);
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, steelbaneHydra);
addTarget(playerA, everflowingChalice);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertCounterCount(vividCreek, CounterType.CHARGE, 0);
assertCounterCount(everflowingChalice, CounterType.CHARGE, 2);
assertCounterCount(everflowingChalice, CounterType.P1P1, 1);
}
}

View file

@ -0,0 +1,73 @@
package org.mage.test.cards.single.ncc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestCommander4Players;
/**
* {@link mage.cards.s.ShieldBroker Shield Broker}
* {3}{U}{U}
* Creature Cephalid Advisor
* When Shield Broker enters the battlefield, put a shield counter on target noncommander creature you dont control.
* You gain control of that creature for as long as it has a shield counter on it.
* (If it would be dealt damage or destroyed, remove a shield counter from it instead.)
*/
public class ShieldBrokerTest extends CardTestCommander4Players {
private static final String shieldBroker = "Shield Broker";
private static final String rograkh = "Rograkh, Son of Rohgahh";
private static final String lightningBolt = "Lightning Bolt";
/**
* Test that it works for non-commander creature.
*/
@Test
public void testNonCommander() {
addCard(Zone.BATTLEFIELD, playerD, rograkh); // {0}
addCard(Zone.HAND, playerA, shieldBroker); // {3}{U}{U}
addCard(Zone.HAND, playerA, lightningBolt);
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shieldBroker);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertCounterCount(rograkh, CounterType.SHIELD, 1);
assertPermanentCount(playerA, rograkh, 1);
playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Mountain");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, lightningBolt, rograkh);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertCounterCount(rograkh, CounterType.SHIELD, 0);
assertPermanentCount(playerA, rograkh, 0);
assertPermanentCount(playerD, rograkh, 1);
}
/**
* Test that it does not work for commander creature.
*/
@Test
public void testCommander() {
addCard(Zone.COMMAND, playerD, rograkh); // {0}
addCard(Zone.HAND, playerA, shieldBroker); // {3}{U}{U}
addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, rograkh);
castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, shieldBroker);
setStopAt(5, PhaseStep.PRECOMBAT_MAIN);
execute();
assertCounterCount(rograkh, CounterType.SHIELD, 0);
assertPermanentCount(playerA, rograkh, 0);
assertPermanentCount(playerD, rograkh, 1);
}
}

View file

@ -0,0 +1,75 @@
package org.mage.test.cards.single.ncc;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.keyword.SuspendAbility;
import mage.constants.PhaseStep;
import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* {@link mage.cards.s.SinisterConcierge Sinister Concierge}
* 2/1
* When Sinister Concierge dies, you may exile it and put three time counters on it.
* If you do, exile up to one target creature and put three time counters on it.
* Each card exiled this way that doesnt have suspend gains suspend.
* @author Alex-Vasile
*/
public class SinisterConciergeTest extends CardTestPlayerBase {
private static final String sinisterConcierge = "Sinister Concierge"; // 2/1
private static final String bondedConstruct = "Bonded Construct"; // Simple 2/1
private static final String lightningBolt = "Lightning Bolt"; // {R}
/**
* Test that both cards are exiled properly.
*/
@Test
public void testWorking() {
addCard(Zone.HAND, playerA, lightningBolt);
addCard(Zone.BATTLEFIELD, playerA, sinisterConcierge);
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.BATTLEFIELD, playerB, bondedConstruct);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, sinisterConcierge);
setChoice(playerA, "Yes");
addTarget(playerA, bondedConstruct);
setStopAt(1, PhaseStep.END_COMBAT);
execute();
assertExileCount(playerA, sinisterConcierge, 1);
assertCounterOnExiledCardCount(sinisterConcierge, CounterType.TIME, 3);
assertExileCount(playerB, bondedConstruct, 1);
assertCounterOnExiledCardCount(bondedConstruct, CounterType.TIME, 3);
setStopAt(5, PhaseStep.PRECOMBAT_MAIN);
execute();
assertExileCount(playerA, sinisterConcierge, 1);
assertCounterOnExiledCardCount(sinisterConcierge, CounterType.TIME, 1);
assertExileCount(playerB, bondedConstruct, 1);
assertCounterOnExiledCardCount(bondedConstruct, CounterType.TIME, 1);
setStopAt(6, PhaseStep.PRECOMBAT_MAIN);
execute();
assertExileCount(playerA, sinisterConcierge, 1);
assertExileCount(playerB, bondedConstruct, 0);
assertPermanentCount(playerB, bondedConstruct, 1);
setStopAt(7, PhaseStep.PRECOMBAT_MAIN);
execute();
assertExileCount(playerA, sinisterConcierge, 0);
assertPermanentCount(playerA, sinisterConcierge, 1);
assertExileCount(playerB, bondedConstruct, 0);
assertPermanentCount(playerB, bondedConstruct, 1);
}
}

View file

@ -0,0 +1,63 @@
package org.mage.test.cards.single.ncc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* {@link mage.cards.s.SkywayRobber Skyway Robber}
* <p>
* Escape{3}{U}, Exile five other cards from your graveyard.
* (You may cast this card from your graveyard for its escape cost.)
* <p>
* Skyway Robber escapes with
* Whenever Skyway Robber deals combat damage to a player,
* you may cast an artifact, instant, or sorcery spell from among cards exiled with Skyway Robber
* without paying its mana cost.
* @author Alex-Vasile
*/
public class SkywayRobberTest extends CardTestPlayerBase {
private static final String skywayRobber = "Skyway Robber";
/**
* Check that you are not given the option to cast anything if there are no valid choices.
*/
@Test
public void testNoOption() {
addCard(Zone.GRAVEYARD, playerA, skywayRobber);
addCard(Zone.GRAVEYARD, playerA, "Mountain", 5);
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + skywayRobber + " with Escape");
attack(3, playerA, skywayRobber);
setStopAt(3, PhaseStep.END_COMBAT);
execute();
assertExileCount(playerA, "Mountain", 5);
assertLife(playerB, 17);
}
/**
* Check that the cast works.
*/
@Test
public void testCast() {
addCard(Zone.GRAVEYARD, playerA, skywayRobber);
addCard(Zone.GRAVEYARD, playerA, "Mountain", 4);
addCard(Zone.GRAVEYARD, playerA, "Sol Ring" );
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + skywayRobber + " with Escape");
attack(3, playerA, skywayRobber);
setStopAt(3, PhaseStep.END_TURN);
execute();
assertExileCount(playerA, "Mountain", 4);
assertPermanentCount(playerA, "Sol Ring", 1);
assertLife(playerB, 17);
}
}

View file

@ -0,0 +1,230 @@
package org.mage.test.cards.single.ncc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* {@link mage.cards.t.ThreefoldSignal Threefold Signal}
* <p>
* Each spell you cast thats exactly three colors has replicate {3}.
* @author Alex-Vasile
*/
public class ThreefoldSignalTest extends CardTestPlayerBase {
private static final String threefoldSignal = "Threefold Signal";
// R
private static final String lightningBolt = "Lightning Bolt";
// WUBRG
private static final String atogatog = "Atogatog";
// WUB
private static final String esperSojourners = "Esper Sojourners";
/**
* Check that it works for three-colored spells
*/
@Test
public void testShouldWork() {
addCard(Zone.BATTLEFIELD, playerA, threefoldSignal);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
addCard(Zone.HAND, playerA, esperSojourners);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, esperSojourners);
setChoice(playerA, true);
setChoice(playerA, false);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, esperSojourners, 2);
}
/**
* Check that it does not trigger for spells with less than three colors.
*/
@Test
public void testShouldNotWork1Color() {
addCard(Zone.BATTLEFIELD, playerA, threefoldSignal);
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.HAND, playerA, lightningBolt);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, playerB);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 17);
}
/**
* Check that it does not trigger for spells with more than three colors.
*/
@Test
public void testShouldNotWork5Color() {
addCard(Zone.BATTLEFIELD, playerA, threefoldSignal);
addCard(Zone.BATTLEFIELD, playerA, "Plains");
addCard(Zone.BATTLEFIELD, playerA, "Island");
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.HAND, playerA, atogatog);
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, atogatog);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, atogatog, 1);
}
/**
* Check that it does not trigger for spells opponents control.
*/
@Test
public void testShouldNotWorkOpponent() {
addCard(Zone.BATTLEFIELD, playerA, threefoldSignal);
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
addCard(Zone.HAND, playerB, esperSojourners);
setStrictChooseMode(true);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, esperSojourners);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerB, esperSojourners, 1);
}
/**
* Check that casting one half of a split card doesn't trigger it even if the whole split card has 3 colors.
* Relevant ruling:
* 709.3a Only the chosen half is evaluated to see if it can be cast.
* Only that half is considered to be put onto the stack.
* 709.3b While on the stack, only the characteristics of the half being cast exist.
* The other halfs characteristics are treated as though they didnt exist.
*/
@Test
public void oneHalfOfSplitCardDoesntTrigger() {
// {G}{U} / {4}{W}{U}
// Beck: Whenever a creature enters the battlefield this turn, you may draw a card.
// Call: Create four 1/1 white Bird creature tokens with flying.
// Fuse
String beckCall = "Beck // Call";
addCard(Zone.HAND, playerA, beckCall);
addCard(Zone.BATTLEFIELD, playerA, threefoldSignal);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4 + 3); // For generic costs and to have enough for replicate
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Call");
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Bird Token", 4);
}
/**
* Test that casting a split card with fuse triggers if both halves together have 3 colors
*/
@Test
public void fusedSplitCardTriggers() {
// {G}{U} / {4}{W}{U}
// Beck: Whenever a creature enters the battlefield this turn, you may draw a card.
// Call: Create four 1/1 white Bird creature tokens with flying.
// Fuse
String beckCall = "Beck // Call";
addCard(Zone.HAND, playerA, beckCall);
addCard(Zone.BATTLEFIELD, playerA, threefoldSignal);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4 + 3); // For generic costs and to have enough for replicate
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Beck // Call");
setChoice(playerA, true); // Pay replicate once
setChoice(playerA, false); // Don't pay replicate twice
// Copy resolves, first Beck then call
setChoice(playerA, "Whenever", 3); // 4 triggers total, pick order for 3 and the 4th is auto-chosen
setChoice(playerA, true, 4); // Draw cards 4 times
// Original resolves
// There will be 8 ETB triggers. 4 creatures enter but there are 2 instances of Beck that were cast
setChoice(playerA, "Whenever", 7); // 8 triggers total, pick order for 7 and the 8th is auto-chosen
setChoice(playerA, true, 8); // Draw cards 8 times
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Bird Token", 4 + 4);
assertHandCount(playerA, 4 + (4+4));
}
/**
* Test that casting a split card with fuse triggers if both halves together have 3 colors
*/
@Test
public void fusedSplitCardTriggers2() {
// {3}{B}{G} / {R}{G}
// Flesh: Exile target creature card from a graveyard.
// Put X +1/+1 counters on target creature, where X is the power of the card you exiled.
// Blood: Target creature you control deals damage equal to its power to any target.
// Fuse
String fleshBlood = "Flesh // Blood";
addCard(Zone.HAND, playerA, fleshBlood);
addCard(Zone.BATTLEFIELD, playerA, threefoldSignal);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1+3 + 3); // For red + generic costs and to have enough for replicate
// All are 2/2
String lion = "Silvercoat Lion";
addCard(Zone.BATTLEFIELD, playerA, lion);
String griffin = "Abbey Griffin";
addCard(Zone.GRAVEYARD, playerA, griffin); // Exile with original cast
String centaur = "Accursed Centaur";
addCard(Zone.GRAVEYARD, playerA, centaur); // Exile with copy
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Flesh // Blood");
setChoice(playerA, true); // Pay replicate once
setChoice(playerA, false); // Don't pay replicate twice
// Flesh
addTarget(playerA, griffin);
addTarget(playerA, lion);
// Blood
addTarget(playerA, lion);
addTarget(playerA, playerB);
// Copy of Flesh
setChoice(playerA, true); // Change the exile card from the Griffin
addTarget(playerA, centaur);
setChoice(playerA, false); // Don't change target from the lion
// Copy of Blood
setChoice(playerA, false); // Don't change target from lion
setChoice(playerA, false); // Don't change target from PlayerB
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertCounterCount(lion, CounterType.P1P1, 4); // 2 from the copy and two from the original cast
assertLife(playerB, 20 - (2+2) - (2+2+2));
assertExileCount(playerA, griffin, 1);
assertExileCount(playerA, centaur, 1);
}
}

View file

@ -0,0 +1,68 @@
package org.mage.test.cards.single.ncc;
import mage.abilities.keyword.IndestructibleAbility;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* Weathered Sentinels
* {3}
* Artifact Creature Wall
* Defender, vigilance, reach, trample
* Weathered Sentinels can attack players who attacked you during their last turn as though it didnt have defender.
* Whenever Weathered Sentinels attacks, it gets +3/+3 and gains indestructible until end of turn.
*/
public class WeatheredSentinelsTest extends CardTestPlayerBase {
private static final String weatheredSentinels = "Weathered Sentinels";
// 1/1 Haste attacker
private static final String gingerBrute = "Gingerbrute";
/**
* Should not be able to attack a player who did not attack you on their last turn
*/
@Test
public void testCantAttackNonAttacker() {
addCard(Zone.BATTLEFIELD, playerA, weatheredSentinels);
attack(1, playerA, weatheredSentinels);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
try {
execute();
} catch (Throwable e) {
if (!e.getMessage().contains("Player PlayerA must have 0 actions but found 1")) {
Assert.fail("Should have had error about playerA not being able to attack, but got:\n" + e.getMessage());
}
}
}
/**
* Should be able to attack a player that attacked you on their last turn, and it should get +3/+3 and indestructible until end of turn.
*/
@Test
public void testCanAttackAttacker() {
addCard(Zone.BATTLEFIELD, playerA, weatheredSentinels);
addCard(Zone.BATTLEFIELD, playerB, gingerBrute);
// Attack playerA
attack(2, playerB, gingerBrute);
// Attack back
attack(3, playerA, weatheredSentinels);
// Check that Weathered Sentinels has a +3/+3 and indestructible
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertAbility(playerA, weatheredSentinels, IndestructibleAbility.getInstance(), true);
assertPowerToughness(playerA, weatheredSentinels, 5, 8);
// Check that Weathered Sentinels lost the abilities next turn
setStopAt(4, PhaseStep.PRECOMBAT_MAIN);
execute();
}
}

View file

@ -0,0 +1,95 @@
package org.mage.test.utils;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
public class CardUtilTest extends CardTestPlayerBase {
// Whenever you cast or copy an instant or sorcery spell, reveal the top card of your library.
// If its a nonland card, you may cast it by paying {1} rather than paying its mana cost.
// If its a land card, put it onto the battlefield.
private static final String jadzi = "Jadzi, Oracle of Arcavios";
// Whenever you discard a nonland card, you may cast it from your graveyard.
private static final String oskar = "Oskar, Rubbish Reclaimer";
// MDFC where the back side is a land "Akoum Teeth"
private static final String akoumWarrior = "Akoum Warrior"; // {5}{R}
// Discard your hand, then draw a card for each card youve discarded this turn.
private static final String changeOfFortune = "Change of Fortune"; // {3}{R}
// MDFC where both sides should be playable
private static final String birgi = "Birgi, God of Storytelling"; // {2}{R}, frontside of Harnfel
private static final String harnfel = "Harnfel, Horn of Bounty"; // {4}{R}, backside of Birgi
/**
* Test that it will for trigger for discarding a MDFC but will only let you cast the nonland side.
*/
@Test
public void cantPlayLandSideOfMDFC() {
addCard(Zone.HAND, playerA, changeOfFortune);
addCard(Zone.HAND, playerA, akoumWarrior);
addCard(Zone.BATTLEFIELD, playerA, oskar);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10);
skipInitShuffling();
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, changeOfFortune);
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.DECLARE_ATTACKERS);
execute();
assertPermanentCount(playerA, akoumWarrior, 1);
}
/**
* Test that when both sides of a MDFC card match, we can choose either side.
*/
@Test
public void testFrontSideOfMDFC() {
addCard(Zone.HAND, playerA, changeOfFortune);
addCard(Zone.HAND, playerA, birgi, 2);
addCard(Zone.BATTLEFIELD, playerA, oskar);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 12);
skipInitShuffling();
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, changeOfFortune);
setChoice(playerA, "Whenever you discard");
setChoice(playerA, "Yes");
setChoice(playerA, "Cast " + birgi);
setChoice(playerA, "Yes");
setChoice(playerA, "Cast " + harnfel);
setStopAt(1, PhaseStep.DECLARE_ATTACKERS);
execute();
assertPermanentCount(playerA, birgi, 1);
assertPermanentCount(playerA, harnfel, 1);
}
/**
* Test that with Jadzi, you are able to play the nonland side of a MDFC, and that the alternative cost works properly.
*/
@Test
public void testJadziPlayingLandAndCast() {
addCard(Zone.BATTLEFIELD, playerA, jadzi);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1+1+1);
addCard(Zone.HAND, playerA, "Lightning Bolt", 2);
addCard(Zone.LIBRARY, playerA, "Cragcrown Pathway");
addCard(Zone.LIBRARY, playerA, akoumWarrior);
skipInitShuffling();
setStrictChooseMode(true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
setChoice(playerA, "Yes");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
setStopAt(1, PhaseStep.DECLARE_ATTACKERS);
execute();
assertPermanentCount(playerA, akoumWarrior, 1);
assertPermanentCount(playerA, "Cragcrown Pathway", 1);
}
}