Merge branch 'master' into Network_Upgrade

Conflicts:
	Mage.Client/src/main/java/mage/client/MageFrame.java
	Mage.Client/src/main/java/mage/client/chat/ChatPanel.java
	Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java
	Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java
	Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java
	Mage.Client/src/main/java/mage/client/table/TablesPanel.java
	Mage.Common/src/mage/remote/SessionImpl.java
	Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java
This commit is contained in:
betasteward 2015-06-12 08:56:26 -04:00
commit 38da157f8c
807 changed files with 13691 additions and 4039 deletions

View file

@ -0,0 +1,84 @@
/*
* 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.AI.basic;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBaseAI;
/**
*
* @author LevelX2
*/
public class CastCreaturesTest extends CardTestPlayerBaseAI {
/**
* Tests that the creature is cast if enough mana is available
*/
@Test
public void testSimpleCast() {
addCard(Zone.HAND, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Silvercoat Lion", 1);
}
@Test
public void testSimpleCast2() {
addCard(Zone.HAND, playerA, "Silvercoat Lion");
addCard(Zone.HAND, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Silvercoat Lion", 2);
}
@Test
@Ignore // AI should cast Myr Enforcer -> Check why it does not
public void testSimpleCast3() {
// Affinity for artifacts (This spell costs less to cast for each artifact you control.)
addCard(Zone.HAND, playerA, "Myr Enforcer");
// {T}: Add to your mana pool.
// {T}, {1}, Sacrifice Mind Stone: Draw a card.
addCard(Zone.BATTLEFIELD, playerA, "Mind Stone", 3);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Myr Enforcer", 1);
}
}

View file

@ -0,0 +1,78 @@
/*
* 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.cards.abilities.add;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class AddAbilitiesToNonPermanentsTest extends CardTestPlayerBase {
/**
* With Teferi, Mage of Zhalfir on the battlefield it has to be possible to search for
* a God of Storms in the deck by using Mystical Teachings.
*
*/
@Test
public void testSearchForCardWithFlashInLibrary() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
// Flash
// Creature cards you own that aren't on the battlefield have flash.
// Each opponent can cast spells only any time he or she could cast a sorcery.
addCard(Zone.BATTLEFIELD, playerA, "Teferi, Mage of Zhalfir");
// Search your library for an instant card or a card with flash, reveal it, and put it into your hand. Then shuffle your library.
// Flashback {5}{B}
addCard(Zone.HAND, playerA, "Mystical Teachings"); // "{3}{U}"
addCard(Zone.LIBRARY, playerA, "Keranos, God of Storms");
addCard(Zone.LIBRARY, playerA, "Plains", 3);
skipInitShuffling();
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mystical Teachings");
addTarget(playerA, "Keranos, God of Storms");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Mystical Teachings", 1);
assertHandCount(playerA, "Keranos, God of Storms", 1);
}
}

View file

@ -232,6 +232,63 @@ public class CursesTest extends CardTestPlayerBase {
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPermanentCount(playerA, "Curse of Misfortunes", 1);
assertPermanentCount(playerA, "Curse of Bloodletting", 1); }
assertPermanentCount(playerA, "Curse of Bloodletting", 1);
}
@Test
public void testCurseOfDeathsHold() {
// Creatures enchanted player controls get -1/-1.
addCard(Zone.HAND, playerA, "Curse of Death's Hold");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB);
setStopAt(1, PhaseStep.END_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPermanentCount(playerA, "Curse of Death's Hold", 1);
assertPowerToughness(playerB, "Silvercoat Lion", 1, 1);
}
@Test
public void testCurseOfDeathsHold2() {
// Creatures enchanted player controls get -1/-1.
addCard(Zone.HAND, playerA, "Curse of Death's Hold");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
addCard(Zone.BATTLEFIELD, playerA, "Tasigur, the Golden Fang", 1);
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
addCard(Zone.BATTLEFIELD, playerB, "Forest", 3);
addCard(Zone.HAND, playerB, "Reclamation Sage");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Reclamation Sage");
addTarget(playerB, "Curse of Death's Hold");
// {2}{G/U}{G/U}: Put the top two cards of your library into your graveyard, then return a nonland card of an opponent's choice from your graveyard to your hand.
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{G/U}{G/U}: Put the top two cards");
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB);
setStopAt(3, PhaseStep.END_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertGraveyardCount(playerB, "Reclamation Sage", 1);
assertPermanentCount(playerA, "Curse of Death's Hold", 1);
assertGraveyardCount(playerA, 2);
assertPowerToughness(playerB, "Silvercoat Lion", 1, 1);
}
}

View file

@ -37,7 +37,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
*
* @author LevelX2
*/
public class CycleTest extends CardTestPlayerBase {
public class CyclingTest extends CardTestPlayerBase {
/**
* 702.28. Cycling
@ -60,7 +60,7 @@ public class CycleTest extends CardTestPlayerBase {
*/
@Test
public void CycleAndTriggerTest() {
public void cycleAndTriggerTest() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
// Destroy all creatures. They can't be regenerated. Draw a card for each creature destroyed this way.
// Cycling {3}{B}{B}
@ -82,15 +82,13 @@ public class CycleTest extends CardTestPlayerBase {
assertPermanentCount(playerB, "Pillarfield Ox", 1);
assertPowerToughness(playerB, "Pillarfield Ox", 0, 2);
}
/**
* Cycle from graveyard or battlefield may not work
*/
@Test
public void CycleFromGraveyard() {
public void cycleFromGraveyard() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
// Destroy all creatures. They can't be regenerated. Draw a card for each creature destroyed this way.
// Cycling {3}{B}{B}
@ -113,5 +111,32 @@ public class CycleTest extends CardTestPlayerBase {
assertPermanentCount(playerB, "Disciple Of Grace", 1);
}
/**
* Type cycling for sliver
*/
@Test
public void cycleFromHomingSliver() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
// Each Sliver card in each player's hand has slivercycling {3}.
addCard(Zone.BATTLEFIELD, playerA, "Homing Sliver");
// All Sliver creatures have flying.
addCard(Zone.HAND, playerA, "Winged Sliver");
addCard(Zone.LIBRARY, playerA, "Horned Sliver");
addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 10);
skipInitShuffling();
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Slivercycling {3}");
addTarget(playerA, "Horned Sliver");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertHandCount(playerA, 1);
assertGraveyardCount(playerA, "Winged Sliver", 1);
assertHandCount(playerA, "Horned Sliver", 1); // searched by slivercyclibng
}
}

View file

@ -91,7 +91,7 @@ public class FadingTest extends CardTestPlayerBase {
public void testFadesAway() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
addCard(Zone.HAND, playerA, "Blastoderm");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blastoderm");
setStopAt(9, PhaseStep.BEGIN_COMBAT);

View file

@ -0,0 +1,78 @@
/*
* 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.cards.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class FlashbackTest extends CardTestPlayerBase {
/**
* Fracturing Gust is bugged. In a match against Affinity, it worked
* properly when cast from hand. When I cast it from graveyard c/o
* Snapcaster Mage flashback, it destroyed my opponent's Darksteel Citadels,
* which it did not do when cast from my hand.
*/
@Test
public void testSnapcasterMageWithFracturingGust() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
// Destroy all artifacts and enchantments. You gain 2 life for each permanent destroyed this way.
addCard(Zone.GRAVEYARD, playerA, "Fracturing Gust");
addCard(Zone.BATTLEFIELD, playerA, "Berserkers' Onslaught", 1);
addCard(Zone.BATTLEFIELD, playerB, "Darksteel Citadel", 1);
// When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost.
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage");
setChoice(playerA, "Fracturing Gust");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback {2}{G/W}{G/W}{G/W}"); // now snapcaster mage is died so -13/-13
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Snapcaster Mage", 1);
assertGraveyardCount(playerA, "Berserkers' Onslaught", 1);
assertPermanentCount(playerB, "Darksteel Citadel", 1);
assertExileCount("Fracturing Gust", 1);
}
}

View file

@ -0,0 +1,79 @@
/*
* 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.cards.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class HeroicTest extends CardTestPlayerBase {
/**
* When casting Dromoka's Command targeting two of my own Heroic creatures, only one of them triggers.
* It appears to be the one targeted with mode 4 (fight) rather than the one targeted with mode 3 (+1/+1 counter).
* Screenshot attached. Reproducible.
*/
@Test
public void testHeroicWithModal() {
// Heroic - Whenever you cast a spell that targets Favored Hoplite, put a +1/+1 counter on Favored Hoplite and prevent all damage that would be dealt to it this turn.
addCard(Zone.BATTLEFIELD, playerA, "Favored Hoplite", 1); // 1/2
// Heroic Whenever you cast a spell that targets Lagonna-Band Trailblazer, put a +1/+1 counter on Lagonna-Band Trailblazer.
addCard(Zone.BATTLEFIELD, playerA, "Lagonna-Band Trailblazer"); // 0/4
// Mode 3 = Put a +1/+1 counter on target creature
// Mode 4 = Target creature you control fights target creature you don't control
addCard(Zone.HAND, playerA, "Dromoka's Command", 1); // {G}{W}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dromoka's Command", "mode=3Lagonna-Band Trailblazer^mode=4Favored Hoplite^Silvercoat Lion");
// Silvercoat lion will be set by AI as only possible target
setModeChoice(playerA, "3");
setModeChoice(playerA, "4");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Dromoka's Command", 1);
assertPowerToughness(playerA, "Favored Hoplite", 2, 3);
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
assertPowerToughness(playerA, "Lagonna-Band Trailblazer", 2, 6);
}
}

View file

@ -76,7 +76,9 @@ public class HexproofTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerB, "Island", 4);
addCard(Zone.HAND, playerB, "Into the Void");
// Return up to two target creatures to their owners' hands.
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Into the Void", "Elder of Laurels^Arbor Elf");
// Target creature you control gets +1/+1 and gains hexproof until end of turn. (It can't be the target of spells or abilities your opponents control.)
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Ranger's Guile", "Elder of Laurels");
setStopAt(2, PhaseStep.END_TURN);

View file

@ -0,0 +1,115 @@
/*
* 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.cards.abilities.keywords;
import mage.abilities.keyword.IntimidateAbility;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class LandfallTest extends CardTestPlayerBase {
@Test
public void testNormalUse() {
addCard(Zone.BATTLEFIELD, playerA, "Plains",3);
addCard(Zone.HAND, playerA, "Plains");
// Instant - {1}{W}
// Target player gains 4 life.
// Landfall - If you had a land enter the battlefield under your control this turn, that player gains 8 life instead.
addCard(Zone.HAND, playerA, "Rest for the Weary",2);
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest for the Weary");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest for the Weary");
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Plains", 4);
assertGraveyardCount(playerA, "Rest for the Weary", 2);
assertLife(playerA, 32); // + 8 from 1 turn + 4 from second turn
assertLife(playerB, 20);
}
/**
* If you Hive Mind an opponent's Rest for the Weary and redirect its target to yourself when it's not your turn,
* the game spits out this message and rolls back to before Rest for the Weary was cast.
*
*/
@Test
public void testHiveMind() {
addCard(Zone.BATTLEFIELD, playerA, "Plains",2);
// Whenever a player casts an instant or sorcery spell, each other player copies that spell. Each of those players may choose new targets for his or her copy.
addCard(Zone.BATTLEFIELD, playerB, "Hive Mind");
// Instant - {1}{W}
// Target player gains 4 life.
// Landfall - If you had a land enter the battlefield under your control this turn, that player gains 8 life instead.
addCard(Zone.HAND, playerA, "Rest for the Weary",1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest for the Weary");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Rest for the Weary", 1);
assertLife(playerA, 24);
assertLife(playerB, 24);
}
@Test
public void testSurrakarMarauder() {
// Landfall - Whenever a land enters the battlefield under your control, Surrakar Marauder gains intimidate until end of turn.
addCard(Zone.BATTLEFIELD, playerA, "Surrakar Marauder",1);
addCard(Zone.HAND, playerA, "Plains");
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Plains", 1);
assertAbility(playerA, "Surrakar Marauder", IntimidateAbility.getInstance(), true);
assertLife(playerA, 20);
assertLife(playerB, 20);
}
}

View file

@ -362,4 +362,40 @@ public class ManifestTest extends CardTestPlayerBase {
assertPermanentCount(playerB, "", 1);
}
/**
* Whisperwood Elemental - Its sacrifice ability doesn't work..
*
*/
@Test
public void testWhisperwoodElemental() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
// Seismic Rupture deals 2 damage to each creature without flying.
addCard(Zone.HAND, playerA, "Seismic Rupture", 1);
// At the beginning of your end step, manifest the top card of your library.
// Sacrifice Whisperwood Elemental: Until end of turn, face-up, nontoken creatures you control gain "When this creature dies, manifest the top card of your library."
addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1);
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 2);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Sacrifice");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Seismic Rupture");
setStopAt(1, PhaseStep.END_TURN);
execute();
// no life gain
assertLife(playerA, 20);
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Seismic Rupture", 1);
assertGraveyardCount(playerB, "Whisperwood Elemental", 1);
assertGraveyardCount(playerB, "Silvercoat Lion", 2);
assertPermanentCount(playerB, "", 2);
}
}

View file

@ -0,0 +1,69 @@
/*
* 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.cards.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class MetalcraftTest extends CardTestPlayerBase {
/**
* Rusted Relic or Blinkmoth nexus is bugged
* Either Relic does not see Blinkmoth as an artifact or it does not turn
* into one when it should.
*
*/
@Test
public void testMetalcraftFromBlinkmoth() {
addCard(Zone.BATTLEFIELD, playerA, "Darksteel Citadel",1);
// Metalcraft - {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts
addCard(Zone.BATTLEFIELD, playerA, "Rusted Relic", 1);
// {T}: Add {1}to your mana pool.
// {1}: Blinkmoth Nexus becomes a 1/1 Blinkmoth artifact creature with flying until end of turn. It's still a land.
// {1}, {T}: Target Blinkmoth creature gets +1/+1 until end of turn.
addCard(Zone.BATTLEFIELD, playerA, "Blinkmoth Nexus", 1);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: Until end of turn {this} becomes ");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerA, "Blinkmoth Nexus", 1, 1);
assertPowerToughness(playerA, "Rusted Relic", 5, 5);
}
}

View file

@ -29,7 +29,6 @@ package org.mage.test.cards.abilities.oneshot.damage;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
@ -83,4 +82,23 @@ public class SatyrFiredancerTest extends CardTestPlayerBase {
assertPermanentCount(playerB, "Silvercoat Lion", 1);
}
@Test
public void testDamageFromOtherCreature() {
// Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals that much damage to target creature that player controls.
addCard(Zone.BATTLEFIELD, playerA, "Satyr Firedancer");
// {T}: Prodigal Pyromancer deals 1 damage to target creature or player.
addCard(Zone.BATTLEFIELD, playerA, "Prodigal Pyromancer", 1);
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", playerB);
addTarget(playerA, playerB);
setStopAt(3, PhaseStep.END_TURN);
execute();
assertLife(playerA, 20);
assertLife(playerB, 19);
}
}

View file

@ -30,7 +30,8 @@ package org.mage.test.cards.abilities.other;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Ignore;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
@ -50,10 +51,11 @@ public class MycosynthGolemTest extends CardTestPlayerBase {
*
*/
@Ignore // at this time player.getPlayable() does not account for spells that gain abilities
// @Ignore // at this time player.getPlayable() does not account for spells that gain abilities
@Test
public void testSpellsAffinity() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mycosynth Golem");
addCard(Zone.HAND, playerA, "Alpha Myr");
@ -64,6 +66,17 @@ public class MycosynthGolemTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Alpha Myr", 1);
assertHandCount(playerA, "Alpha Myr", 0);
Permanent mountain = getPermanent("Mountain", playerA);
Permanent forest = getPermanent("Forest", playerA);
int tappedLands = 0;
if (mountain.isTapped()) {
tappedLands++;
}
if (forest.isTapped()) {
tappedLands++;
}
Assert.assertEquals("only one land may be tapped because the cost reduction", 1, tappedLands);
}

View file

@ -112,8 +112,8 @@ public class NecromancyTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerA, "Disenchant");
addCard(Zone.GRAVEYARD, playerA, "Craw Wurm");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Necromancy");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy"); // enchanting the Craw Wurm
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Necromancy"); // if Necromancy leaves, the enchanted creature has to leave too
setStopAt(1, PhaseStep.END_TURN);
execute();

View file

@ -79,4 +79,49 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Worldheart Phoenix", 1);
}
@Test
public void testNarsetEnlightenedMaster() {
// First strike
// Hexproof
// Whenever Narset, Enlightented Master attacks, exile the top four cards of your library. Until end of turn, you may cast noncreature cards exiled with Narset this turn without paying their mana costs.
addCard(Zone.BATTLEFIELD, playerB, "Narset, Enlightened Master", 1);
skipInitShuffling();
addCard(Zone.LIBRARY, playerB, "Silvercoat Lion");
addCard(Zone.LIBRARY, playerB, "Abzan Banner");
// Ferocious - If you control a creature with power 4 or greater, you may cast Dragon Grip as though it had flash. (You may cast it any time you could cast an instant.)
// Enchant creature
// Enchanted creature gets +2/+0 and has first strike.
addCard(Zone.LIBRARY, playerB, "Dragon Grip");
// You gain 2 life for each creature you control.
addCard(Zone.LIBRARY, playerB, "Peach Garden Oath");
addCard(Zone.LIBRARY, playerB, "Plains");
attack(2, playerB, "Narset, Enlightened Master");
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Silvercoat Lion"); // can't be cast from exile
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Abzan Banner"); // can be cast from exile
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Dragon Grip", "Narset, Enlightened Master"); // can be cast from exile
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Peach Garden Oath"); // can be cast from exile
setStopAt(2, PhaseStep.END_TURN);
execute();
assertExileCount("Silvercoat Lion", 1);
assertPermanentCount(playerB, "Abzan Banner", 1);
assertPermanentCount(playerB, "Dragon Grip", 1);
assertGraveyardCount(playerB, "Peach Garden Oath", 1);
assertPowerToughness(playerB, "Narset, Enlightened Master", 5, 2);
assertHandCount(playerB, "Plains", 1);
assertLife(playerA, 17);
assertLife(playerB, 22);
}
}

View file

@ -0,0 +1,83 @@
/*
* 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.cards.continuous;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class MightOfOldKrosaTest extends CardTestPlayerBase {
@Test
public void testTwiceMightOfOldKrosaBeginCombat() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
addCard(Zone.HAND, playerA, "Might of Old Krosa", 2);
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Might of Old Krosa", "Silvercoat Lion");
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Might of Old Krosa", "Silvercoat Lion");
setStopAt(1, PhaseStep.END_COMBAT);
execute();
assertGraveyardCount(playerA, "Might of Old Krosa", 2);
assertPermanentCount(playerA, "Silvercoat Lion", 1);
assertPowerToughness(playerA, "Silvercoat Lion", 6, 6);
}
/**
* Threw two Might of old Krosa's onto a creature, but only one had any effect.
*/
@Test
public void testTwiceMightOfOldKrosa() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
addCard(Zone.HAND, playerA, "Might of Old Krosa", 2);
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Might of Old Krosa", "Silvercoat Lion");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Might of Old Krosa", "Silvercoat Lion");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Might of Old Krosa", 2);
assertPermanentCount(playerA, "Silvercoat Lion", 1);
assertPowerToughness(playerA, "Silvercoat Lion", 10, 10);
}
}

View file

@ -0,0 +1,250 @@
/*
* 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.cards.continuous;
import mage.cards.Card;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class PaintersServantTest extends CardTestPlayerBase {
/**
* Test that the added color is applied as Painter's Servant is on the battlefield
*/
@Test
public void testColorSet() {
// As Painter's Servant enters the battlefield, choose a color.
// All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors.
addCard(Zone.HAND, playerA, "Painter's Servant", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
addCard(Zone.HAND, playerA, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");
addCard(Zone.HAND, playerB, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant");
setChoice(playerA, "Blue");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Painter's Servant", 1);
Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA);
Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite());
Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue());
silvercoatLion = getPermanent("Silvercoat Lion", playerB);
Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite());
Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue());
for(Card card: playerA.getLibrary().getCards(currentGame)) {
Assert.assertEquals(card.getName() + " should be blue",true, card.getColor(currentGame).isBlue());
}
for(Card card: playerB.getLibrary().getCards(currentGame)) {
Assert.assertEquals(card.getName() + " should be blue",true, card.getColor(currentGame).isBlue());
}
for(Card card: playerA.getHand().getCards(currentGame)) {
Assert.assertEquals(true, card.getColor(currentGame).isRed());
Assert.assertEquals(true, card.getColor(currentGame).isBlue());
}
for(Card card: playerB.getHand().getCards(currentGame)) {
Assert.assertEquals(true, card.getColor(currentGame).isRed());
Assert.assertEquals(true, card.getColor(currentGame).isBlue());
}
for(Card card: playerA.getGraveyard().getCards(currentGame)) {
Assert.assertEquals(true, card.getColor(currentGame).isWhite());
Assert.assertEquals(true, card.getColor(currentGame).isBlue());
}
for(Card card: playerB.getGraveyard().getCards(currentGame)) {
Assert.assertEquals(true, card.getColor(currentGame).isWhite());
Assert.assertEquals(true, card.getColor(currentGame).isBlue());
}
}
/**
* Test that the added color is no longer applied as Painter's Servant has left the battlefield
*/
@Test
public void testColorReset() {
// As Painter's Servant enters the battlefield, choose a color.
// All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors.
addCard(Zone.HAND, playerA, "Painter's Servant", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
addCard(Zone.HAND, playerA, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");
addCard(Zone.HAND, playerB, "Lightning Bolt",2);
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
addCard(Zone.GRAVEYARD, playerB, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant");
setChoice(playerA, "Blue");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Painter's Servant");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Painter's Servant", 1);
Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA);
Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite());
Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isBlue());
silvercoatLion = getPermanent("Silvercoat Lion", playerB);
Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite());
Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isBlue());
for(Card card: playerA.getLibrary().getCards(currentGame)) {
Assert.assertEquals(card.getName() + " should not be blue",false, card.getColor(currentGame).isBlue());
}
for(Card card: playerB.getLibrary().getCards(currentGame)) {
Assert.assertEquals(card.getName() + " should not be blue",false, card.getColor(currentGame).isBlue());
}
for(Card card: playerA.getHand().getCards(currentGame)) {
Assert.assertEquals(true, card.getColor(currentGame).isRed());
Assert.assertEquals(false, card.getColor(currentGame).isBlue());
}
for(Card card: playerB.getHand().getCards(currentGame)) {
Assert.assertEquals(true, card.getColor(currentGame).isRed());
Assert.assertEquals(false, card.getColor(currentGame).isBlue());
}
for(Card card: playerA.getGraveyard().getCards(currentGame)) {
if(card.getName().equals("Silvercoat Lion")) {
Assert.assertEquals(true, card.getColor(currentGame).isWhite());
Assert.assertEquals(false, card.getColor(currentGame).isBlue());
}
}
for(Card card: playerB.getGraveyard().getCards(currentGame)) {
if(card.getName().equals("Silvercoat Lion")) {
Assert.assertEquals(true, card.getColor(currentGame).isWhite());
Assert.assertEquals(false, card.getColor(currentGame).isBlue());
}
}
}
/**
* 5/1/2008 While Painter's Servant is on the battlefield, an effect that
* changes an object's colors will overwrite Painter's Servant's effect. For
* example, casting Cerulean Wisps on a creature will turn it blue,
* regardless of the color chosen for Painter's Servant.
*/
@Test
public void testColorOverwrite() {
// As Painter's Servant enters the battlefield, choose a color.
// All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors.
addCard(Zone.HAND, playerA, "Painter's Servant", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
// Target creature becomes blue until end of turn. Untap that creature.
// Draw a card.
addCard(Zone.HAND, playerB, "Cerulean Wisps", 1); // Instant {U}
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant");
setChoice(playerA, "Red");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cerulean Wisps", "Silvercoat Lion", "Painter's Servant", StackClause.WHILE_NOT_ON_STACK);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Painter's Servant", 1);
Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA);
Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isWhite());
Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isRed());
Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue());
}
/**
* Check color of spells
*/
@Test
public void testColorSpell() {
// As Painter's Servant enters the battlefield, choose a color.
// All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors.
addCard(Zone.HAND, playerA, "Painter's Servant", 1);
// Draw two cards.
addCard(Zone.HAND, playerA, "Divination", 1); // {U}{2}
addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
// Whenever a player casts a red spell, you may gain 1 life.
addCard(Zone.BATTLEFIELD, playerA, "Dragon's Claw");
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
// Target creature becomes blue until end of turn. Untap that creature.
// Draw a card.
addCard(Zone.HAND, playerB, "Cerulean Wisps", 1); // Instant {U}
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant");
setChoice(playerA, "Red");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cerulean Wisps", "Silvercoat Lion", "Painter's Servant", StackClause.WHILE_NOT_ON_STACK);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Divination", NO_TARGET, "Painter's Servant", StackClause.WHILE_NOT_ON_STACK);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Painter's Servant", 1);
assertGraveyardCount(playerA, "Divination", 1);
assertGraveyardCount(playerB, "Cerulean Wisps", 1);
assertLife(playerA, 22); // + 1 from Cerulean Wisps + 1 from Divination
Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA);
Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isWhite());
Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isRed());
Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue());
}
}

View file

@ -0,0 +1,63 @@
/*
* 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.cards.continuous;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class SilenceTest extends CardTestPlayerBase {
@Test
public void testSilence() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.HAND, playerA, "Silence");
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
addCard(Zone.HAND, playerB, "Silvercoat Lion", 1);
castSpell(2, PhaseStep.UPKEEP, playerA, "Silence");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion");
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Silence", 1);
assertHandCount(playerB, "Silvercoat Lion", 1);
assertPermanentCount(playerB, "Silvercoat Lion", 0);
}
}

View file

@ -31,14 +31,20 @@ public class TargetOpponentGainsControlTest extends CardTestPlayerBase {
@Test
public void testChangeControlEffectFromTwoCards() {
addCard(Zone.HAND, playerA, "Lightning Bolt", 3);
addCard(Zone.HAND, playerA, "Unhallowed Pact", 3);
addCard(Zone.BATTLEFIELD, playerA, "Treacherous Pit-Dweller");
// Enchant creature
// When enchanted creature dies, return that card to the battlefield under your control.
addCard(Zone.HAND, playerA, "Unhallowed Pact", 1); // {2}{B}
// Undying
// When Treacherous Pit-Dweller enters the battlefield from a graveyard, target opponent gains control of it.
addCard(Zone.BATTLEFIELD, playerA, "Treacherous Pit-Dweller"); // 4/3
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unhallowed Pact", "Treacherous Pit-Dweller");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller");
castSpell(1, PhaseStep.UPKEEP, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); // comes back with undying
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unhallowed Pact", "Treacherous Pit-Dweller");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); // Treacherous Pit-Dweller is now 5/4
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller");
setStopAt(1, PhaseStep.END_TURN);
@ -46,8 +52,10 @@ public class TargetOpponentGainsControlTest extends CardTestPlayerBase {
// went to graveyard
assertGraveyardCount(playerA, "Unhallowed Pact", 1);
assertGraveyardCount(playerA, "Lightning Bolt", 3);
// returned back
assertPermanentCount(playerA, "Treacherous Pit-Dweller", 1);
assertGraveyardCount(playerA, "Treacherous Pit-Dweller", 0);
assertPermanentCount(playerB, "Treacherous Pit-Dweller", 1); // opponent gets it because ETB of Dweller resolves always last
}
}

View file

@ -108,9 +108,11 @@ public class CloneTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 6);
addCard(Zone.BATTLEFIELD, playerB, "Forest", 1);
// Target creature you control gets +1/+1 and gains hexproof until end of turn. (It can't be the target of spells or abilities your opponents control.)
addCard(Zone.HAND, playerB, "Ranger's Guile");
addCard(Zone.HAND, playerA, "Clone");
// Return target nonland permanent to its owner's hand.
addCard(Zone.HAND, playerA, "Disperse");
addCard(Zone.BATTLEFIELD, playerB, "Nightmare", 1);

View file

@ -142,4 +142,39 @@ public class IsochronScepterTest extends CardTestPlayerBase {
assertLife(playerB, 20);
}
/**
* Resolving a Silence cast from exile via Isochron Scepter during my opponent's upkeep does
* not prevent that opponent from casting spells that turn.
*
*/
@Test
public void testSilence() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.HAND, playerA, "Isochron Scepter");
addCard(Zone.HAND, playerA, "Silence");
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
addCard(Zone.HAND, playerB, "Silvercoat Lion", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Isochron Scepter");
addTarget(playerA, "Silence");
activateAbility(2, PhaseStep.UPKEEP, playerA, "{2},{T}:");
setChoice(playerA, "Yes");
setChoice(playerA, "Yes");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion");
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Isochron Scepter", 1);
assertExileCount("Silence", 1);
assertHandCount(playerB, "Silvercoat Lion", 1);
assertPermanentCount(playerB, "Silvercoat Lion", 0);
}
}

View file

@ -316,7 +316,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Frost Titan");
addCard(Zone.HAND, playerA, "Terror");
// {1}{U} - Target creature gains shroud until end of turn and can't be blocked this turn.
addCard(Zone.HAND, playerA, "Veil of Secrecy");
addCard(Zone.HAND, playerA, "Veil of Secrecy");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
@ -328,32 +328,31 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
setChoice(playerB, "Frost Titan");
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Terror", "Frost Titan"); // of player Bs Phantasmal Image copying Frost Titan
// should be countered if not paying {2}
// should be countered if not paying {2}
setStopAt(2, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Veil of Secrecy", 1);
assertGraveyardCount(playerA, "Terror", 1);
assertLife(playerB, 20);
assertLife(playerA, 20);
assertPermanentCount(playerA, "Frost Titan", 1);
assertPermanentCount(playerA, "Frost Titan", 1);
assertGraveyardCount(playerB, "Phantasmal Image", 1); // if triggered ability did not work, the Titan would be in the graveyard instaed
}
// I've casted a Phantasmal Image targeting opponent's Wurmcoil Engine
// When my Phantasmal Image died, it didn't triggered the Wurmcoil Engine's last ability
// (When Wurmcoil Engine dies, put a 3/3 colorless Wurm artifact creature token with deathtouch and
// a 3/3 colorless Wurm artifact creature token with lifelink onto the battlefield.)
@Test
public void testDiesTriggeredAbilities() {
addCard(Zone.BATTLEFIELD, playerA, "Wurmcoil Engine");
// Destroy target artifact or enchantment.
// Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn.
addCard(Zone.HAND, playerA, "Public Execution");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6);
@ -364,22 +363,212 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
setChoice(playerB, "Wurmcoil Engine");
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Wurmcoil Engine"); // of player Bs Phantasmal Image copying Frost Titan
// should be countered if not paying {2}
// should be countered if not paying {2}
setStopAt(2, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Public Execution", 1);
assertLife(playerB, 20);
assertLife(playerA, 20);
assertPermanentCount(playerA, "Wurmcoil Engine", 1);
assertPermanentCount(playerA, "Wurmcoil Engine", 1);
assertGraveyardCount(playerB, "Phantasmal Image", 1);
assertPermanentCount(playerB, "Wurm", 2); // if triggered ability did not work, the Titan would be in the graveyard instaed
}
/**
* Phantasmal Image is not regestering Leave the battlefield triggers,
* persist and undying triggers
*/
@Test
public void testLeavesTheBattlefieldTriggeredAbilities() {
// Shadow (This creature can block or be blocked by only creatures with shadow.)
// When Thalakos Seer leaves the battlefield, draw a card.
addCard(Zone.BATTLEFIELD, playerA, "Thalakos Seer");
// Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn.
addCard(Zone.HAND, playerA, "Public Execution");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6);
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
addCard(Zone.HAND, playerB, "Phantasmal Image");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted
setChoice(playerB, "Thalakos Seer");
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Thalakos Seer");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Public Execution", 1);
assertLife(playerB, 20);
assertLife(playerA, 20);
assertPermanentCount(playerA, "Thalakos Seer", 1);
assertGraveyardCount(playerB, "Phantasmal Image", 1);
assertHandCount(playerB, 2); // 1 from draw turn 2 and 1 from Thalakos Seer leaves the battlefield trigger
}
/**
* Action
* Game State 1 -----------------> Game State 2
* (On 'field) (Move to GY) (In graveyard)
*
* LTB abilities such as Persist are expceptional in that they trigger based on their existence and
* state of objects before the event (Game State 1, when the card is on the battlefield) rather than
* after (Game State 2, when the card is in the graveyard). It doesn't matter that the LTB ability
* doesn't exist in Game State 2. [CR 603.6d]
*
* 603.6d Normally, objects that exist immediately after an event are checked to see if the event matched any trigger conditions.
* Continuous effects that exist at that time are used to determine what the trigger conditions are and what the objects involved
* in the event look like. However, some triggered abilities must be treated specially. Leaves-the-battlefield abilities, abilities
* that trigger when a permanent phases out, abilities that trigger when an object that all players can see is put into a hand or
* library, abilities that trigger specifically when an object becomes unattached, abilities that trigger when a player loses control
* of an object, and abilities that trigger when a player planeswalks away from a plane will trigger based on their existence, and
* the appearance of objects, prior to the event rather than afterward. The game has to look back in time to determine if these abilities trigger.
*
* Example: Two creatures are on the battlefield along with an artifact that has the ability Whenever a creature dies, you gain 1 life.
* Someone plays a spell that destroys all artifacts, creatures, and enchantments. The artifacts ability triggers twice, even though
* the artifact goes to its owners graveyard at the same time as the creatures.
*
*/
@Test
public void testPersist() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
// When Kitchen Finks enters the battlefield, you gain 2 life.
// Persist (When this creature dies, if it had no -1/-1 counters on it, return it to the battlefield under its owner's control with a -1/-1 counter on it.)
addCard(Zone.HAND, playerA, "Kitchen Finks");
// Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn.
addCard(Zone.HAND, playerA, "Public Execution");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6);
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
// You may have Phantasmal Image enter the battlefield as a copy of any creature
// on the battlefield, except it's an Illusion in addition to its other types and
// it gains "When this creature becomes the target of a spell or ability, sacrifice it."
addCard(Zone.HAND, playerB, "Phantasmal Image");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitchen Finks");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted
setChoice(playerB, "Kitchen Finks");
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Kitchen Finks");
setChoice(playerB, "Kitchen Finks");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Public Execution", 1);
assertLife(playerA, 22);
assertLife(playerB, 24);
assertPermanentCount(playerA, "Kitchen Finks", 1);
assertHandCount(playerB, "Phantasmal Image", 0);
assertGraveyardCount(playerB, "Phantasmal Image", 0);
assertPermanentCount(playerB, "Kitchen Finks", 1);
assertPowerToughness(playerB, "Kitchen Finks", 2, 1);
}
@Test
public void testUndying() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.)
addCard(Zone.HAND, playerA, "Butcher Ghoul");
// Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn.
addCard(Zone.HAND, playerA, "Public Execution");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6);
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
// You may have Phantasmal Image enter the battlefield as a copy of any creature
// on the battlefield, except it's an Illusion in addition to its other types and
// it gains "When this creature becomes the target of a spell or ability, sacrifice it."
addCard(Zone.HAND, playerB, "Phantasmal Image");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Butcher Ghoul");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted
setChoice(playerB, "Butcher Ghoul");
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Butcher Ghoul");
setChoice(playerB, "Butcher Ghoul");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Public Execution", 1);
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPermanentCount(playerA, "Butcher Ghoul", 1);
assertHandCount(playerB, "Phantasmal Image", 0);
assertGraveyardCount(playerB, "Phantasmal Image", 0);
assertPermanentCount(playerB, "Butcher Ghoul", 1);
assertPowerToughness(playerB, "Butcher Ghoul", 2, 2);
}
/**
* 12:29: Attacker: Wurmcoil Engine [466] (6/6) blocked by Wurmcoil Engine
* [4ed] (6/6)
* 12:29: yespair gains 6 life
* 12:29: HipSomHap gains 6 life
* 12:29: Wurmcoil Engine [4ed] died
* 12:29: Ability triggers: Wurmcoil Engine [4ed] - When Wurmcoil Engine [4ed] dies, put a a 3/3 colorless
* Wurm artifact creature token with deathtouch onto the battlefield. Put a
* a 3/3 colorless Wurm artifact creature token with lifelink onto the
* battlefield.
* 12:29: Phantasmal Image [466] died
* 12:29: HipSomHap puts a Wurm [7d0] token onto the battlefield
* 12:29: HipSomHap puts a Wurm [186] token onto the battlefield
*
* To the best of my knowledge, the Phantasmal Image [466], which entered
* the battlefield as a Wurmcoil Engine, should grant tokens through the
* Dies-trigger as well, right?
*/
@Test
public void testDiesTriggered2() {
addCard(Zone.BATTLEFIELD, playerB, "Wurmcoil Engine");
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.HAND, playerA, "Phantasmal Image");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phantasmal Image"); // not targeted
setChoice(playerB, "Wurmcoil Engine");
attack(2, playerB, "Wurmcoil Engine");
block(2, playerA, "Wurmcoil Engine", "Wurmcoil Engine");
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertLife(playerB, 26);
assertLife(playerA, 26);
assertGraveyardCount(playerA, "Phantasmal Image", 1);
assertGraveyardCount(playerB, "Wurmcoil Engine", 1);
assertPermanentCount(playerA, "Wurm", 2);
assertPermanentCount(playerB, "Wurm", 2);
}
}

View file

@ -0,0 +1,105 @@
/*
* 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.cards.copy;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class VesuvaTest extends CardTestPlayerBase {
/**
* played a Vesuva on Glimmerpost and since when Vesuva becames Glimmerpost
* it is already on the battlefield the ability won't trigger. Then when i
* used Vesuva to copy an opponent's Dark Depth i got the land with the 10
* counters.
*
* If Dark Depth says "it enters the battlefield with 10 counter" but Vesuva
* is already on the field shouldnt have any counters or the problem was
* that Glimmerpost should have trigger?
*/
@Test
public void testGlimmerpost() {
// When Glimmerpost enters the battlefield, you gain 1 life for each Locus on the battlefield.
// {T}: {1} Add to your mana pool.
addCard(Zone.HAND, playerA, "Glimmerpost", 1);
// You may have Vesuva enter the battlefield tapped as a copy of any land on the battlefield.
addCard(Zone.HAND, playerA, "Vesuva", 1);
addCard(Zone.HAND, playerB, "Glimmerpost", 1);
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Glimmerpost");
playLand(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Glimmerpost");
playLand(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Vesuva");
setChoice(playerA, "Glimmerpost");
setStopAt(3, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Glimmerpost", 2);
assertPermanentCount(playerB, "Glimmerpost", 1);
assertLife(playerA, 24); // 20 + 1 + 3
assertLife(playerB, 22); // 20 + 2
}
@Test
public void testDarkDepth() {
// Dark Depths enters the battlefield with ten ice counters on it.
// {3}: Remove an ice counter from Dark Depths.
// When Dark Depths has no ice counters on it, sacrifice it. If you do, put a legendary 20/20 black Avatar creature token with flying and "This creature is indestructible" named Marit Lage onto the battlefield.
addCard(Zone.BATTLEFIELD, playerB, "Dark Depths", 1);
// You may have Vesuva enter the battlefield tapped as a copy of any land on the battlefield.
addCard(Zone.HAND, playerA, "Vesuva", 1);
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vesuva");
setChoice(playerA, "Dark Depths");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Dark Depths", 1);
assertPermanentCount(playerB, "Dark Depths", 1);
Permanent darkDepth = getPermanent("Dark Depths", playerA);
if (darkDepth != null) {
Assert.assertEquals(darkDepth.getCounters().getCount("ice"), 10);
}
}
}

View file

@ -0,0 +1,65 @@
/*
* 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.cards.cost.additional;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class RemoveCounterCostTest extends CardTestPlayerBase {
@Test
public void testNovijenSages() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 7);
// Graft 4
// {1}, Remove two +1/+1 counters from among creatures you control: Draw a card.
addCard(Zone.HAND, playerA, "Novijen Sages");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Novijen Sages");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1},Remove two +1/+1 counters");
setChoice(playerA, "X=2");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Novijen Sages", 1);
assertPowerToughness(playerA, "Novijen Sages", 2, 2);
assertHandCount(playerA, 1);
assertLife(playerA, 20);
assertLife(playerB, 20);
}
}

View file

@ -41,12 +41,16 @@ public class SewerNemesisTest extends CardTestPlayerBase {
/**
* Sewer Nemesis count's all cards in each player's graveyard to determine it's * / *, not just the cosen player's graveyard.
* BUG: Sewer Nemesis count's all cards in each player's graveyard to determine it's * / *, not just the chosen player's graveyard.
*
*/
@Test
public void test1() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
// As Sewer Nemesis enters the battlefield, choose a player.
// Sewer Nemesis's power and toughness are each equal to the number of cards in the chosen player's graveyard.
// Whenever the chosen player casts a spell, that player puts the top card of his or her library into his or her graveyard.
addCard(Zone.HAND, playerA, "Sewer Nemesis");
addCard(Zone.GRAVEYARD, playerA, "Raging Goblin",4);

View file

@ -0,0 +1,90 @@
/*
* 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.cards.mana;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class ManaFlareTest extends CardTestPlayerBase {
@Test
public void testIsland() {
// Whenever a player taps a land for mana, that player adds one mana to his or her mana pool of any type that land produced.
addCard(Zone.BATTLEFIELD, playerA, "Mana Flare", 1);
addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
// Creature {U}{U}
// {U},{T} :Return target permanent you control to its owner's hand.
addCard(Zone.HAND, playerA, "Vedalken Mastermind", 1);
// because available mana calculation does not work correctly with Mana Flare we have to tap the land manually
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U} to your mana pool");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vedalken Mastermind");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Vedalken Mastermind", 1);
}
/**
* Mana Flare is only adding colorless mana, at least off of dual lands (Watery Grave in this instance).
* Island only adds colorless. Plains adds white though.
*/
@Test
public void testWateryGrave() {
// {T}: Add one mana of any color to your mana pool. Spend this mana only to cast a multicolored spell.
addCard(Zone.BATTLEFIELD, playerB, "Mana Flare", 1);
addCard(Zone.BATTLEFIELD, playerB, "Watery Grave", 1);
// Creature {B}{B}
// {B}: Nantuko Shade gets +1/+1 until end of turn.
addCard(Zone.HAND, playerB, "Nantuko Shade", 1);
// because available mana calculation does not work correctly with Mana Flare we have to tap the land manually
activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add {B} to your mana pool");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Nantuko Shade");
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerB, "Nantuko Shade", 1);
}
}

View file

@ -0,0 +1,76 @@
/*
* 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.cards.replacement;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class LeylineOfTheVoidTest extends CardTestPlayerBase {
/**
* Leyline of the Void is on the battlefield, Helm of Obedience is used on
* an opponent with X=1. Now the Helm states to mill "until a creature card
* or X cards are put into the graveyard this way". For each of those milled
* cards, Leyline states to "remove [them] from the game instead". In other
* words: Leyline + Helm and X of at least 1 exiles the library of one
* unlucky opponent. Or should, because right now it doesn't. Right now it's
* Helm's originally intended "X or 1st Creature" amount of cards that after
* it's effect get exiled.
*/
@Test
public void testGrindstoneProgenius() {
addCard(Zone.BATTLEFIELD, playerA, "Plains");
// If Leyline of the Void is in your opening hand, you may begin the game with it on the battlefield.
// If a card would be put into an opponent's graveyard from anywhere, exile it instead.
addCard(Zone.BATTLEFIELD, playerA, "Leyline of the Void");
// {X}, {T}: Target opponent puts cards from the top of his or her library into his or her graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice Helm of Obedience and put that card onto the battlefield under your control. X can't be 0.
addCard(Zone.BATTLEFIELD, playerA, "Helm of Obedience");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{X},{T}: Target opponent puts cards", playerB);
setChoice(playerA, "X=1");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertExileCount(playerB, 71); // All cards go to exile replaced from Leyline of the void
}
}

View file

@ -18,18 +18,25 @@ public class SigardaHostOfHeronsTest extends CardTestPlayerBase {
*/
@Test
public void testCard() {
// Spells and abilities your opponents control can't cause you to sacrifice permanents.
addCard(Zone.BATTLEFIELD, playerA, "Sigarda, Host of Herons");
// {T}, Tap two untapped Humans you control: Exile target artifact or enchantment.
addCard(Zone.BATTLEFIELD, playerA, "Devout Chaplain");
// {2}{B}, Sacrifice a creature: Target opponent reveals his or her hand. You choose a card from it. That player discards that card. Activate this ability only any time you could cast a sorcery.
addCard(Zone.BATTLEFIELD, playerA, "Corpse Traders");
// Target player sacrifices a creature.
addCard(Zone.HAND, playerA, "Diabolic Edict");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
addCard(Zone.HAND, playerB, "Diabolic Edict");
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
// At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.
// At the beginning of each opponent's upkeep, that player sacrifices a creature.
addCard(Zone.BATTLEFIELD, playerB, "Sheoldred, Whispering One");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Diabolic Edict", playerA);
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Diabolic Edict", playerB);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Diabolic Edict", playerA); // sacrificing for player A prevented by Sigarda
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Diabolic Edict", playerB); // playerB has to sacrifice Sheldred
setStopAt(3, PhaseStep.END_TURN);
execute();
@ -40,9 +47,13 @@ public class SigardaHostOfHeronsTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Sigarda, Host of Herons", 1);
assertPermanentCount(playerA, "Devout Chaplain", 1);
assertPermanentCount(playerA, "Corpse Traders", 1);
assertGraveyardCount(playerA, "Diabolic Edict", 1);
assertGraveyardCount(playerA, 1);
assertPermanentCount(playerB, "Sheoldred, Whispering One", 0);
assertHandCount(playerB, "Diabolic Edict", 0);
assertGraveyardCount(playerB, "Sheoldred, Whispering One", 1);
assertGraveyardCount(playerB, "Diabolic Edict", 1);
assertGraveyardCount(playerB, 2);
}

View file

@ -14,7 +14,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
public class TorporOrbTest extends CardTestPlayerBase {
@Test
public void testCard() {
public void testWallOfOmens() {
addCard(Zone.BATTLEFIELD, playerA, "Torpor Orb");
addCard(Zone.HAND, playerA, "Wall of Omens");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
@ -32,4 +32,30 @@ public class TorporOrbTest extends CardTestPlayerBase {
assertHandCount(playerA, 0);
}
/**
* Treacherous Pit-Dweller doesnt function properly with Torpor Orb and Hushwing Gryff
*/
@Test
public void testPitTweller() {
addCard(Zone.BATTLEFIELD, playerB, "Torpor Orb");
addCard(Zone.BATTLEFIELD, playerB, "Treacherous Pit-Dweller"); // 4/3
addCard(Zone.HAND, playerA, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
attack(2, playerB, "Treacherous Pit-Dweller");
castSpell(2, PhaseStep.DECLARE_ATTACKERS, playerA, "Lightning Bolt", "Treacherous Pit-Dweller");
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Lightning Bolt", 1);
assertPermanentCount(playerB, "Treacherous Pit-Dweller", 1);
assertPowerToughness(playerB, "Treacherous Pit-Dweller", 5,4);
}
}

View file

@ -93,4 +93,55 @@ public class WinLoseEffectsTest extends CardTestPlayerBase {
assertLife(playerB, 20);
}
@Test
public void testAngelsGrace2() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
// Instant {W}
// You can't lose the game this turn and your opponents can't win the game this turn. Until end of turn, damage that would reduce your life total to less than 1 reduces it to 1 instead.
addCard(Zone.HAND, playerA, "Angel's Grace");
// Instant - {3}{B}{B}
// Reveal the top card of your library and put that card into your hand. You lose life equal to its converted mana cost.
// You may repeat this process any number of times.
addCard(Zone.HAND, playerA, "Ad Nauseam");
// Creature
// If you would draw a card while your library has no cards in it, you win the game instead.
addCard(Zone.BATTLEFIELD, playerA, "Laboratory Maniac", 1);
skipInitShuffling();
playerA.getLibrary().clear();
// Instant {U}
// Draw a card. Scry 2
addCard(Zone.LIBRARY, playerA, "Serum Visions"); // 1 life lost
addCard(Zone.LIBRARY, playerA, "Bogardan Hellkite", 3); // 24 life lost
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel's Grace");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ad Nauseam");
setChoice(playerA, "Yes");
setChoice(playerA, "Yes");
setChoice(playerA, "Yes");
setChoice(playerA, "Yes");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Serum Visions");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Angel's Grace", 1);
assertGraveyardCount(playerA, "Ad Nauseam", 1);
assertGraveyardCount(playerA, "Serum Visions", 1);
Assert.assertEquals("Player A library is empty", 0 , playerA.getLibrary().size());
assertLife(playerA, -5);
assertLife(playerB, 20);
Assert.assertTrue("Player A has not won but should have", playerA.hasWon());
}
}

View file

@ -0,0 +1,78 @@
/*
* 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.cards.replacement.prevent;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class RefractionTrapTest extends CardTestPlayerBase {
/**
* I found a bug when i cast Refraction Trap I choose to redirect damage to
* opponent creature and it stays alive (in 2 situations)
*/
@Test
public void testPreventDamageFromSpell() {
addCard(Zone.HAND, playerA, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
// If an opponent cast a red instant or sorcery spell this turn, you may pay {W}
// rather than pay Refraction Trap's mana cost.
// Prevent the next 3 damage that a source of your choice would deal to you and/or
// permanents you control this turn. If damage is prevented this way, Refraction Trap
// deals that much damage to target creature or player.
addCard(Zone.HAND, playerB, "Refraction Trap");
addCard(Zone.BATTLEFIELD, playerB, "Plains");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Refraction Trap", "Silvercoat Lion", "Lightning Bolt");
setChoice(playerB, "Yes");
setChoice(playerB, "Lightning Bolt");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Lightning Bolt", 1);
assertGraveyardCount(playerB, "Refraction Trap", 1);
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
}
}

View file

@ -0,0 +1,71 @@
/*
* 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.cards.restriction;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class CantAttackTest extends CardTestPlayerBase {
/**
* Tests "If all other elves get the Forestwalk ability and can't be blockt from creatures whose controler has a forest in game"
*/
@Test
public void testAttack() {
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // 2/2
addCard(Zone.BATTLEFIELD, playerA, "Myr Enforcer"); // 4/4
addCard(Zone.BATTLEFIELD, playerB, "Akron Legionnaire"); // 8/4
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); // 2/2
addCard(Zone.BATTLEFIELD, playerB, "Myr Enforcer"); // 4/4
attack(2, playerB, "Akron Legionnaire");
attack(2, playerB, "Silvercoat Lion");
attack(2, playerB, "Myr Enforcer");
attack(3, playerA, "Silvercoat Lion");
attack(3, playerA, "Myr Enforcer");
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertLife(playerA, 8); // 8 + 4
assertLife(playerB, 14); // 4 + 2
}
}

View file

@ -68,6 +68,74 @@ public class MisdirectionTest extends CardTestPlayerBase {
assertGraveyardCount(playerA, "Rakshasa's Secret", 1);
assertGraveyardCount(playerB, "Misdirection", 1);
assertHandCount(playerB, "Silvercoat Lion", 0);
}
// check to change target permanent creature legal to to a creature the opponent of the spell controller controls
@Test
public void testChangePublicExecution() {
// Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn.
addCard(Zone.HAND, playerA, "Public Execution");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6);
/*
Misdirection {3}{U}{U}
Instant
You may exile a blue card from your hand rather than pay Misdirection's mana cost.
Change the target of target spell with a single target.
*/
addCard(Zone.HAND, playerB, "Misdirection");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1);
addCard(Zone.BATTLEFIELD, playerB, "Custodian of the Trove", 1); // 4/3
addCard(Zone.BATTLEFIELD, playerB, "Island", 5);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Public Execution", "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Misdirection", "Public Execution", "Public Execution");
addTarget(playerB, "Custodian of the Trove");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Public Execution", 1);
assertGraveyardCount(playerB, "Misdirection", 1);
assertGraveyardCount(playerB, "Custodian of the Trove",1);
assertPermanentCount(playerB, "Pillarfield Ox", 1);
assertPowerToughness(playerB, "Pillarfield Ox", 0, 4);
}
// check to change target permanent creature not legal to to a creature the your opponent controls
@Test
public void testChangePublicExecution2() {
// Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn.
addCard(Zone.HAND, playerA, "Public Execution");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6);
addCard(Zone.BATTLEFIELD, playerA, "Keeper of the Lens", 1);
/*
Misdirection {3}{U}{U}
Instant
You may exile a blue card from your hand rather than pay Misdirection's mana cost.
Change the target of target spell with a single target.
*/
addCard(Zone.HAND, playerB, "Misdirection");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1);
addCard(Zone.BATTLEFIELD, playerB, "Custodian of the Trove", 1); // 4/3
addCard(Zone.BATTLEFIELD, playerB, "Island", 5);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Public Execution", "Custodian of the Trove");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Misdirection", "Public Execution", "Public Execution");
addTarget(playerB, "Keeper of the Lens");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Public Execution", 1);
assertGraveyardCount(playerB, "Misdirection", 1);
assertPermanentCount(playerA, "Keeper of the Lens", 1);
assertPermanentCount(playerB, "Pillarfield Ox", 1);
assertPowerToughness(playerB, "Pillarfield Ox", 0, 4);
assertGraveyardCount(playerB, "Custodian of the Trove",1);
}
}

View file

@ -0,0 +1,105 @@
/*
* 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.cards.single.ths;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class KeranosGodOfStormsTest extends CardTestPlayerBase {
@Test
public void testKeranosNormal() {
// Reveal the first card you draw on each of your turns.
// Whenever you reveal a land card this way, draw a card.
// Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player.
addCard(Zone.BATTLEFIELD, playerB, "Keranos, God of Storms"); // {3}{U}{R}
// Look at target player's hand.
// Draw a card.
addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 3);
skipInitShuffling();
addTarget(playerB, playerA); // damage from Keranos trigger
setStopAt(2, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerB, "Keranos, God of Storms", 1);
assertHandCount(playerB, "Silvercoat Lion", 1); // 1 Lion from the draw and 1 Lion from Peek
assertLife(playerA, 17);
assertLife(playerB, 20);
}
/**
* Keranos, God of Storms, will look at the first card drawn after he is
* played, not the first card drawn each turn.
*
* My opponent, draws, plays Keranos, then plays Stroke of Genius. Keranos
* triggers on the first card for Stroke of Genius.
*/
@Test
public void testKeranosCastAfterFirstDraw() {
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerB, "Island", 5);
// Reveal the first card you draw on each of your turns.
// Whenever you reveal a land card this way, draw a card.
// Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player.
addCard(Zone.HAND, playerB, "Keranos, God of Storms"); // {3}{U}{R}
// Look at target player's hand.
// Draw a card.
addCard(Zone.HAND, playerB, "Peek");
addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 3);
skipInitShuffling();
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Keranos, God of Storms");
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Peek", playerA); // you won't do damage because it's not the first draw this turn - Draw in draw phase was the first
addTarget(playerB, playerA); // not needed if it works correct
setStopAt(2, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerB, "Keranos, God of Storms", 1);
assertGraveyardCount(playerB, "Peek", 1);
assertHandCount(playerB, "Silvercoat Lion", 2); // 1 Lion from the draw and 1 Lion from Peek
assertLife(playerA, 20);
assertLife(playerB, 20);
}
}

View file

@ -0,0 +1,74 @@
/*
* 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.cards.single.ths;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class OrdealEnchantmentsTest extends CardTestPlayerBase {
@Test
public void testOrdealofHeliod() {
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
// Enchant creature
// Whenever enchanted creature attacks, put a +1/+1 counter on it. Then if it has three or more +1/+1 counters on it, sacrifice Ordeal of Heliod.
// When you sacrifice Ordeal of Heliod, you gain 10 life.
addCard(Zone.HAND, playerB, "Ordeal of Heliod");
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Ordeal of Heliod", "Silvercoat Lion");
attack(2, playerB, "Silvercoat Lion");
attack(4, playerB, "Silvercoat Lion");
attack(6, playerB, "Silvercoat Lion");
setStopAt(6, PhaseStep.END_COMBAT);
execute();
assertLife(playerA, 8); // 3 + 4 + 5 = 12
assertLife(playerB, 30);
assertGraveyardCount(playerB, "Ordeal of Heliod", 1);
assertPermanentCount(playerB, "Silvercoat Lion", 1);
assertPowerToughness(playerB, "Silvercoat Lion", 5,5);
}
}

View file

@ -0,0 +1,71 @@
/*
* 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.cards.triggers;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class BurningEarthTest extends CardTestPlayerBase {
/**
* Burning Earth - It doesn't cause the damage it should. My opponent taps a
* Blood Crypt and an Overgrown Tomb for black and green mana respectively
* and casts his card all the while without taking any damage.
*
*/
@Test
public void testBurningEarth() {
// Destroy target artifact or creature. It can't be regenerated.
addCard(Zone.HAND, playerB, "Putrefy"); // {1}{B}{G}
addCard(Zone.BATTLEFIELD, playerB, "Darksteel Citadel", 1);
addCard(Zone.BATTLEFIELD, playerB, "Blood Crypt", 1); // {B}{R}
addCard(Zone.BATTLEFIELD, playerB, "Overgrown Tomb", 1); // {B}{G}
// Whenever a player taps a nonbasic land for mana, Burning Earth deals 1 damage to that player.
addCard(Zone.BATTLEFIELD, playerA, "Burning Earth", 1);
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Putrefy", "Silvercoat Lion");
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, "Putrefy", 1);
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
assertLife(playerA, 20);
assertLife(playerB, 17);
}
}

View file

@ -37,7 +37,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
* @author LevelX2
*/
public class ReturnToBattlefieldUnderYourControlTargetEffectTest extends CardTestPlayerBase {
public class ReturnToBattlefieldEffectsTest extends CardTestPlayerBase {
@Test
public void testSaffiEriksdotter() {
@ -115,5 +115,41 @@ public class ReturnToBattlefieldUnderYourControlTargetEffectTest extends CardTes
assertExileCount("Arcbound Worker", 1);
}
/**
* With my opponent's Deathmist Raptor return-to-battlefield trigger on the stack,
* I exiled the Deathmist Raptor with Pharika, God of Affliction. However, the Deathmist Raptor
* returned to the battlefield from exile, though it should not have because it had
* changed zones so was a different object.
*/
@Test
public void testDeathmistRaptor() {
// Deathtouch
// Whenever a permanent you control is turned face up, you may return Deathmist Raptor from your graveyard to the battlefield face up or face down.
// Megamorph {4}{G}
addCard(Zone.GRAVEYARD, playerA, "Deathmist Raptor");
addCard(Zone.HAND, playerA, "Pine Walker");
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
addCard(Zone.BATTLEFIELD, playerB, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1);
// {B}{G}: Exile target creature card from a graveyard. It's owner puts a 1/1 black and green Snake enchantment creature token with deathtouch onto the battlefield.
addCard(Zone.BATTLEFIELD, playerB, "Pharika, God of Affliction", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker");
setChoice(playerA, "Yes"); // cast it face down as 2/2 creature
activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "{4}{G}: Turn this face-down permanent face up.");
activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerB, "{B}{G}: Exile target creature card from a graveyard",
"Deathmist Raptor", "Whenever a permanent you control is turned face up");
setStopAt(3, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerB, "Pharika, God of Affliction", 1);
assertPermanentCount(playerA, "Snake", 1);
assertPermanentCount(playerA, "Pine Walker", 1);
assertExileCount("Deathmist Raptor", 1);
}
}

View file

@ -0,0 +1,72 @@
/*
* 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.cards.triggers;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class ReturnToHandEffectsTest extends CardTestPlayerBase {
/**
* Enduring Renewal doesn't return creatures to hand put into graveyard from
* the battlefield It happened with Enduring Renewal in the battlefield
* while feeding Ornithopter to Grinding Station
*/
@Test
public void testEnduringRenewal() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
// Play with your hand revealed.
// If you would draw a card, reveal the top card of your library instead. If it's a creature card, put it into your graveyard. Otherwise, draw a card.
// Whenever a creature is put into your graveyard from the battlefield, return it to your hand.
addCard(Zone.BATTLEFIELD, playerA, "Enduring Renewal");
// {T}, Sacrifice an artifact: Target player puts the top three cards of his or her library into his or her graveyard.
// Whenever an artifact enters the battlefield, you may untap Grinding Station.
addCard(Zone.BATTLEFIELD, playerA, "Grinding Station", 1);
addCard(Zone.BATTLEFIELD, playerA, "Ornithopter", 1);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice an artifact", playerB);
// addTarget(playerA, "Ornithopter");
setChoice(playerA, "Ornithopter");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, 3);
assertHandCount(playerA, "Ornithopter", 1);
}
}

View file

@ -65,4 +65,32 @@ public class SpellCastTriggerTest extends CardTestPlayerBase {
assertPowerToughness(playerA, "Sunscorch Regent", 5, 4);
}
}
/**
* Monastery Mentor triggers are causing a "rollback" error.
*/
@Test
public void testMonasteryMentor() {
// Prowess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.)
// Whenever you cast a noncreature spell, put a 1/1 white Monk creature token with prowess onto the battlefield.
addCard(Zone.BATTLEFIELD, playerA, "Monastery Mentor", 1);
addCard(Zone.HAND, playerA, "Lightning Bolt", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerA, 20);
assertLife(playerB, 14);
assertGraveyardCount(playerA, "Lightning Bolt", 2);
assertPermanentCount(playerA, "Monk", 2);
assertPowerToughness(playerA, "Monk", 2, 2);
assertPowerToughness(playerA, "Monk", 1, 1);
assertPowerToughness(playerA, "Monastery Mentor", 4, 4);
}
}

View file

@ -25,11 +25,15 @@ public class SynodCenturionTest extends CardTestPlayerBase {
@Test
public void testAlone() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
// Whenever a player casts a black spell, you may gain 1 life.
addCard(Zone.BATTLEFIELD, playerA, "Demon's Horn");
// Destroy target artifact.
addCard(Zone.HAND, playerA, "Shatter");
// When you control no other artifacts, sacrifice Synod Centurion.
addCard(Zone.HAND, playerA, "Synod Centurion");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Synod Centurion");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Shatter", "Demon's Horn");
setStopAt(1, PhaseStep.END_TURN);

View file

@ -0,0 +1,54 @@
/*
* 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.commander.duel;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestCommanderDuelBase;
/**
*
* @author LevelX2
*/
public class CastCommanderTest extends CardTestCommanderDuelBase {
@Test
public void testFirstAbility() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ob Nixilis of the Black Oath");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 40);
assertLife(playerB, 40);
assertPermanentCount(playerA, "Ob Nixilis of the Black Oath", 1);
}
}

View file

@ -0,0 +1,68 @@
/*
* 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.commander.duel;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestCommanderDuelBase;
/**
*
* @author LevelX2
*/
public class OpalPalaceTest extends CardTestCommanderDuelBase {
/**
* I cast my commander with Opal Palace's second ability and it did not receive a +1/+1 counter
* the first time it was cast (rulings say it should on the first time cast).
*/
@Test
public void testFirstAbility() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
// {T}: Add {1} to your mana pool.
// {1}, {T}: Add to your mana pool one mana of any color in your commander's color identity.
// If you spend this mana to cast your commander, it enters the battlefield with a number of +1/+1 counters on it
// equal to the number of times it's been cast from the command zone this game.
addCard(Zone.BATTLEFIELD, playerA, "Opal Palace", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ob Nixilis of the Black Oath");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 40);
assertLife(playerB, 40);
assertPermanentCount(playerA, "Ob Nixilis of the Black Oath", 1);
assertCounterCount("Ob Nixilis of the Black Oath", CounterType.P1P1, 1);
}
}

View file

@ -0,0 +1,146 @@
/*
* 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.multiplayer;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
/**
*
* @author LevelX2
*/
public class BloodchiefAscensionTest extends CardTestMultiPlayerBase {
@Test
public void testBloodchiefAscensionAllPlayers() {
// Enchantment
// At the beginning of each end step, if an opponent lost 2 or more life this turn, you may put a quest counter on Bloodchief Ascension. (Damage causes loss of life.)
// Whenever a card is put into an opponent's graveyard from anywhere, if Bloodchief Ascension has three or more quest counters on it, you may have that player lose 2 life. If you do, you gain 2 life.
addCard(Zone.BATTLEFIELD, playerA, "Bloodchief Ascension");
addCard(Zone.BATTLEFIELD, playerA, "Mountain",3);
addCard(Zone.HAND, playerA, "Fireball");
addCard(Zone.BATTLEFIELD, playerD, "Mountain",3);
addCard(Zone.HAND, playerD, "Fireball");
addCard(Zone.BATTLEFIELD, playerC, "Mountain",3);
addCard(Zone.HAND, playerC, "Fireball");
addCard(Zone.BATTLEFIELD, playerB, "Mountain",3);
addCard(Zone.HAND, playerB, "Fireball");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fireball", playerA);
setChoice(playerA, "X=2");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Fireball", playerD);
setChoice(playerD, "X=2");
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Fireball", playerC);
setChoice(playerC, "X=2");
castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Fireball", playerB);
setChoice(playerB, "X=2");
// Player order: A -> D -> C -> B
setStopAt(4, PhaseStep.END_TURN);
execute();
assertLife(playerA, 18);
assertLife(playerB, 18);
assertLife(playerC, 18);
assertLife(playerD, 18);
assertGraveyardCount(playerA, "Fireball", 1);
assertGraveyardCount(playerB, "Fireball", 1);
assertGraveyardCount(playerC, "Fireball", 1);
assertGraveyardCount(playerD, "Fireball", 1);
assertCounterCount("Bloodchief Ascension", CounterType.QUEST, 2); // 1 opponent out of range
}
/**
* One of my opponents in a multiplayer game had a Bloodchief Ascension in play. I took lethal damage on my turn,
* but he didn't get a counter on Bloodchief Ascension at my end step. I think he should, even though I had left
* the game from dying, because of:
*
* 800.4g. If a player leaves the game during his or her turn, that turn continues to its completion without an
* active player. If the active player would receive priority, instead the next player in turn order receives priority,
* or the top object on the stack resolves, or the phase or step ends, whichever is appropriate.
*/
@Test
public void testBloodchiefAscension() {
// Enchantment
// At the beginning of each end step, if an opponent lost 2 or more life this turn, you may put a quest counter on Bloodchief Ascension. (Damage causes loss of life.)
// Whenever a card is put into an opponent's graveyard from anywhere, if Bloodchief Ascension has three or more quest counters on it, you may have that player lose 2 life. If you do, you gain 2 life.
addCard(Zone.BATTLEFIELD, playerA, "Bloodchief Ascension");
addCard(Zone.BATTLEFIELD, playerA, "Mountain",3);
addCard(Zone.HAND, playerA, "Fireball");
addCard(Zone.BATTLEFIELD, playerD, "Mountain",3);
addCard(Zone.HAND, playerD, "Fireball");
addCard(Zone.BATTLEFIELD, playerC, "Mountain",3);
addCard(Zone.HAND, playerC, "Fireball");
addCard(Zone.BATTLEFIELD, playerB, "Mountain",21);
addCard(Zone.HAND, playerB, "Fireball");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fireball", playerA);
setChoice(playerA, "X=2");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Fireball", playerD);
setChoice(playerD, "X=2");
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Fireball", playerC);
setChoice(playerC, "X=2");
castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Fireball", playerB);
setChoice(playerB, "X=20");
// Player order: A -> D -> C -> B
setStopAt(4, PhaseStep.END_TURN);
execute();
assertLife(playerA, 18);
assertLife(playerB, 0);
assertLife(playerC, 18);
assertLife(playerD, 18);
Assert.assertTrue("playerB has lost", playerB.hasLost());
assertGraveyardCount(playerA, "Fireball", 1);
assertGraveyardCount(playerC, "Fireball", 1);
assertGraveyardCount(playerD, "Fireball", 1);
assertCounterCount("Bloodchief Ascension", CounterType.QUEST, 2); // 1 opponent out of range
}
}

View file

@ -29,7 +29,6 @@ package org.mage.test.multiplayer;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
@ -65,6 +64,7 @@ public class PrimordialTest extends CardTestMultiPlayerBase {
assertPermanentCount(playerA, "Walking Corpse", 0);
assertPermanentCount(playerA, "Pillarfield Ox", 1);
assertGraveyardCount(playerC, "Walking Corpse", 1);
assertGraveyardCount(playerD, "Pillarfield Ox", 0);
}
}

View file

@ -102,16 +102,18 @@ public class RandomPlayer extends ComputerPlayer {
List<Ability> playables = getPlayableAbilities(game);
Ability ability;
while (true) {
if (playables.size() == 1)
if (playables.size() == 1) {
ability = playables.get(0);
else
} else {
ability = playables.get(rnd.nextInt(playables.size()));
}
List<Ability> options = getPlayableOptions(ability, game);
if (!options.isEmpty()) {
if (options.size() == 1)
if (options.size() == 1) {
ability = options.get(0);
else
} else {
ability = options.get(rnd.nextInt(options.size()));
}
}
if (ability.getManaCosts().getVariableCosts().size() > 0) {
int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost();
@ -154,10 +156,11 @@ public class RandomPlayer extends ComputerPlayer {
ability = source;
}
else {
if (options.size() == 1)
if (options.size() == 1) {
ability = options.get(0);
else
} else {
ability = options.get(rnd.nextInt(options.size()));
}
}
if (ability.isUsesStack()) {
game.getStack().push(new StackAbility(ability, playerId));
@ -203,15 +206,18 @@ public class RandomPlayer extends ComputerPlayer {
@Override
public void selectBlockers(Game game, UUID defendingPlayerId) {
int numGroups = game.getCombat().getGroups().size();
if (numGroups == 0) return;
if (numGroups == 0) {
return;
}
List<Permanent> blockers = getAvailableBlockers(game);
for (Permanent blocker: blockers) {
int check = rnd.nextInt(numGroups + 1);
if (check < numGroups) {
CombatGroup group = game.getCombat().getGroups().get(check);
if (group.getAttackers().size() > 0)
if (group.getAttackers().size() > 0) {
this.declareBlocker(this.getId(), blocker.getId(), group.getAttackers().get(0), game);
}
}
}
actionCount++;
@ -243,8 +249,9 @@ public class RandomPlayer extends ComputerPlayer {
protected boolean chooseRandomTarget(Target target, Ability source, Game game) {
Set<UUID> possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game);
if (possibleTargets.isEmpty())
if (possibleTargets.isEmpty()) {
return false;
}
if (!target.isRequired(source)) {
if (rnd.nextInt(possibleTargets.size() + 1) == 0) {
return false;
@ -300,8 +307,9 @@ public class RandomPlayer extends ComputerPlayer {
@Override
public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
if (cards.isEmpty())
if (cards.isEmpty()) {
return !target.isRequired(source);
}
Card card = cards.getRandom(game);
target.addTarget(card.getId(), source, game);
return true;
@ -373,8 +381,9 @@ public class RandomPlayer extends ComputerPlayer {
public Mode chooseMode(Modes modes, Ability source, Game game) {
Iterator<Mode> it = modes.values().iterator();
Mode mode = it.next();
if (modes.size() == 1)
if (modes.size() == 1) {
return mode;
}
int modeNum = rnd.nextInt(modes.values().size());
for (int i = 0; i < modeNum; i++) {
mode = it.next();

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,7 @@ import java.util.Random;
*/
public class PlayGameTest extends MageTestBase {
private static List<String> colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu");
private final static List<String> colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu");
@Ignore
@Test
@ -67,7 +67,8 @@ public class PlayGameTest extends MageTestBase {
long t1 = System.nanoTime();
GameOptions options = new GameOptions();
options.testMode = true;
game.start(computerA.getId(), options);
game.setGameOptions(options);
game.start(computerA.getId());
long t2 = System.nanoTime();
logger.info("Winner: " + game.getWinner());

View file

@ -27,7 +27,7 @@ import java.util.Random;
*/
public class TestPlayRandomGame extends MageTestBase {
private static List<String> colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu");
private final static List<String> colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu");
@Test
@Ignore
@ -58,12 +58,11 @@ public class TestPlayRandomGame extends MageTestBase {
game.addPlayer(computerB, deck2);
game.loadCards(deck2.getCards(), computerB.getId());
boolean testMode = true;
long t1 = System.nanoTime();
GameOptions options = new GameOptions();
options.testMode = true;
game.start(computerA.getId(), options);
game.setGameOptions(options);
game.start(computerA.getId());
long t2 = System.nanoTime();
logger.info("Winner: " + game.getWinner());

View file

@ -1,13 +1,14 @@
package org.mage.test.serverside.base;
import mage.constants.PhaseStep;
import java.util.List;
import mage.abilities.Ability;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.filter.Filter;
import mage.players.Player;
import org.mage.test.player.TestPlayer;
import java.util.List;
/**
* Interface for all test initialization and assertion operations.
@ -81,11 +82,14 @@ public interface CardTestAPI {
/**
* Define turn number to stop the game on.
* @param turn
*/
void setStopOnTurn(int turn);
/**
* Define the turn number and step to stop the game on.
* @param turn
* @param step
*/
void setStopAt(int turn, PhaseStep step);

View file

@ -0,0 +1,54 @@
/*
* 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.serverside.base;
import java.io.FileNotFoundException;
import mage.constants.MultiplayerAttackOption;
import mage.constants.RangeOfInfluence;
import mage.game.CommanderDuel;
import mage.game.Game;
import mage.game.GameException;
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
/**
*
* @author LevelX2
*/
public abstract class CardTestCommanderDuelBase extends CardTestPlayerAPIImpl {
@Override
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
Game game = new CommanderDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 40);
playerA = createPlayer(game, playerA, "PlayerA","CommanderDuel.dck");
playerB = createPlayer(game, playerB, "PlayerB","CommanderDuel.dck");
return game;
}
}

View file

@ -1,18 +1,14 @@
package org.mage.test.serverside.base;
import mage.constants.PhaseStep;
import mage.cards.Card;
import mage.cards.decks.Deck;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.constants.MultiplayerAttackOption;
import mage.constants.RangeOfInfluence;
import mage.filter.Filter;
import mage.game.*;
import mage.game.permanent.Permanent;
import mage.players.Player;
import org.junit.Assert;
import org.junit.Before;
import org.mage.test.player.TestPlayer;
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
import java.io.File;
@ -26,274 +22,15 @@ import java.io.FileNotFoundException;
*/
public abstract class CardTestMultiPlayerBase extends CardTestPlayerAPIImpl {
protected enum ExpectedType {
TURN_NUMBER,
RESULT,
LIFE,
BATTLEFIELD,
GRAVEYARD,
UNKNOWN
}
protected GameOptions gameOptions;
@Before
public void reset() throws GameException, FileNotFoundException {
if (currentGame != null) {
logger.debug("Resetting previous game and creating new one!");
currentGame = null;
System.gc();
}
@Override
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
Game game = new FreeForAll(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20);
playerA = createPlayer(game, playerA, "PlayerA");
playerB = createPlayer(game, playerB, "PlayerB");
playerC = createPlayer(game, playerC, "PlayerC");
playerD = createPlayer(game, playerD, "PlayerD");
activePlayer = playerA;
currentGame = game;
stopOnTurn = 2;
stopAtStep = PhaseStep.UNTAP;
for (Player player : currentGame.getPlayers().values()) {
TestPlayer testPlayer = (TestPlayer)player;
getCommands(testPlayer).clear();
getLibraryCards(testPlayer).clear();
getHandCards(testPlayer).clear();
getBattlefieldCards(testPlayer).clear();
getGraveCards(testPlayer).clear();
}
gameOptions = new GameOptions();
}
public void load(String path) throws FileNotFoundException, GameException {
String cardPath = TESTS_PATH + path;
File checkFile = new File(cardPath);
if (!checkFile.exists()) {
throw new FileNotFoundException("Couldn't find test file: " + cardPath);
}
if (checkFile.isDirectory()) {
throw new FileNotFoundException("Couldn't find test file: " + cardPath + ". It is directory.");
}
if (currentGame != null) {
logger.debug("Resetting previous game and creating new one!");
currentGame = null;
System.gc();
}
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, 0, 20);
playerA = createNewPlayer("ComputerA");
playerA.setTestMode(true);
Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false);
if (deck.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size());
}
game.addPlayer(playerA, deck);
game.loadCards(deck.getCards(), playerA.getId());
playerB = createNewPlayer("ComputerB");
playerB.setTestMode(true);
Deck deck2 = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false);
if (deck2.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size());
}
game.addPlayer(playerB, deck2);
game.loadCards(deck2.getCards(), playerB.getId());
parseScenario(cardPath);
activePlayer = playerA;
currentGame = game;
}
/**
* Starts testing card by starting current game.
*
* @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game.
*/
public void execute() throws IllegalStateException {
if (currentGame == null || activePlayer == null) {
throw new IllegalStateException("Game is not initialized. Use load method to load a test case and initialize a game.");
}
for (Player player : currentGame.getPlayers().values()) {
TestPlayer testPlayer = (TestPlayer)player;
currentGame.cheat(player.getId(), getCommands(testPlayer));
currentGame.cheat(player.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer),
getBattlefieldCards(testPlayer), getGraveCards(testPlayer));
}
boolean testMode = true;
long t1 = System.nanoTime();
gameOptions.testMode = true;
gameOptions.stopOnTurn = stopOnTurn;
gameOptions.stopAtStep = stopAtStep;
currentGame.start(activePlayer.getId(), gameOptions);
long t2 = System.nanoTime();
logger.debug("Winner: " + currentGame.getWinner());
logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms");
assertTheResults();
}
/**
* Assert expected and actual results.
*/
private void assertTheResults() {
logger.debug("Matching expected results:");
for (String line : expectedResults) {
boolean ok = false;
try {
ExpectedType type = getExpectedType(line);
if (type.equals(CardTestMultiPlayerBase.ExpectedType.UNKNOWN)) {
throw new AssertionError("Unknown expected type, check the line in $expected section=" + line);
}
parseType(type, line);
ok = true;
} finally {
logger.info(" " + line + " - " + (ok ? "OK" : "ERROR"));
}
}
}
private ExpectedType getExpectedType(String line) {
if (line.startsWith("turn:")) {
return CardTestMultiPlayerBase.ExpectedType.TURN_NUMBER;
}
if (line.startsWith("result:")) {
return CardTestMultiPlayerBase.ExpectedType.RESULT;
}
if (line.startsWith("life:")) {
return CardTestMultiPlayerBase.ExpectedType.LIFE;
}
if (line.startsWith("battlefield:")) {
return CardTestMultiPlayerBase.ExpectedType.BATTLEFIELD;
}
if (line.startsWith("graveyard:")) {
return CardTestMultiPlayerBase.ExpectedType.GRAVEYARD;
}
return CardTestMultiPlayerBase.ExpectedType.UNKNOWN;
}
private void parseType(ExpectedType type, String line) {
if (type.equals(CardTestMultiPlayerBase.ExpectedType.TURN_NUMBER)) {
int turn = getIntParam(line, 1);
Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum());
return;
}
if (type.equals(CardTestMultiPlayerBase.ExpectedType.RESULT)) {
String expected = getStringParam(line, 1);
String actual = "draw";
if (currentGame.getWinner().equals("Player ComputerA is the winner")) {
actual = "won";
} else if (currentGame.getWinner().equals("Player ComputerB is the winner")) {
actual = "lost";
}
Assert.assertEquals("Game results are not equal", expected, actual);
return;
}
if (type.equals(CardTestMultiPlayerBase.ExpectedType.LIFE)) {
String player = getStringParam(line, 1);
int expected = getIntParam(line, 2);
if (player.equals("ComputerA")) {
int actual = currentGame.getPlayer(playerA.getId()).getLife();
Assert.assertEquals("Life amounts are not equal", expected, actual);
} else if (player.equals("ComputerB")) {
int actual = currentGame.getPlayer(playerB.getId()).getLife();
Assert.assertEquals("Life amounts are not equal", expected, actual);
} else {
throw new IllegalArgumentException("Wrong player in 'life' line, player=" + player + ", line=" + line);
}
return;
}
if (type.equals(CardTestMultiPlayerBase.ExpectedType.BATTLEFIELD)) {
String playerName = getStringParam(line, 1);
String cardName = getStringParam(line, 2);
int expectedCount = getIntParam(line, 3);
Player player = null;
if (playerName.equals("ComputerA")) {
player = currentGame.getPlayer(playerA.getId());
} else if (playerName.equals("ComputerB")) {
player = currentGame.getPlayer(playerB.getId());
} else {
throw new IllegalArgumentException("Wrong player in 'battlefield' line, player=" + player + ", line=" + line);
}
int actualCount = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) {
if (permanent.getControllerId().equals(player.getId())) {
if (permanent.getName().equals(cardName)) {
actualCount++;
}
}
}
Assert.assertEquals("(Battlefield) Card counts are not equal (" + cardName + ")", expectedCount, actualCount);
return;
}
if (type.equals(CardTestMultiPlayerBase.ExpectedType.GRAVEYARD)) {
String playerName = getStringParam(line, 1);
String cardName = getStringParam(line, 2);
int expectedCount = getIntParam(line, 3);
Player player = null;
if (playerName.equals("ComputerA")) {
player = currentGame.getPlayer(playerA.getId());
} else if (playerName.equals("ComputerB")) {
player = currentGame.getPlayer(playerB.getId());
} else {
throw new IllegalArgumentException("Wrong player in 'graveyard' line, player=" + player + ", line=" + line);
}
int actualCount = 0;
for (Card card : player.getGraveyard().getCards(currentGame)) {
if (card.getName().equals(cardName)) {
actualCount++;
}
}
Assert.assertEquals("(Graveyard) Card counts are not equal (" + cardName + ")", expectedCount, actualCount);
}
}
private int getIntParam(String line, int index) {
String[] params = line.split(":");
if (index > params.length - 1) {
throw new IllegalArgumentException("Not correct line: " + line);
}
return Integer.parseInt(params[index]);
}
private String getStringParam(String line, int index) {
String[] params = line.split(":");
if (index > params.length - 1) {
throw new IllegalArgumentException("Not correct line: " + line);
}
return params[index];
}
protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) {
if (currentGame == null) {
throw new IllegalStateException("Current game is null");
}
if (scope.equals(Filter.ComparisonScope.All)) {
throw new UnsupportedOperationException("ComparisonScope.All is not implemented.");
}
for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) {
if (permanent.getName().equals(cardName)) {
Assert.assertEquals("Power is not the same", power, permanent.getPower().getValue());
Assert.assertEquals("Toughness is not the same", toughness, permanent.getToughness().getValue());
break;
}
}
}
protected void skipInitShuffling() {
gameOptions.skipInitShuffling = true;
return game;
}
}

View file

@ -1,27 +1,11 @@
package org.mage.test.serverside.base;
import java.io.File;
import java.io.FileNotFoundException;
import mage.cards.Card;
import mage.cards.decks.Deck;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.constants.MultiplayerAttackOption;
import mage.constants.PhaseStep;
import mage.constants.RangeOfInfluence;
import mage.filter.Filter;
import mage.game.Game;
import mage.game.GameException;
import mage.game.GameOptions;
import mage.game.TwoPlayerDuel;
import mage.game.permanent.Permanent;
import mage.players.Player;
import org.junit.Assert;
import org.junit.Before;
import org.mage.test.player.TestPlayer;
import static org.mage.test.serverside.base.MageTestPlayerBase.TESTS_PATH;
import static org.mage.test.serverside.base.MageTestPlayerBase.activePlayer;
import static org.mage.test.serverside.base.MageTestPlayerBase.currentGame;
import static org.mage.test.serverside.base.MageTestPlayerBase.logger;
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
/**
@ -30,293 +14,14 @@ import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
* @author ayratn
*/
public abstract class CardTestPlayerBase extends CardTestPlayerAPIImpl {
public static final String NO_TARGET = "NO_TARGET";
protected enum ExpectedType {
TURN_NUMBER,
RESULT,
LIFE,
BATTLEFIELD,
GRAVEYARD,
UNKNOWN
}
protected GameOptions gameOptions;
public CardTestPlayerBase() {
}
@Override
protected TestPlayer createNewPlayer(String playerName) {
return createPlayer(playerName);
}
@Before
public void reset() throws GameException, FileNotFoundException {
if (currentGame != null) {
logger.debug("Resetting previous game and creating new one!");
currentGame = null;
System.gc();
}
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20);
playerA = createNewPlayer("PlayerA");
playerA.setTestMode(true);
logger.debug("Loading deck...");
Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false);
logger.debug("Done!");
if (deck.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size());
}
game.loadCards(deck.getCards(), playerA.getId());
game.addPlayer(playerA, deck);
playerA = createPlayer(game, playerA, "PlayerA");
playerB = createPlayer(game, playerB, "PlayerB");
return game;
}
playerB = createNewPlayer("PlayerB");
playerB.setTestMode(true);
Deck deck2 = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false);
if (deck2.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size());
}
game.loadCards(deck2.getCards(), playerB.getId());
game.addPlayer(playerB, deck2);
activePlayer = playerA;
currentGame = game;
stopOnTurn = 2;
stopAtStep = PhaseStep.UNTAP;
for (Player player : currentGame.getPlayers().values()) {
TestPlayer testPlayer = (TestPlayer)player;
getCommands(testPlayer).clear();
getLibraryCards(testPlayer).clear();
getHandCards(testPlayer).clear();
getBattlefieldCards(testPlayer).clear();
getGraveCards(testPlayer).clear();
}
gameOptions = new GameOptions();
}
public void load(String path) throws FileNotFoundException, GameException {
String cardPath = TESTS_PATH + path;
File checkFile = new File(cardPath);
if (!checkFile.exists()) {
throw new FileNotFoundException("Couldn't find test file: " + cardPath);
}
if (checkFile.isDirectory()) {
throw new FileNotFoundException("Couldn't find test file: " + cardPath + ". It is directory.");
}
if (currentGame != null) {
logger.debug("Resetting previous game and creating new one!");
currentGame = null;
System.gc();
}
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, 0, 20);
playerA = createNewPlayer("ComputerA");
playerA.setTestMode(true);
Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false);
if (deck.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size());
}
game.addPlayer(playerA, deck);
game.loadCards(deck.getCards(), playerA.getId());
playerB = createNewPlayer("ComputerB");
playerB.setTestMode(true);
Deck deck2 = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false);
if (deck2.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size());
}
game.addPlayer(playerB, deck2);
game.loadCards(deck2.getCards(), playerB.getId());
parseScenario(cardPath);
activePlayer = playerA;
currentGame = game;
}
/**
* Starts testing card by starting current game.
*
* @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game.
*/
public void execute() throws IllegalStateException {
if (currentGame == null || activePlayer == null) {
throw new IllegalStateException("Game is not initialized. Use load method to load a test case and initialize a game.");
}
for (Player player : currentGame.getPlayers().values()) {
TestPlayer testPlayer = (TestPlayer)player;
currentGame.cheat(player.getId(), getCommands(testPlayer));
currentGame.cheat(player.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer),
getBattlefieldCards(testPlayer), getGraveCards(testPlayer));
}
boolean testMode = true;
long t1 = System.nanoTime();
gameOptions.testMode = true;
gameOptions.stopOnTurn = stopOnTurn;
gameOptions.stopAtStep = stopAtStep;
currentGame.start(activePlayer.getId(), gameOptions);
long t2 = System.nanoTime();
logger.debug("Winner: " + currentGame.getWinner());
logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms");
assertTheResults();
}
/**
* Assert expected and actual results.
*/
private void assertTheResults() {
logger.debug("Matching expected results:");
for (String line : expectedResults) {
boolean ok = false;
try {
ExpectedType type = getExpectedType(line);
if (type.equals(CardTestPlayerBase.ExpectedType.UNKNOWN)) {
throw new AssertionError("Unknown expected type, check the line in $expected section=" + line);
}
parseType(type, line);
ok = true;
} finally {
logger.info(" " + line + " - " + (ok ? "OK" : "ERROR"));
}
}
}
private ExpectedType getExpectedType(String line) {
if (line.startsWith("turn:")) {
return CardTestPlayerBase.ExpectedType.TURN_NUMBER;
}
if (line.startsWith("result:")) {
return CardTestPlayerBase.ExpectedType.RESULT;
}
if (line.startsWith("life:")) {
return CardTestPlayerBase.ExpectedType.LIFE;
}
if (line.startsWith("battlefield:")) {
return CardTestPlayerBase.ExpectedType.BATTLEFIELD;
}
if (line.startsWith("graveyard:")) {
return CardTestPlayerBase.ExpectedType.GRAVEYARD;
}
return CardTestPlayerBase.ExpectedType.UNKNOWN;
}
private void parseType(ExpectedType type, String line) {
if (type.equals(CardTestPlayerBase.ExpectedType.TURN_NUMBER)) {
int turn = getIntParam(line, 1);
Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum());
return;
}
if (type.equals(CardTestPlayerBase.ExpectedType.RESULT)) {
String expected = getStringParam(line, 1);
String actual = "draw";
switch (currentGame.getWinner()) {
case "Player ComputerA is the winner":
actual = "won";
break;
case "Player ComputerB is the winner":
actual = "lost";
break;
}
Assert.assertEquals("Game results are not equal", expected, actual);
return;
}
Player player = null;
String playerName = getStringParam(line, 1);
switch (playerName) {
case "ComputerA":
player = currentGame.getPlayer(playerA.getId());
break;
case "ComputerB":
player = currentGame.getPlayer(playerB.getId());
break;
}
if (player == null) {
throw new IllegalArgumentException("Wrong player in 'battlefield' line, player=" + player + ", line=" + line);
}
if (type.equals(CardTestPlayerBase.ExpectedType.LIFE)) {
int expected = getIntParam(line, 2);
int actual = player.getLife();
Assert.assertEquals("Life amounts are not equal", expected, actual);
return;
}
if (type.equals(CardTestPlayerBase.ExpectedType.BATTLEFIELD)) {
String cardName = getStringParam(line, 2);
int expectedCount = getIntParam(line, 3);
int actualCount = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) {
if (permanent.getControllerId().equals(player.getId())) {
if (permanent.getName().equals(cardName)) {
actualCount++;
}
}
}
Assert.assertEquals("(Battlefield) Card counts are not equal (" + cardName + ")", expectedCount, actualCount);
return;
}
if (type.equals(CardTestPlayerBase.ExpectedType.GRAVEYARD)) {
String cardName = getStringParam(line, 2);
int expectedCount = getIntParam(line, 3);
int actualCount = 0;
for (Card card : player.getGraveyard().getCards(currentGame)) {
if (card.getName().equals(cardName)) {
actualCount++;
}
}
Assert.assertEquals("(Graveyard) Card counts are not equal (" + cardName + ")", expectedCount, actualCount);
}
}
private int getIntParam(String line, int index) {
String[] params = line.split(":");
if (index > params.length - 1) {
throw new IllegalArgumentException("Not correct line: " + line);
}
return Integer.parseInt(params[index]);
}
private String getStringParam(String line, int index) {
String[] params = line.split(":");
if (index > params.length - 1) {
throw new IllegalArgumentException("Not correct line: " + line);
}
return params[index];
}
protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) {
if (currentGame == null) {
throw new IllegalStateException("Current game is null");
}
if (scope.equals(Filter.ComparisonScope.All)) {
throw new UnsupportedOperationException("ComparisonScope.All is not implemented.");
}
for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) {
if (permanent.getName().equals(cardName)) {
Assert.assertEquals("Power is not the same", power, permanent.getPower().getValue());
Assert.assertEquals("Toughness is not the same", toughness, permanent.getToughness().getValue());
break;
}
}
}
protected void skipInitShuffling() {
gameOptions.skipInitShuffling = true;
}
}

View file

@ -0,0 +1,71 @@
/*
* 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.serverside.base;
import java.io.FileNotFoundException;
import mage.constants.MultiplayerAttackOption;
import mage.constants.RangeOfInfluence;
import mage.game.Game;
import mage.game.GameException;
import mage.game.TwoPlayerDuel;
import mage.player.ai.ComputerPlayer7;
import org.mage.test.player.TestPlayer;
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
/**
*
* @author LevelX2
*/
public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl {
int skill = 9;
@Override
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20);
playerA = createPlayer(game, playerA, "PlayerA");
playerB = createPlayer(game, playerB, "PlayerB");
return game;
}
@Override
protected TestPlayer createPlayer(String name) {
if (name.equals("PlayerA")) {
TestPlayer testPlayer = new TestPlayer(new ComputerPlayer7("PlayerA", RangeOfInfluence.ONE, skill));
testPlayer.setAIPlayer(true);
return testPlayer;
}
return super.createPlayer(name);
}
public void setAISkill(int skill) {
this.skill = skill;
}
}

View file

@ -31,7 +31,6 @@ import java.io.FilenameFilter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.mage.test.serverside.base.MageTestPlayerBase.currentGame;
/**
* Base class for all tests.

View file

@ -28,6 +28,7 @@ import java.io.FilenameFilter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import mage.player.ai.ComputerPlayer;
/**
* Base class for all tests.
@ -260,14 +261,15 @@ public abstract class MageTestPlayerBase {
}
private TestPlayer getPlayer(String name) {
if (name.equals("ComputerA")) {
return playerA;
} else if (name.equals("ComputerB")) {
return playerB;
} else if (name.equals("ComputerC")) {
return playerC;
} else if (name.equals("ComputerD")) {
return playerD;
switch (name) {
case "ComputerA":
return playerA;
case "ComputerB":
return playerB;
case "ComputerC":
return playerC;
case "ComputerD":
return playerD;
}
throw new IllegalArgumentException("Couldn't find player for name=" + name);
}
@ -339,6 +341,7 @@ public abstract class MageTestPlayerBase {
}
protected TestPlayer createPlayer(String name) {
return new TestPlayer(name, RangeOfInfluence.ONE);
return new TestPlayer(new ComputerPlayer(name, RangeOfInfluence.ONE));
}
}

View file

@ -28,6 +28,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
/**
* Default game initialization params for red player (that plays with Mountains)
*/
@Override
public void useRedDefault() {
// *** ComputerA ***
// battlefield:ComputerA:Mountain:5
@ -88,6 +89,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param player {@link Player} to add cards for. Use either playerA or playerB.
* @param cardName Card name in string format.
*/
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName) {
addCard(gameZone, player, cardName, 1, false);
}
@ -100,6 +102,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param cardName Card name in string format.
* @param count Amount of cards to be added.
*/
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) {
addCard(gameZone, player, cardName, count, false);
}
@ -114,6 +117,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped.
* In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown
*/
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) {
@ -179,6 +183,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param player {@link Player} to set life count for.
* @param life Life count to set.
*/
@Override
public void setLife(TestPlayer player, int life) {
if (player.equals(playerA)) {
commandsA.put(Zone.OUTSIDE, "life:" + String.valueOf(life));
@ -190,16 +195,18 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
/**
* Define turn number to stop the game on.
*/
@Override
public void setStopOnTurn(int turn) {
stopOnTurn = turn == -1 ? null : Integer.valueOf(turn);
stopOnTurn = turn == -1 ? null : turn;
stopAtStep = PhaseStep.UNTAP;
}
/**
* Define turn number and step to stop the game on.
*/
@Override
public void setStopAt(int turn, PhaseStep step) {
stopOnTurn = turn == -1 ? null : Integer.valueOf(turn);
stopOnTurn = turn == -1 ? null : turn;
stopAtStep = step;
}
@ -208,6 +215,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
*
* @param turn Expected turn number to compare with. 1-based.
*/
@Override
public void assertTurn(int turn) throws AssertionError {
Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum());
}
@ -217,21 +225,28 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
*
* @param result Expected {@link GameResult} to compare with.
*/
@Override
public void assertResult(Player player, GameResult result) throws AssertionError {
if (player.equals(playerA)) {
GameResult actual = CardTestAPI.GameResult.DRAW;
if (currentGame.getWinner().equals("Player PlayerA is the winner")) {
actual = CardTestAPI.GameResult.WON;
} else if (currentGame.getWinner().equals("Player PlayerB is the winner")) {
actual = CardTestAPI.GameResult.LOST;
switch (currentGame.getWinner()) {
case "Player PlayerA is the winner":
actual = CardTestAPI.GameResult.WON;
break;
case "Player PlayerB is the winner":
actual = CardTestAPI.GameResult.LOST;
break;
}
Assert.assertEquals("Game results are not equal", result, actual);
} else if (player.equals(playerB)) {
GameResult actual = CardTestAPI.GameResult.DRAW;
if (currentGame.getWinner().equals("Player PlayerB is the winner")) {
actual = CardTestAPI.GameResult.WON;
} else if (currentGame.getWinner().equals("Player PlayerA is the winner")) {
actual = CardTestAPI.GameResult.LOST;
switch (currentGame.getWinner()) {
case "Player PlayerB is the winner":
actual = CardTestAPI.GameResult.WON;
break;
case "Player PlayerA is the winner":
actual = CardTestAPI.GameResult.LOST;
break;
}
Assert.assertEquals("Game results are not equal", result, actual);
}
@ -243,6 +258,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param player {@link Player} to get life for comparison.
* @param life Expected player's life to compare with.
*/
@Override
public void assertLife(Player player, int life) throws AssertionError {
int actual = currentGame.getPlayer(player.getId()).getLife();
Assert.assertEquals("Life amounts are not equal", life, actual);
@ -265,6 +281,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you want "at least one creature with given name should have specified p\t"
* Use ALL, if you want "all creature with gived name should have specified p\t"
*/
@Override
public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope)
throws AssertionError {
int count = 0;
@ -298,6 +315,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
/**
* {@inheritDoc}
*/
@Override
public void assertAbilities(Player player, String cardName, List<Ability> abilities)
throws AssertionError {
int count = 0;
@ -326,6 +344,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param player {@link Player} which permanents should be counted.
* @param count Expected count.
*/
@Override
public void assertPermanentCount(Player player, int count) throws AssertionError {
int actualCount = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) {
@ -343,6 +362,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
* @param cardName Name of the cards that should be counted.
* @param count Expected count.
*/
@Override
public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError {
int actualCount = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) {

View file

@ -1,5 +1,8 @@
package org.mage.test.serverside.base.impl;
import java.io.FileNotFoundException;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.cards.Card;
import mage.cards.decks.Deck;
@ -17,18 +20,18 @@ import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.GameException;
import mage.game.GameOptions;
import mage.game.command.CommandObject;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
import mage.players.Player;
import org.junit.Assert;
import org.junit.Before;
import org.mage.test.player.TestPlayer;
import org.mage.test.serverside.base.CardTestAPI;
import org.mage.test.serverside.base.CardTestAPI.GameResult;
import org.mage.test.serverside.base.MageTestPlayerBase;
import java.util.List;
import java.util.UUID;
/**
* API for test initialization and asserting the test results.
*
@ -36,6 +39,20 @@ import java.util.UUID;
*/
public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implements CardTestAPI {
// Defines the constant if for activate ability is not target but a ability on the stack to define
public static final String NO_TARGET = "NO_TARGET";
protected GameOptions gameOptions;
protected enum ExpectedType {
TURN_NUMBER,
RESULT,
LIFE,
BATTLEFIELD,
GRAVEYARD,
UNKNOWN
}
static {
// CardScanner.scanned = true;
CardScanner.scan();
@ -83,33 +100,119 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
removeAllCardsFromLibrary(playerB);
addCard(Zone.LIBRARY, playerB, "Plains", 10);
}
@Before
public void reset() throws GameException, FileNotFoundException {
if (currentGame != null) {
logger.debug("Resetting previous game and creating new one!");
currentGame = null;
System.gc();
}
currentGame = createNewGameAndPlayers();
activePlayer = playerA;
stopOnTurn = 2;
stopAtStep = PhaseStep.UNTAP;
for (Player player : currentGame.getPlayers().values()) {
TestPlayer testPlayer = (TestPlayer)player;
getCommands(testPlayer).clear();
getLibraryCards(testPlayer).clear();
getHandCards(testPlayer).clear();
getBattlefieldCards(testPlayer).clear();
getGraveCards(testPlayer).clear();
}
gameOptions = new GameOptions();
}
abstract protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException;
protected TestPlayer createPlayer(Game game, TestPlayer player, String name) throws GameException {
return createPlayer(game, player, name, "RB Aggro.dck");
}
protected TestPlayer createPlayer(Game game, TestPlayer player, String name, String deckName) throws GameException {
player = createNewPlayer(name);
player.setTestMode(true);
logger.debug("Loading deck...");
Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false);
Deck deck = Deck.load(DeckImporterUtil.importDeck(deckName), false, false);
logger.debug("Done!");
if (deck.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size());
}
game.addPlayer(player, deck);
game.loadCards(deck.getCards(), player.getId());
game.loadCards(deck.getSideboard(), player.getId());
game.addPlayer(player, deck);
return player;
}
/**
* Starts testing card by starting current game.
*
* @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game.
*/
public void execute() throws IllegalStateException {
if (currentGame == null || activePlayer == null) {
throw new IllegalStateException("Game is not initialized. Use load method to load a test case and initialize a game.");
}
for (Player player : currentGame.getPlayers().values()) {
TestPlayer testPlayer = (TestPlayer)player;
currentGame.cheat(player.getId(), getCommands(testPlayer));
currentGame.cheat(player.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer),
getBattlefieldCards(testPlayer), getGraveCards(testPlayer));
}
long t1 = System.nanoTime();
gameOptions.testMode = true;
gameOptions.stopOnTurn = stopOnTurn;
gameOptions.stopAtStep = stopAtStep;
currentGame.setGameOptions(gameOptions);
currentGame.start(activePlayer.getId());
long t2 = System.nanoTime();
logger.debug("Winner: " + currentGame.getWinner());
logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms");
}
protected TestPlayer createNewPlayer(String playerName) {
return createPlayer(playerName);
}
protected Player getPlayerFromName(String playerName, String line) {
Player player = null;
switch (playerName) {
case "ComputerA":
player = currentGame.getPlayer(playerA.getId());
break;
case "ComputerB":
player = currentGame.getPlayer(playerB.getId());
break;
case "ComputerC":
player = currentGame.getPlayer(playerC.getId());
break;
case "ComputerD":
player = currentGame.getPlayer(playerD.getId());
break;
default:
throw new IllegalArgumentException("Wrong player in 'battlefield' line, player=" + player + ", line=" + line);
}
return player;
}
/**
* Removes all cards from player's library from the game.
* Usually this should be used once before initialization to form the library in certain order.
*
* @param player {@link Player} to remove all library cards from.
*/
@Override
public void removeAllCardsFromLibrary(TestPlayer player) {
getCommands(player).put(Zone.LIBRARY, "clear");
}
@ -131,7 +234,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param player {@link Player} to add cards for. Use either playerA or playerB.
* @param cardName Card name in string format.
*/
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName) {
addCard(gameZone, player, cardName, 1, false);
}
@ -144,7 +246,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param cardName Card name in string format.
* @param count Amount of cards to be added.
*/
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) {
addCard(gameZone, player, cardName, count, false);
}
@ -159,7 +260,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped.
* In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown
*/
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) {
if (gameZone.equals(Zone.BATTLEFIELD)) {
@ -214,7 +314,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param player {@link Player} to set life count for.
* @param life Life count to set.
*/
@Override
public void setLife(TestPlayer player, int life) {
getCommands(player).put(Zone.OUTSIDE, "life:" + String.valueOf(life));
}
@ -747,6 +846,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
player.addAction(turnNum, step, "activate:Cast " + cardName + "$targetPlayer=" + target.getName() + "$manaInPool=" + manaInPool);
}
/**
*
* @param turnNum
* @param step
* @param player
* @param cardName
* @param targetName for modes you can add "mode=3" before target name, multiple targets can be seperated by ^
*/
public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) {
player.addAction(turnNum, step, "activate:Cast " + cardName + "$target=" + targetName);
}
@ -843,6 +950,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
public void attack(int turnNum, TestPlayer player, String attacker) {
player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:"+attacker);
}
public void attack(int turnNum, TestPlayer player, String attacker, TestPlayer defendingPlayer) {
player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:"+attacker+"$defendingPlayer="+defendingPlayer.getName());
}
public void attack(int turnNum, TestPlayer player, String attacker, String planeswalker) {
player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, new StringBuilder("attack:").append(attacker).append("$planeswalker=").append(planeswalker).toString());
@ -897,5 +1008,61 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
public void addTarget(TestPlayer player, TestPlayer targetPlayer) {
player.addTarget("targetPlayer="+targetPlayer.getName());
}
protected void skipInitShuffling() {
gameOptions.skipInitShuffling = true;
}
protected ExpectedType getExpectedType(String line) {
if (line.startsWith("turn:")) {
return ExpectedType.TURN_NUMBER;
}
if (line.startsWith("result:")) {
return ExpectedType.RESULT;
}
if (line.startsWith("life:")) {
return ExpectedType.LIFE;
}
if (line.startsWith("battlefield:")) {
return ExpectedType.BATTLEFIELD;
}
if (line.startsWith("graveyard:")) {
return ExpectedType.GRAVEYARD;
}
return ExpectedType.UNKNOWN;
}
protected String getStringParam(String line, int index) {
String[] params = line.split(":");
if (index > params.length - 1) {
throw new IllegalArgumentException("Not correct line: " + line);
}
return params[index];
}
protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) {
if (currentGame == null) {
throw new IllegalStateException("Current game is null");
}
if (scope.equals(Filter.ComparisonScope.All)) {
throw new UnsupportedOperationException("ComparisonScope.All is not implemented.");
}
for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) {
if (permanent.getName().equals(cardName)) {
Assert.assertEquals("Power is not the same", power, permanent.getPower().getValue());
Assert.assertEquals("Toughness is not the same", toughness, permanent.getToughness().getValue());
break;
}
}
}
protected int getIntParam(String line, int index) {
String[] params = line.split(":");
if (index > params.length - 1) {
throw new IllegalArgumentException("Not correct line: " + line);
}
return Integer.parseInt(params[index]);
}
}