mirror of
https://github.com/magefree/mage.git
synced 2026-01-24 12:19:59 -08:00
Merge branch 'master' into feature/network_bandwidth
This commit is contained in:
commit
6461622204
150 changed files with 2609 additions and 355 deletions
|
|
@ -289,6 +289,197 @@ public class CursesTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerA, 2);
|
||||
|
||||
assertPowerToughness(playerB, "Silvercoat Lion", 1, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void cruelRealityHasBothCreatureAndPwChoosePw() {
|
||||
|
||||
/*
|
||||
Cruel Reality {5}{B}{B}
|
||||
Enchantment - Aura Curse
|
||||
Enchant player
|
||||
At the beginning of enchanted player's upkeep, that player sacrifices a creature or planeswalker. If the player can't, he or she loses 5 life.
|
||||
*/
|
||||
String cReality = "Cruel Reality";
|
||||
|
||||
/*
|
||||
Ugin, the Spirit Dragon {8}
|
||||
Planeswalker — Ugin
|
||||
+2: Ugin, the Spirit Dragon deals 3 damage to target creature or player.
|
||||
−X: Exile each permanent with converted mana cost X or less that's one or more colors.
|
||||
−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.
|
||||
*/
|
||||
String ugin = "Ugin, the Spirit Dragon";
|
||||
String memnite = "Memnite"; // {0} 1/1
|
||||
|
||||
addCard(Zone.HAND, playerA, cReality);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7);
|
||||
addCard(Zone.BATTLEFIELD, playerB, ugin);
|
||||
addCard(Zone.BATTLEFIELD, playerB, memnite);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cReality, playerB);
|
||||
setChoice(playerB, ugin);
|
||||
|
||||
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, ugin, 1);
|
||||
assertPermanentCount(playerB, memnite, 1);
|
||||
assertPermanentCount(playerA, cReality, 1);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cruelRealityHasBothCreatureAndPwChooseCreature() {
|
||||
|
||||
/*
|
||||
Cruel Reality {5}{B}{B}
|
||||
Enchantment - Aura Curse
|
||||
Enchant player
|
||||
At the beginning of enchanted player's upkeep, that player sacrifices a creature or planeswalker. If the player can't, he or she loses 5 life.
|
||||
*/
|
||||
String cReality = "Cruel Reality";
|
||||
|
||||
/*
|
||||
Ugin, the Spirit Dragon {8}
|
||||
Planeswalker — Ugin
|
||||
+2: Ugin, the Spirit Dragon deals 3 damage to target creature or player.
|
||||
−X: Exile each permanent with converted mana cost X or less that's one or more colors.
|
||||
−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.
|
||||
*/
|
||||
String ugin = "Ugin, the Spirit Dragon";
|
||||
String memnite = "Memnite"; // {0} 1/1
|
||||
|
||||
addCard(Zone.HAND, playerA, cReality);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7);
|
||||
addCard(Zone.BATTLEFIELD, playerB, ugin);
|
||||
addCard(Zone.BATTLEFIELD, playerB, memnite);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cReality, playerB);
|
||||
setChoice(playerB, memnite);
|
||||
|
||||
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, memnite, 1);
|
||||
assertPermanentCount(playerB, ugin, 1);
|
||||
assertPermanentCount(playerA, cReality, 1);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cruelRealityOnlyHasCreatureNoChoiceMade() {
|
||||
|
||||
/*
|
||||
Cruel Reality {5}{B}{B}
|
||||
Enchantment - Aura Curse
|
||||
Enchant player
|
||||
At the beginning of enchanted player's upkeep, that player sacrifices a creature or planeswalker. If the player can't, he or she loses 5 life.
|
||||
*/
|
||||
String cReality = "Cruel Reality";
|
||||
String memnite = "Memnite"; // {0} 1/1
|
||||
|
||||
addCard(Zone.HAND, playerA, cReality);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7);
|
||||
addCard(Zone.BATTLEFIELD, playerB, memnite);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cReality, playerB);
|
||||
|
||||
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, memnite, 1);
|
||||
assertPermanentCount(playerA, cReality, 1);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cruelRealityOnlyHasPwNoChoiceMade() {
|
||||
|
||||
/*
|
||||
Cruel Reality {5}{B}{B}
|
||||
Enchantment - Aura Curse
|
||||
Enchant player
|
||||
At the beginning of enchanted player's upkeep, that player sacrifices a creature or planeswalker. If the player can't, he or she loses 5 life.
|
||||
*/
|
||||
String cReality = "Cruel Reality";
|
||||
|
||||
/*
|
||||
Ugin, the Spirit Dragon {8}
|
||||
Planeswalker — Ugin
|
||||
+2: Ugin, the Spirit Dragon deals 3 damage to target creature or player.
|
||||
−X: Exile each permanent with converted mana cost X or less that's one or more colors.
|
||||
−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.
|
||||
*/
|
||||
String ugin = "Ugin, the Spirit Dragon";
|
||||
|
||||
addCard(Zone.HAND, playerA, cReality);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7);
|
||||
addCard(Zone.BATTLEFIELD, playerB, ugin);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cReality, playerB);
|
||||
|
||||
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, ugin, 1);
|
||||
assertPermanentCount(playerA, cReality, 1);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cruelRealityOnlyHasCreatureTryToChooseNotToSac() {
|
||||
|
||||
/*
|
||||
Cruel Reality {5}{B}{B}
|
||||
Enchantment - Aura Curse
|
||||
Enchant player
|
||||
At the beginning of enchanted player's upkeep, that player sacrifices a creature or planeswalker. If the player can't, he or she loses 5 life.
|
||||
*/
|
||||
String cReality = "Cruel Reality";
|
||||
String memnite = "Memnite"; // {0} 1/1
|
||||
|
||||
addCard(Zone.HAND, playerA, cReality);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7);
|
||||
addCard(Zone.BATTLEFIELD, playerB, memnite);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cReality, playerB);
|
||||
setChoice(playerB, "No");
|
||||
|
||||
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, memnite, 1);
|
||||
assertPermanentCount(playerA, cReality, 1);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cruelRealityNoCreatureOrPwForcesLifeLoss() {
|
||||
|
||||
/*
|
||||
Cruel Reality {5}{B}{B}
|
||||
Enchantment - Aura Curse
|
||||
Enchant player
|
||||
At the beginning of enchanted player's upkeep, that player sacrifices a creature or planeswalker. If the player can't, he or she loses 5 life.
|
||||
*/
|
||||
String cReality = "Cruel Reality";
|
||||
String gPrison = "Ghostly Prison"; // {2}{W} enchantment - doesnt matter text for this
|
||||
|
||||
addCard(Zone.HAND, playerA, cReality);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7);
|
||||
addCard(Zone.BATTLEFIELD, playerB, gPrison);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cReality, playerB);
|
||||
setChoice(playerB, gPrison); // try to set choice to enchantment
|
||||
|
||||
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerB, gPrison, 1);
|
||||
assertPermanentCount(playerA, cReality, 1);
|
||||
assertLife(playerB, 15);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
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 escplan9
|
||||
*/
|
||||
public class ExertTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void exertUsedDoesNotUntapNextUntapStep() {
|
||||
|
||||
/*
|
||||
Glory-Bound Initiate {1}{W}
|
||||
Creature - Human Warrior 3/1
|
||||
You may exert Glory-Bound Intiate as it attacks. When you do, it gets +1/+3 and gains lifelink until end of turn.
|
||||
*/
|
||||
String gbInitiate = "Glory-Bound Initiate";
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, gbInitiate);
|
||||
attack(1, playerA, gbInitiate);
|
||||
setChoice(playerA, "Yes");
|
||||
|
||||
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertTapped(gbInitiate, true); // does not untap due to exert
|
||||
assertLife(playerA, 24); // exerted 4/4 lifelink
|
||||
assertLife(playerB, 16);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exertNotUsedRegularAttack() {
|
||||
|
||||
/*
|
||||
Glory-Bound Initiate {1}{W}
|
||||
Creature - Human Warrior 3/1
|
||||
You may exert Glory-Bound Intiate as it attacks. When you do, it gets +1/+3 and gains lifelink until end of turn.
|
||||
*/
|
||||
String gbInitiate = "Glory-Bound Initiate";
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, gbInitiate);
|
||||
attack(1, playerA, gbInitiate);
|
||||
setChoice(playerA, "No");
|
||||
|
||||
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertTapped(gbInitiate, false); // untaps as normal
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 17);
|
||||
}
|
||||
|
||||
/*
|
||||
"If you gain control of another player's creature until end of turn and exert it, it will untap during that player's untap step."
|
||||
See issue #3183
|
||||
*/
|
||||
@Test
|
||||
public void stolenExertCreatureShouldUntapDuringOwnersUntapStep() {
|
||||
|
||||
/*
|
||||
Glory-Bound Initiate {1}{W}
|
||||
Creature - Human Warrior 3/1
|
||||
You may exert Glory-Bound Intiate as it attacks. When you do, it gets +1/+3 and gains lifelink until end of turn.
|
||||
*/
|
||||
String gbInitiate = "Glory-Bound Initiate";
|
||||
String aTreason = "Act of Treason"; // {2}{R} sorcery gain control target creature, untap, gains haste until end of turn
|
||||
|
||||
addCard(Zone.HAND, playerA, gbInitiate);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
addCard(Zone.HAND, playerB, aTreason);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, gbInitiate);
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, aTreason, gbInitiate);
|
||||
attack(2, playerB, gbInitiate);
|
||||
setChoice(playerB, "Yes");
|
||||
|
||||
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, aTreason, 1);
|
||||
assertLife(playerA, 16);
|
||||
assertLife(playerB, 24);
|
||||
assertTapped(gbInitiate, false); // stolen creature exerted does untap during owner's untap step
|
||||
}
|
||||
}
|
||||
|
|
@ -73,18 +73,48 @@ public class TransmuteTest extends CardTestPlayerBase {
|
|||
@Test
|
||||
public void searchSplittedCardOneManaCmcSpell() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
addCard(Zone.HAND, playerA, "Dizzy Spell");
|
||||
// Target creature gets -3/-0 until end of turn.
|
||||
// Transmute {1}{U}{U} ({1}{U}{U}, Discard this card: Search your library for a card with the same converted mana cost as this card, reveal it, and put it into your hand. Then shuffle your library. Transmute only as a sorcery.)
|
||||
addCard(Zone.HAND, playerA, "Dizzy Spell"); // Instant {U}
|
||||
|
||||
// Wear {1}{R}
|
||||
// Destroy target artifact.
|
||||
// Tear {W}
|
||||
// Destroy target enchantment.
|
||||
addCard(Zone.LIBRARY, playerA, "Wear // Tear");
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Transmute {1}{U}{U}");
|
||||
setChoice(playerA, "Wear // Tear");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Dizzy Spell", 1);
|
||||
assertHandCount(playerA, "Wear", 1); // Filter search can only search for one side of a split card
|
||||
assertHandCount(playerA, "Tear", 1); // Filter search can only search for one side of a split card
|
||||
assertHandCount(playerA, "Wear // Tear", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchSplittedCardThreeManaCmcSpell() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
||||
// Counter target spell unless its controller discards his or her hand.
|
||||
// Transmute {1}{U}{B}
|
||||
addCard(Zone.HAND, playerA, "Perplex"); // Instant {1}{U}{B}
|
||||
|
||||
// Wear {1}{R}
|
||||
// Destroy target artifact.
|
||||
// Tear {W}
|
||||
// Destroy target enchantment.
|
||||
addCard(Zone.LIBRARY, playerA, "Wear // Tear");
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Transmute {1}{U}{B}");
|
||||
setChoice(playerA, "Wear // Tear");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Perplex", 1);
|
||||
assertHandCount(playerA, "Wear // Tear", 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import mage.constants.Zone;
|
|||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
|
@ -26,9 +29,47 @@ public class TwoHeadedSliverTest extends CardTestPlayerBase {
|
|||
block(3, playerB, "Silvercoat Lion", "Two-Headed Sliver");
|
||||
|
||||
setStopAt(3, PhaseStep.END_TURN);
|
||||
|
||||
try {
|
||||
execute();
|
||||
fail("Expected exception not thrown");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
assertEquals("Two-Headed Sliver is blocked by 1 creature(s). It has to be blocked by 2 or more.", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanBeBlockedByTwoEffectAbility() {
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
// Two-Headed Sliver {1}{R} 1/1
|
||||
// All Sliver creatures have "This creature can't be blocked except by two or more creatures."
|
||||
addCard(Zone.HAND, playerA, "Two-Headed Sliver");
|
||||
|
||||
// Silvercoat Lion {1}{W} 2/2
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
|
||||
// Coral Barrier {2}{U} 1/3
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Coral Barrier");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Two-Headed Sliver");
|
||||
|
||||
attack(3, playerA, "Two-Headed Sliver");
|
||||
// Two blocks will succeed
|
||||
block(3, playerB, "Silvercoat Lion", "Two-Headed Sliver");
|
||||
block(3, playerB, "Coral Barrier", "Two-Headed Sliver");
|
||||
|
||||
setStopAt(3, PhaseStep.END_TURN);
|
||||
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Two-Headed Sliver", 1);
|
||||
assertLife(playerB, 19);
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
||||
// Two-Headed Sliver died from the block
|
||||
assertPermanentCount(playerA, "Two-Headed Sliver", 0);
|
||||
assertPermanentCount(playerB, "Silvercoat Lion", 1);
|
||||
assertPermanentCount(playerB, "Coral Barrier", 1);
|
||||
assertGraveyardCount(playerA, "Two-Headed Sliver", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,8 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
|
|||
public class FluctuatorTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* NOTE: As of 4/19/2017 this test is failing due to a bug in code. See issue #3148
|
||||
*
|
||||
* Fluctuator makes 'Akroma's Vengeance' cyclic cost reduced to {1}
|
||||
* Test it with one Plains on battlefield.
|
||||
*/
|
||||
|
|
@ -88,8 +90,9 @@ public class FluctuatorTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* NOTE: As of 4/19/2017 this test is failing due to a bug in code. See issue #3148
|
||||
*
|
||||
* Test 2 Fluctuators reduce cycling cost up to 4.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testTwoFluctuatorsReduceBy4() {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ public class SplitCardCmcTest extends CardTestPlayerBase {
|
|||
public void testSplitCardCmcInHand() {
|
||||
// Total CMC of Failure // Comply is 3, so should be exiled by Transgress the Mind.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
// Devoid
|
||||
// Target player reveals his or her hand. You may choose a card from it with converted mana cost 3 or greater and exile that card.
|
||||
addCard(Zone.HAND, playerA, "Transgress the Mind");
|
||||
addCard(Zone.HAND, playerB, "Failure // Comply");
|
||||
|
||||
|
|
@ -42,11 +44,17 @@ public class SplitCardCmcTest extends CardTestPlayerBase {
|
|||
public void testSplitCardCmcOnStack() {
|
||||
// Counterbalance revealing Wear // Tear counters a spell with converted mana cost 3, but not 1 or 2.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
||||
addCard(Zone.HAND, playerA, "Typhoid Rats");
|
||||
addCard(Zone.HAND, playerA, "Typhoid Rats"); // Creature 1/1 {B}
|
||||
|
||||
// Whenever an opponent casts a spell, you may reveal the top card of your library. If you do, counter that spell
|
||||
// if it has the same converted mana cost as the revealed card.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Counterbalance");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
|
||||
|
||||
// Wear {1}{R}
|
||||
// Destroy target artifact.
|
||||
// Tear {W}
|
||||
// Destroy target enchantment.
|
||||
addCard(Zone.LIBRARY, playerB, "Wear // Tear"); // CMC now 3
|
||||
skipInitShuffling(); // so the set to top card stays at top
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
|
|||
public class LilianaTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void testMe() {
|
||||
public void testCreatureGainsZombieAsAdditionalType() {
|
||||
/*
|
||||
Binding Mummy {1}{W}
|
||||
Creature - Zombie 2/2
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ import mage.constants.Zone;
|
|||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2, icetc
|
||||
|
|
@ -212,12 +215,13 @@ public class BlockRequirementTest extends CardTestPlayerBase {
|
|||
block(1, playerB, "Hill Giant", "Breaker of Armies");
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
// Hill giant is still alive
|
||||
assertPermanentCount(playerB, "Hill Giant", 1);
|
||||
// Player B was unable to block, so goes down to 10 life
|
||||
assertLife(playerB, 8);
|
||||
try {
|
||||
execute();
|
||||
fail("Expected exception not thrown");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
assertEquals("Breaker of Armies is blocked by 1 creature(s). It has to be blocked by 2 or more.", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -230,17 +234,17 @@ public class BlockRequirementTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerA, "Slayer's Cleaver");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Memnite"); // {1} 1/1
|
||||
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Dimensional Infiltrator"); // 2/1 - Eldrazi
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Llanowar Elves"); // 1/1
|
||||
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Memnite"); // pumps to 4/2
|
||||
attack(1, playerA, "Memnite"); // must be blocked by Dimensional Infiltrator
|
||||
block(1, playerB, "Llanowar Elves", "Memnite"); // should not be allowed as only blocker
|
||||
|
||||
|
||||
setStopAt(1, PhaseStep.END_COMBAT);
|
||||
execute();
|
||||
|
||||
|
||||
assertPermanentCount(playerA, "Slayer's Cleaver", 1);
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Memnite", 1);
|
||||
|
|
|
|||
|
|
@ -65,12 +65,16 @@ public class DuskDawnTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerA, "Dusk // Dawn", 0);
|
||||
}
|
||||
|
||||
// Fail to cast Dawn (Aftermath part) from hand
|
||||
@Test
|
||||
public void testCastDawnFail() {
|
||||
// Fail to cast dawn from hand
|
||||
// Dusk {2}{W}{W}
|
||||
// Destroy all creatures with power 3 or greater.
|
||||
// Dawn {3}{W}{W}
|
||||
// Return all creature cards with power less than or equal to 2 from your graveyard to your hand.
|
||||
addCard(Zone.HAND, playerA, "Dusk // Dawn");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||
addCard(Zone.GRAVEYARD, playerA, "Devoted Hero");
|
||||
addCard(Zone.GRAVEYARD, playerA, "Devoted Hero"); // Creature 1/2 {W}
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dawn");
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ public class PermeatingMassTest extends CardTestPlayerBase {
|
|||
assertPowerToughness(playerA, "Permeating Mass", 1, 3);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: As of 04/19/2017 this test is failing due to a bug in code. See issue #3167
|
||||
*/
|
||||
@Test
|
||||
public void damagedCreatureWithVaryingPTbecomesCopyOfPermeatingMass() {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ package org.mage.test.cards.triggers;
|
|||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Ignore;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.Filter;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
|
|
@ -45,9 +46,10 @@ public class TargetedTriggeredTest extends CardTestPlayerBase {
|
|||
*
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
// this does not currently work in test, because the target event will be fired earlier during tests,
|
||||
//@Ignore
|
||||
// this does not currently work in test (????), because the target event will be fired earlier during tests,
|
||||
// so the zone change counter for the fixed target of the counterspell will not work
|
||||
// UPDATE: seems to work fine now? 04/19/2017 escplan9
|
||||
public void testKiraGreatGlassSpinnerFirstSpellTurn() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt");
|
||||
|
|
@ -89,4 +91,115 @@ public class TargetedTriggeredTest extends CardTestPlayerBase {
|
|||
assertPowerToughness(playerB, "Ashenmoor Liege", 4, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlyphKeeperCountersFirstSpell() {
|
||||
|
||||
/*
|
||||
Glyph Keeper {3}{U}{U}
|
||||
Creature - Sphinx
|
||||
Flying 5/3
|
||||
Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability."
|
||||
*/
|
||||
String gKeeper = "Glyph Keeper";
|
||||
String bolt = "Lightning Bolt"; // {R} instant deal 3 dmg
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerA, bolt);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, gKeeper);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, bolt, 1);
|
||||
assertPermanentCount(playerB, gKeeper, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlyphKeeperCountersFirstSpellButNotSecondSpell() {
|
||||
|
||||
/*
|
||||
Glyph Keeper {3}{U}{U}
|
||||
Creature - Sphinx
|
||||
Flying 5/3
|
||||
Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability."
|
||||
*/
|
||||
String gKeeper = "Glyph Keeper";
|
||||
String bolt = "Lightning Bolt"; // {R} instant deal 3 dmg
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
addCard(Zone.HAND, playerA, bolt, 2);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, gKeeper);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper);
|
||||
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, bolt, gKeeper);
|
||||
|
||||
setStopAt(1, PhaseStep.DECLARE_ATTACKERS);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, bolt, 2);
|
||||
assertPermanentCount(playerB, gKeeper, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
NOTE: test is failing due to card bug as of 04/20/2017. See issue #3180
|
||||
I had a Glyph Keeper on board (cloned with Vizier of many faces). -- note this test is a simplified version, next test will test on the Clone if needed
|
||||
First I played a Soulstinger and targeted the Glyph Keeper, the ability was countered. Then on the same main phase I played a Cartouche of Strength targeting the Glyph Keeper, that was also countered.
|
||||
Only the first should have been countered.
|
||||
*/
|
||||
@Test
|
||||
public void testGlyphKeeperCountersFirstAbilityButNotSecondOne() {
|
||||
|
||||
/*
|
||||
Glyph Keeper {3}{U}{U}
|
||||
Creature - Sphinx
|
||||
Flying 5/3
|
||||
Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability."
|
||||
*/
|
||||
String gKeeper = "Glyph Keeper";
|
||||
|
||||
/*
|
||||
Soulstinger {3}{B}
|
||||
Creature - Scorpion Demon 4/5
|
||||
When Soulstinger enters the battlefield, put two -1/-1 counter on target creature you control.
|
||||
When Soulstinger dies, you may put a -1/-1 counter on target creature for each -1/-1 counter on Soulstinger.
|
||||
*/
|
||||
String sStinger = "Soulstinger";
|
||||
|
||||
/*
|
||||
Cartouche of Strength {2}{G}
|
||||
Enchantment - Aura Cartouche
|
||||
Enchant creature you control
|
||||
When Cartouche of Strength enters the battlefield, you may have enchanted creature fight target creature an opponent controls.
|
||||
Enchanted creature gets +1/+1 and has trample.
|
||||
*/
|
||||
String cStrength = "Cartouche of Strength";
|
||||
String memnite = "Memnite"; // {0} 1/1
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, gKeeper);
|
||||
addCard(Zone.HAND, playerA, sStinger);
|
||||
addCard(Zone.HAND, playerA, cStrength);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 6);
|
||||
addCard(Zone.BATTLEFIELD, playerB, memnite);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sStinger);
|
||||
addTarget(playerA, gKeeper); // should be countered by Glyph Keeper clause as first ability targetting it
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cStrength, gKeeper); // should not be countered anymore
|
||||
addTarget(playerA, memnite); // Cartouche of Strength fight
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, gKeeper, 1);
|
||||
assertGraveyardCount(playerA, sStinger, 0); // countered
|
||||
assertGraveyardCount(playerA, cStrength, 0); // should not be countered
|
||||
assertPermanentCount(playerA, cStrength, 1);
|
||||
assertGraveyardCount(playerB, memnite, 1); // dies from fight
|
||||
assertPowerToughness(playerA, gKeeper, 5, 3, Filter.ComparisonScope.All); // Soul Stinger should never have given it two -1/-1 counters
|
||||
assertCounterCount(playerA, gKeeper, CounterType.M1M1, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ package org.mage.test.cards.watchers;
|
|||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.junit.Assert;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
|
|
@ -11,27 +10,28 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
|
|||
* Created by IGOUDT on 30-3-2017.
|
||||
*/
|
||||
public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase {
|
||||
|
||||
private final String kira = "Kira, Great Glass-Spinner";
|
||||
private final String shock = "Shock";
|
||||
|
||||
@Test
|
||||
public void counterFirst(){
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Ugin, the Spirit Dragon"); // starts with 7 Loyality counters
|
||||
public void counterFirst() {
|
||||
|
||||
String ugin = "Ugin, the Spirit Dragon";
|
||||
addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyality counters
|
||||
|
||||
/*
|
||||
Kira, Great Glass-Spinner {1}{U}{U}
|
||||
Legendary Creature - Spirit 2/2
|
||||
Flying
|
||||
Creatures you control have "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability."
|
||||
*/
|
||||
addCard(Zone.BATTLEFIELD, playerA, kira);
|
||||
addCard(Zone.HAND, playerA, shock);
|
||||
addCard(Zone.HAND, playerA, shock);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {source} deals 3 damage to target creature or player.", kira);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {source} deals 3 damage to target creature or player.", kira); // Ugin ability
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
|
||||
execute();
|
||||
|
||||
Permanent _kira = getPermanent(kira, playerA.getId());
|
||||
Assert.assertNotNull(_kira);
|
||||
|
||||
|
||||
assertPermanentCount(playerA, kira, 1);
|
||||
assertCounterCount(playerA, ugin, CounterType.LOYALTY, 9);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import org.junit.Assert;
|
|||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Test restrictions for choosing attackers and blockers.
|
||||
*
|
||||
|
|
@ -38,7 +41,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
* Frost
|
||||
*/
|
||||
@Test
|
||||
public void testWallofWrost() {
|
||||
public void testWallofFrost() {
|
||||
// Whenever Wall of Frost blocks a creature, that creature doesn't untap during its controller's next untap step.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Wall of Frost"); // 0/7
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm");
|
||||
|
|
@ -338,36 +341,36 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
*/
|
||||
@Test
|
||||
public void testMustAttackButCannotAttackAlone()
|
||||
{
|
||||
{
|
||||
/* Mogg Flunkies {1}{R} 3/3
|
||||
Creature — Goblin
|
||||
Mogg Flunkies can't attack or block alone.
|
||||
*/
|
||||
String flunkies = "Mogg Flunkies";
|
||||
|
||||
|
||||
/* Goblin Assault {2}{R}
|
||||
* Enchantment
|
||||
* Enchantment
|
||||
At the beginning of your upkeep, create a 1/1 red Goblin creature token with haste.
|
||||
Goblin creatures attack each turn if able.
|
||||
*/
|
||||
String gAssault = "Goblin Assault";
|
||||
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, flunkies);
|
||||
addCard(Zone.BATTLEFIELD, playerB, gAssault);
|
||||
|
||||
|
||||
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
|
||||
assertTapped(flunkies, false);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Reported bug: Tromokratis is unable to be blocked.
|
||||
*/
|
||||
@Test
|
||||
public void tromokratisBlockedByAll() {
|
||||
/*
|
||||
/*
|
||||
Tromokratis {5}{U}{U}
|
||||
Legendary Creature — Kraken 8/8
|
||||
Tromokratis has hexproof unless it's attacking or blocking.
|
||||
|
|
@ -376,30 +379,30 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
String tromokratis = "Tromokratis";
|
||||
String gBears = "Grizzly Bears"; // {1}{G} 2/2
|
||||
String memnite = "Memnite"; // {0} 1/1
|
||||
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, tromokratis);
|
||||
addCard(Zone.BATTLEFIELD, playerB, gBears);
|
||||
addCard(Zone.BATTLEFIELD, playerB, memnite);
|
||||
|
||||
|
||||
attack(1, playerA, tromokratis);
|
||||
block(1, playerB, gBears, tromokratis);
|
||||
block(1, playerB, memnite, tromokratis);
|
||||
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerB, gBears, 1);
|
||||
assertGraveyardCount(playerB, memnite, 1);
|
||||
assertTapped(tromokratis, true);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Reported bug: Tromokratis is unable to be blocked.
|
||||
*/
|
||||
@Test
|
||||
public void tromokratisNotBlockedByAll() {
|
||||
/*
|
||||
/*
|
||||
Tromokratis {5}{U}{U}
|
||||
Legendary Creature — Kraken 8/8
|
||||
Tromokratis has hexproof unless it's attacking or blocking.
|
||||
|
|
@ -409,24 +412,138 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
String gBears = "Grizzly Bears"; // {1}{G} 2/2
|
||||
String memnite = "Memnite"; // {0} 1/1
|
||||
String hGiant = "Hill Giant"; // {3}{R} 3/3
|
||||
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, tromokratis);
|
||||
addCard(Zone.BATTLEFIELD, playerB, gBears);
|
||||
addCard(Zone.BATTLEFIELD, playerB, memnite);
|
||||
addCard(Zone.BATTLEFIELD, playerB, hGiant);
|
||||
|
||||
|
||||
attack(2, playerB, hGiant); // forces a creature to be tapped so unable to block Tromokratis, which means it cannot be blocked at all
|
||||
attack(3, playerA, tromokratis);
|
||||
block(3, playerB, gBears, tromokratis);
|
||||
block(3, playerB, memnite, tromokratis);
|
||||
|
||||
|
||||
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
|
||||
assertLife(playerB, 12); // Hill Giant could not block it, so no other creature could block Tromokratis either
|
||||
assertPermanentCount(playerB, gBears, 1);
|
||||
assertPermanentCount(playerB, memnite, 1);
|
||||
assertTapped(tromokratis, true);
|
||||
assertTapped(hGiant, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void underworldCerberusBlockedByOneTest() {
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from his or her graveyard to his or her hand.
|
||||
*/
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite"); // 1/1
|
||||
|
||||
attack(3, playerA, "Underworld Cerberus");
|
||||
block(3, playerB, "Memnite", "Underworld Cerberus");
|
||||
|
||||
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
||||
try {
|
||||
execute();
|
||||
fail("Expected exception not thrown");
|
||||
} catch(UnsupportedOperationException e) {
|
||||
assertEquals("Underworld Cerberus is blocked by 1 creature(s). It has to be blocked by 3 or more.", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void underworldCerberusBlockedByTwoTest() {
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from his or her graveyard to his or her hand.
|
||||
*/
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 2); // 1/1
|
||||
|
||||
attack(3, playerA, "Underworld Cerberus");
|
||||
block(3, playerB, "Memnite", "Underworld Cerberus");
|
||||
block(3, playerB, "Memnite", "Underworld Cerberus");
|
||||
|
||||
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
||||
try {
|
||||
execute();
|
||||
fail("Expected exception not thrown");
|
||||
} catch(UnsupportedOperationException e) {
|
||||
assertEquals("Underworld Cerberus is blocked by 2 creature(s). It has to be blocked by 3 or more.", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void underworldCerberusBlockedByThreeTest() {
|
||||
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from his or her graveyard to his or her hand.
|
||||
*/
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 3); // 1/1
|
||||
|
||||
|
||||
// Blocked by 3 creatures - this is acceptable
|
||||
attack(3, playerA, "Underworld Cerberus");
|
||||
block(3, playerB, "Memnite", "Underworld Cerberus");
|
||||
block(3, playerB, "Memnite", "Underworld Cerberus");
|
||||
block(3, playerB, "Memnite", "Underworld Cerberus");
|
||||
|
||||
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Underworld Cerberus", 1);
|
||||
assertPermanentCount(playerB, "Memnite", 0);
|
||||
assertGraveyardCount(playerB, "Memnite", 3);
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void underworldCerberusBlockedByTenTest() {
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from his or her graveyard to his or her hand.
|
||||
*/
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 10); // 1/1
|
||||
|
||||
// Blocked by 10 creatures - this is acceptable as it's >3
|
||||
attack(3, playerA, "Underworld Cerberus");
|
||||
for(int i = 0; i < 10; i++) {
|
||||
block(3, playerB, "Memnite", "Underworld Cerberus");
|
||||
}
|
||||
|
||||
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Underworld Cerberus", 0);
|
||||
assertPermanentCount(playerB, "Memnite", 4);
|
||||
// Actually exiled when it dies
|
||||
assertGraveyardCount(playerA, "Underworld Cerberus", 0);
|
||||
assertExileCount(playerA, "Underworld Cerberus", 1);
|
||||
// Cards are returned to their owner's hand when Underworld Cerberus dies
|
||||
assertGraveyardCount(playerB, "Memnite", 0);
|
||||
assertHandCount(playerB, "Memnite", 6);
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package org.mage.test.combat;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public class CanBlockMultipleCreatures extends CardTestPlayerBase {
|
||||
|
||||
// test must be ignored until creature blocking multiple supported by test framework
|
||||
@Ignore
|
||||
@Test
|
||||
public void testCombat() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Watcher in the Web", 1);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Ulrich, Uncontested Alpha", 1); // 6/6
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Kessig Dire Swine", 1); // 6/6 (trample if delirium)
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Howlpack Wolf", 1); // 3/3
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Incorrigible Youths", 1); // 4/3
|
||||
|
||||
// Trample requirement for Kessig Dire Swine
|
||||
addCard(Zone.GRAVEYARD, playerB, "Forest", 1);
|
||||
addCard(Zone.GRAVEYARD, playerB, "Memnite", 1);
|
||||
addCard(Zone.GRAVEYARD, playerB, "Flight", 1);
|
||||
addCard(Zone.GRAVEYARD, playerB, "Drain Life", 1);
|
||||
|
||||
// Attack with all 4 creatures and block all with the Watcher in the Web
|
||||
attack(2, playerB, "Ulrich, Uncontested Alpha");
|
||||
attack(2, playerB, "Kessig Dire Swine");
|
||||
attack(2, playerB, "Howlpack Wolf");
|
||||
attack(2, playerB, "Incorrigible Youths");
|
||||
|
||||
block(2, playerA, "Watcher in the Web", "Ulrich, Uncontested Alpha");
|
||||
block(2, playerA, "Watcher in the Web", "Kessig Dire Swine");
|
||||
block(2, playerA, "Watcher in the Web", "Howlpack Wolf");
|
||||
block(2, playerA, "Watcher in the Web", "Incorrigible Youths");
|
||||
|
||||
setStopAt(2, PhaseStep.COMBAT_DAMAGE);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 19);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Reported bug: Night Market Guard was able to block a creature with Menace
|
||||
*/
|
||||
@Test
|
||||
public void testNightMarketGuardShouldNotBlockCreatureWithMenace()
|
||||
{
|
||||
/*
|
||||
Night Market Guard {3} 3/1
|
||||
Artifact Creature — Construct
|
||||
Night Market Guard can block an additional creature each combat.
|
||||
*/
|
||||
String nMarketGuard = "Night Market Guard";
|
||||
|
||||
/*
|
||||
Embraal Bruiser {1}{B}
|
||||
Creature - Human Warrior
|
||||
Embraal Bruiser enters the battlefield tapped.
|
||||
Embraal Bruiser has menace as long as you control an artifact.
|
||||
*/
|
||||
String eBruiser = "Embraal Bruiser";
|
||||
|
||||
/*
|
||||
{0} 1/1
|
||||
* Artifact Creature — Construct
|
||||
*/
|
||||
String memnite = "Memnite";
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, nMarketGuard);
|
||||
addCard(Zone.BATTLEFIELD, playerB, eBruiser);
|
||||
addCard(Zone.BATTLEFIELD, playerB, memnite); // only here to grant Embraal Menace
|
||||
|
||||
attack(4, playerB, eBruiser);
|
||||
block(4, playerA, nMarketGuard, eBruiser);
|
||||
|
||||
setStopAt(4, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertTapped(eBruiser, true);
|
||||
assertLife(playerA, 17); // could not block, so 3 damage goes through
|
||||
assertPermanentCount(playerA, nMarketGuard, 1);
|
||||
assertPermanentCount(playerB, eBruiser, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package org.mage.test.combat;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
* @author Simown
|
||||
*/
|
||||
public class CanBlockMultipleCreaturesTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void testMultipleBlockWithTrample() {
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Watcher in the Web", 1);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Ulrich, Uncontested Alpha", 1); // 6/6
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Kessig Dire Swine", 1); // 6/6 (trample if delirium)
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Howlpack Wolf", 1); // 3/3
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Incorrigible Youths", 1); // 4/3
|
||||
|
||||
// Trample requirement for Kessig Dire Swine
|
||||
addCard(Zone.GRAVEYARD, playerB, "Forest", 1);
|
||||
addCard(Zone.GRAVEYARD, playerB, "Memnite", 1);
|
||||
addCard(Zone.GRAVEYARD, playerB, "Flight", 1);
|
||||
addCard(Zone.GRAVEYARD, playerB, "Drain Life", 1);
|
||||
|
||||
// Attack with all 4 creatures and block all with the Watcher in the Web
|
||||
attack(2, playerB, "Kessig Dire Swine");
|
||||
attack(2, playerB, "Ulrich, Uncontested Alpha");
|
||||
attack(2, playerB, "Howlpack Wolf");
|
||||
attack(2, playerB, "Incorrigible Youths");
|
||||
|
||||
// BLOCKING ORDER MATTERS - the trampling creature must be selected to block first
|
||||
// You can manually change the blocking order but it's easier to assign them in order
|
||||
block(2, playerA, "Watcher in the Web", "Kessig Dire Swine");
|
||||
block(2, playerA, "Watcher in the Web", "Ulrich, Uncontested Alpha");
|
||||
block(2, playerA, "Watcher in the Web", "Howlpack Wolf");
|
||||
block(2, playerA, "Watcher in the Web", "Incorrigible Youths");
|
||||
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 19);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleBlockWithTrample2() {
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Watcher in the Web", 1);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Ulrich, Uncontested Alpha", 1); // 6/6
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Kessig Dire Swine", 1); // 6/6 (trample if delirium)
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Howlpack Wolf", 1); // 3/3
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Incorrigible Youths", 1); // 4/3
|
||||
|
||||
// Trample requirement for Kessig Dire Swine
|
||||
addCard(Zone.GRAVEYARD, playerB, "Forest", 1);
|
||||
addCard(Zone.GRAVEYARD, playerB, "Memnite", 1);
|
||||
addCard(Zone.GRAVEYARD, playerB, "Flight", 1);
|
||||
addCard(Zone.GRAVEYARD, playerB, "Drain Life", 1);
|
||||
|
||||
// Attack with all 4 creatures and block all with the Watcher in the Web
|
||||
attack(2, playerB, "Kessig Dire Swine");
|
||||
attack(2, playerB, "Ulrich, Uncontested Alpha");
|
||||
attack(2, playerB, "Howlpack Wolf");
|
||||
attack(2, playerB, "Incorrigible Youths");
|
||||
|
||||
// BLOCKING ORDER MATTERS - the trampling creature must be selected to block first
|
||||
block(2, playerA, "Watcher in the Web", "Kessig Dire Swine");
|
||||
block(2, playerA, "Watcher in the Web", "Ulrich, Uncontested Alpha");
|
||||
block(2, playerA, "Watcher in the Web", "Howlpack Wolf");
|
||||
// Don't block Incorrigible Youths
|
||||
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
// Damage 1 from Kessig Dire Swine + 4 from Incorrigible Youths
|
||||
assertLife(playerA, 15);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanOnlyBlockSingle() {
|
||||
|
||||
// Hundred-Handed One {2}{W}{W}
|
||||
// Monstrosity 3. {3}{W}{W}{W} (If this creature isn’t monstrous, put three +1/+1 counters on it and it becomes monstrous.)
|
||||
//As long as Hundred-Handed One is monstrous, it has reach and can block an additional ninety-nine creatures each combat.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Hundred-Handed One", 1);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Bronze Sable", 1); // 2/1
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Fabled Hero", 1); // 2/2 double strike
|
||||
|
||||
// Attack with all 4 creatures and try and block both with hundred-handed one
|
||||
attack(2, playerB, "Bronze Sable");
|
||||
attack(2, playerB, "Fabled Hero");
|
||||
|
||||
block(2, playerA, "Hundred-Handed One", "Bronze Sable");
|
||||
block(2, playerA, "Hundred-Handed One", "Fabled Hero");
|
||||
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
||||
// Will fail on purpose - we are trying to block too many creatures!
|
||||
try {
|
||||
execute();
|
||||
fail("Expected exception not thrown");
|
||||
} catch(UnsupportedOperationException e) {
|
||||
assertEquals("Hundred-Handed One cannot block Fabled Hero", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanBlockMultiple() {
|
||||
|
||||
// Hundred-Handed One {2}{W}{W}
|
||||
// Monstrosity 3. {3}{W}{W}{W} (If this creature isn’t monstrous, put three +1/+1 counters on it and it becomes monstrous.)
|
||||
// As long as Hundred-Handed One is monstrous, it has reach and can block an additional ninety-nine creatures each combat.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Hundred-Handed One", 1);
|
||||
// For monstrosity
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Bronze Sable", 1); // 2/1
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Fabled Hero", 1); // 2/2 double strike
|
||||
|
||||
// Make hundred-handed one monstrous
|
||||
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{W}{W}{W}: Monstrosity 3.");
|
||||
|
||||
// Attack with all 4 creatures and try and block both with hundred-handed one
|
||||
attack(2, playerB, "Bronze Sable");
|
||||
attack(2, playerB, "Fabled Hero");
|
||||
|
||||
block(2, playerA, "Hundred-Handed One", "Bronze Sable");
|
||||
block(2, playerA, "Hundred-Handed One", "Fabled Hero");
|
||||
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
||||
// Will not fail this time as hundred-handed one is monstrous and can block up to 100 creatures
|
||||
execute();
|
||||
|
||||
// Was a 3/5 but monstrosity 3
|
||||
assertPowerToughness(playerA, "Hundred-Handed One", 6, 8);
|
||||
// No one has been hit
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reported bug: Night Market Guard was able to block a creature with Menace
|
||||
*/
|
||||
@Test
|
||||
public void testNightMarketGuardShouldNotBlockCreatureWithMenace()
|
||||
{
|
||||
/*
|
||||
Night Market Guard {3} 3/1
|
||||
Artifact Creature — Construct
|
||||
Night Market Guard can block an additional creature each combat.
|
||||
*/
|
||||
String nMarketGuard = "Night Market Guard";
|
||||
|
||||
/*
|
||||
Embraal Bruiser {1}{B}
|
||||
Creature - Human Warrior
|
||||
Embraal Bruiser enters the battlefield tapped.
|
||||
Embraal Bruiser has menace as long as you control an artifact.
|
||||
*/
|
||||
String eBruiser = "Embraal Bruiser";
|
||||
|
||||
/*
|
||||
{0} 1/1
|
||||
* Artifact Creature — Construct
|
||||
*/
|
||||
String memnite = "Memnite";
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, nMarketGuard);
|
||||
addCard(Zone.BATTLEFIELD, playerB, eBruiser);
|
||||
addCard(Zone.BATTLEFIELD, playerB, memnite); // only here to grant Embraal Menace
|
||||
|
||||
attack(4, playerB, eBruiser);
|
||||
block(4, playerA, nMarketGuard, eBruiser);
|
||||
|
||||
setStopAt(4, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
||||
// Catch the illegal block
|
||||
try {
|
||||
execute();
|
||||
fail("Expected exception not thrown");
|
||||
} catch(UnsupportedOperationException e) {
|
||||
assertEquals("Embraal Bruiser is blocked by 1 creature(s). It has to be blocked by 2 or more.", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -28,13 +28,10 @@
|
|||
package org.mage.test.player;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
|
|
@ -65,15 +62,10 @@ import mage.counters.Counter;
|
|||
import mage.counters.Counters;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterAttackingCreature;
|
||||
import mage.filter.common.FilterCreatureForCombat;
|
||||
import mage.filter.common.FilterCreatureForCombatBlock;
|
||||
import mage.filter.common.FilterCreatureOrPlayer;
|
||||
import mage.filter.common.FilterPlaneswalkerPermanent;
|
||||
import mage.filter.common.*;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.filter.predicate.permanent.AttackingPredicate;
|
||||
import mage.filter.predicate.permanent.BlockingPredicate;
|
||||
import mage.filter.predicate.permanent.SummoningSicknessPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.Graveyard;
|
||||
|
|
@ -181,6 +173,11 @@ public class TestPlayer implements Player {
|
|||
return null;
|
||||
}
|
||||
|
||||
// Gets all permanents that match the filter
|
||||
protected List<Permanent> findPermanents(FilterPermanent filter, UUID controllerId, Game game) {
|
||||
return game.getBattlefield().getAllActivePermanents(filter, controllerId, game);
|
||||
}
|
||||
|
||||
private boolean checkExecuteCondition(String[] groups, Game game) {
|
||||
if (groups[2].startsWith("spellOnStack=")) {
|
||||
String spellOnStack = groups[2].substring(13);
|
||||
|
|
@ -289,7 +286,7 @@ public class TestPlayer implements Player {
|
|||
int index = 0;
|
||||
int targetsSet = 0;
|
||||
for (String targetName : targetList) {
|
||||
Mode selectedMode = null;
|
||||
Mode selectedMode;
|
||||
if (targetName.startsWith("mode=")) {
|
||||
int modeNr = Integer.parseInt(targetName.substring(5, 6));
|
||||
if (modeNr == 0 || modeNr > (ability.getModes().isEachModeMoreThanOnce() ? ability.getModes().getSelectedModes().size() : ability.getModes().size())) {
|
||||
|
|
@ -561,25 +558,86 @@ public class TestPlayer implements Player {
|
|||
@Override
|
||||
public void selectBlockers(Game game, UUID defendingPlayerId) {
|
||||
UUID opponentId = game.getOpponents(computerPlayer.getId()).iterator().next();
|
||||
// Map of Blocker reference -> list of creatures blocked
|
||||
Map<MageObjectReference, List<MageObjectReference>> blockedCreaturesByCreature = new HashMap<>();
|
||||
for (PlayerAction action : actions) {
|
||||
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("block:")) {
|
||||
String command = action.getAction();
|
||||
command = command.substring(command.indexOf("block:") + 6);
|
||||
String[] groups = command.split("\\$");
|
||||
FilterCreatureForCombatBlock filterBlocker = new FilterCreatureForCombatBlock();
|
||||
filterBlocker.add(new NamePredicate(groups[0]));
|
||||
filterBlocker.add(Predicates.not(new BlockingPredicate()));
|
||||
Permanent blocker = findPermanent(filterBlocker, computerPlayer.getId(), game);
|
||||
if (blocker != null) {
|
||||
FilterAttackingCreature filterAttacker = new FilterAttackingCreature();
|
||||
filterAttacker.add(new NamePredicate(groups[1]));
|
||||
Permanent attacker = findPermanent(filterAttacker, opponentId, game);
|
||||
if (attacker != null) {
|
||||
computerPlayer.declareBlocker(defendingPlayerId, blocker.getId(), attacker.getId(), game);
|
||||
FilterAttackingCreature filterAttacker = new FilterAttackingCreature();
|
||||
filterAttacker.add(new NamePredicate(groups[1]));
|
||||
Permanent attacker = findPermanent(filterAttacker, opponentId, game);
|
||||
FilterControlledPermanent filterPermanent = new FilterControlledPermanent();
|
||||
filterPermanent.add(new NamePredicate(groups[0]));
|
||||
// Get all possible blockers - those with the same name on the battlefield
|
||||
List<Permanent> possibleBlockers = findPermanents(filterPermanent, computerPlayer.getId(), game);
|
||||
if (!possibleBlockers.isEmpty() && attacker != null) {
|
||||
boolean blockerFound = false;
|
||||
for(Permanent blocker: possibleBlockers) {
|
||||
// See if it can block this creature
|
||||
if(canBlockAnother(game, blocker, attacker, blockedCreaturesByCreature)) {
|
||||
computerPlayer.declareBlocker(defendingPlayerId, blocker.getId(), attacker.getId(), game);
|
||||
blockerFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we haven't found a blocker then an illegal block has been made in the test
|
||||
if(!blockerFound) {
|
||||
throw new UnsupportedOperationException(groups[0] + " cannot block " + groups[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkMultipleBlockers(game, blockedCreaturesByCreature);
|
||||
}
|
||||
|
||||
// Checks if a creature can block at least one more creature
|
||||
private boolean canBlockAnother(Game game, Permanent blocker, Permanent attacker, Map<MageObjectReference, List<MageObjectReference>> blockedCreaturesByCreature) {
|
||||
MageObjectReference blockerRef = new MageObjectReference(blocker, game);
|
||||
// See if we already reference this blocker
|
||||
for(MageObjectReference r: blockedCreaturesByCreature.keySet()) {
|
||||
if(r.equals(blockerRef)) {
|
||||
// Use the existing reference if we do
|
||||
blockerRef = r;
|
||||
}
|
||||
}
|
||||
List<MageObjectReference> blocked = blockedCreaturesByCreature.getOrDefault(blockerRef, new ArrayList<>());
|
||||
int numBlocked = blocked.size();
|
||||
// Can't block any more creatures
|
||||
if(++numBlocked > blocker.getMaxBlocks()) {
|
||||
return false;
|
||||
}
|
||||
// Add the attacker reference to the list of creatures this creature is blocking
|
||||
blocked.add(new MageObjectReference(attacker, game));
|
||||
blockedCreaturesByCreature.put(blockerRef, blocked);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for Menace type abilities - if creatures can be blocked by >X or <Y only
|
||||
private void checkMultipleBlockers(Game game, Map<MageObjectReference, List<MageObjectReference>> blockedCreaturesByCreature) {
|
||||
// Stores the total number of blockers for each attacker
|
||||
Map<MageObjectReference, Integer> blockersForAttacker = new HashMap<>();
|
||||
// Calculate the number of blockers each attacker has
|
||||
for(List<MageObjectReference> attackers : blockedCreaturesByCreature.values()) {
|
||||
for(MageObjectReference mr: attackers) {
|
||||
Integer blockers = blockersForAttacker.getOrDefault(mr, 0);
|
||||
blockersForAttacker.put(mr, blockers+1);
|
||||
}
|
||||
}
|
||||
// Check each attacker is blocked by an allowed amount of creatures
|
||||
for(Map.Entry<MageObjectReference, Integer> entry: blockersForAttacker.entrySet()) {
|
||||
Permanent attacker = entry.getKey().getPermanent(game);
|
||||
Integer blockers = entry.getValue();
|
||||
// If getMaxBlockedBy() == 0 it means any number of creatures can block this creature
|
||||
if(attacker.getMaxBlockedBy() != 0 && blockers > attacker.getMaxBlockedBy()) {
|
||||
throw new UnsupportedOperationException(attacker.getName() + " is blocked by " + blockers + " creature(s). It can only be blocked by " + attacker.getMaxBlockedBy() + " or less.");
|
||||
}
|
||||
else if(blockers < attacker.getMinBlockedBy()) {
|
||||
throw new UnsupportedOperationException(attacker.getName() + " is blocked by " + blockers + " creature(s). It has to be blocked by " + attacker.getMinBlockedBy() + " or more.");
|
||||
}
|
||||
}
|
||||
// No errors raised - all the blockers pass the test!
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -805,9 +805,19 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
|||
* @param count Expected count.
|
||||
*/
|
||||
public void assertHandCount(Player player, String cardName, int count) throws AssertionError {
|
||||
FilterCard filter = new FilterCard();
|
||||
filter.add(new NamePredicate(cardName));
|
||||
int actual = currentGame.getPlayer(player.getId()).getHand().count(filter, player.getId(), currentGame);
|
||||
int actual;
|
||||
if (cardName.contains("//")) { // special logic for cheched split cards, because in game logic of card name filtering is different than for test
|
||||
actual = 0;
|
||||
for (Card card : currentGame.getPlayer(player.getId()).getHand().getCards(currentGame)) {
|
||||
if (card.getName().equals(cardName)) {
|
||||
actual++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FilterCard filter = new FilterCard();
|
||||
filter.add(new NamePredicate(cardName));
|
||||
actual = currentGame.getPlayer(player.getId()).getHand().count(filter, player.getId(), currentGame);
|
||||
}
|
||||
Assert.assertEquals("(Hand) Card counts for card " + cardName + " for " + player.getName() + " are not equal ", count, actual);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue