Fixing issues with Changelings and general problems with creature types (ready to merge) (#7098)

* updated Changelings to use setIsAllCreatureTypes

* updated Dr Julius Jumblemorph and Mistform Ultimus to not use changeling

* added test for Mistform Ultimus

* updated effects which give all creature types to controlled creatures

* updated effects which give all creature types to targeted creatures

* Update LoseAllCreatureTypesTargetEffect.java

* updated effects which give all creature types to attached creatures

* Update EgoErasure.java

* added another test for changelings

* updated two tokens I left out before

* updated hasSubtype

* updated shareCreatureTypes

* fixed an incorrect test

* cleaned up some cards which check for shared creature types

* added new changeling test

* fixed issue with shareCreatureTypes

* fixed a text issue

* added new tests for subtype effects

* various individual card fixes and cleanups

* fixed and updated various effects

* many more fixes

* a few more fixes

* added test for One with the Stars

* added changeling verify test

* updated effects which add additional subtypes

* more miscellaneous fixes

* added additional test

* some fixes for card type checks

* updated methods for adding types to make it easier to avoid duplicates and illegal additions

* small test update

* fixed a recursive loop issue

* fixed another error

* fixed it for real this time

* streamlined type removal process

* streamlined subtype set generation
This commit is contained in:
Evan Kranzler 2020-10-30 22:32:59 -04:00 committed by GitHub
parent 42e00b7a37
commit 8617bc128e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
182 changed files with 2431 additions and 2820 deletions

View file

@ -2,7 +2,10 @@
package org.mage.test.cards.abilities.keywords;
import mage.abilities.Ability;
import mage.abilities.keyword.ChangelingAbility;
import mage.abilities.keyword.HasteAbility;
import mage.constants.PhaseStep;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
@ -10,7 +13,6 @@ import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class ChangelingTest extends CardTestPlayerBase {
@ -80,7 +82,7 @@ public class ChangelingTest extends CardTestPlayerBase {
/**
* NOTE: As of 05/06/2017 this test is failing due to a bug in code.
* See issue #3316
*
* <p>
* Kaseto, Orochi Archmage do not give Chameleon Colossus +2/+2 , even though Chameleon Colossus should have the "snake" type
*/
@Test
@ -103,10 +105,10 @@ public class ChangelingTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Chameleon Colossus");
/* Nessian Asp {4}{G} - 4/5
* Creature Snake
* Reach
* {6}{G}: Monstrosity 4. (If this creature isn't monstrous, put four +1/+1 counters on it and it becomes monstrous.)
*/
* Creature Snake
* Reach
* {6}{G}: Monstrosity 4. (If this creature isn't monstrous, put four +1/+1 counters on it and it becomes monstrous.)
*/
addCard(Zone.BATTLEFIELD, playerA, "Nessian Asp");
@ -122,4 +124,41 @@ public class ChangelingTest extends CardTestPlayerBase {
assertPowerToughness(playerA, "Chameleon Colossus", 6, 6);
}
@Test
public void testLoseAllCreatureTypes() {
addCard(Zone.BATTLEFIELD, playerA, "Game-Trail Changeling");
addCard(Zone.BATTLEFIELD, playerA, "Goblin Chieftain");
addCard(Zone.HAND, playerA, "Nameless Inversion");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nameless Inversion", "Game-Trail Changeling");
setStopAt(1, PhaseStep.END_TURN);
execute();
// Should have no creature types but still have the Changeling ability
assertPowerToughness(playerA, "Game-Trail Changeling", 7, 1);
assertNotSubtype("Game-Trail Changeling", SubType.SHAPESHIFTER);
assertAbility(playerA, "Game-Trail Changeling", HasteAbility.getInstance(), false);
assertAbility(playerA, "Game-Trail Changeling", ChangelingAbility.getInstance(), true);
}
@Test
public void testLoseAbilities() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerB, "Game-Trail Changeling");
addCard(Zone.HAND, playerA, "Merfolk Trickster");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Merfolk Trickster");
addTarget(playerA, "Game-Trail Changeling");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertTapped("Game-Trail Changeling", true);
assertSubtype("Game-Trail Changeling", SubType.GOBLIN);
assertSubtype("Game-Trail Changeling", SubType.ELF);
assertSubtype("Game-Trail Changeling", SubType.SHAPESHIFTER);
assertAbility(playerB, "Game-Trail Changeling", ChangelingAbility.getInstance(), false);
}
}

View file

@ -1,5 +1,6 @@
package org.mage.test.cards.continuous;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
@ -8,29 +9,33 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
public class ChangelingTest extends CardTestPlayerBase {
// Mistform Ultimus is every creature type
private final String ultimus = "Mistform Ultimus";
private static final String ultimus = "Mistform Ultimus";
// each creature gets +1/+1 for each creature you control that shares a creatureype
private final String coatOfArms = "Coat of Arms";
private static final String coatOfArms = "Coat of Arms";
// all merfolk get +1/+1
private final String lordOfAtlantis = "Lord of Atlantis";
private static final String lordOfAtlantis = "Lord of Atlantis";
// all illusions get +1/+1
private final String lordOfUnreal = "Lord of the Unreal";
private static final String lordOfUnreal = "Lord of the Unreal";
// mutavault becomes a token that is all creature types
private final String mutavault = "Mutavault";
private static final String mutavault = "Mutavault";
// vehicles have no creature type
private static final String copter = "Smuggler's Copter";
// 2/2 changeling
private final String woodlandChangeling = "Woodland Changeling";
private static final String woodlandChangeling = "Woodland Changeling";
@Test
public void coatOfArmsTest(){
public void coatOfArmsTest() {
addCard(Zone.BATTLEFIELD, playerA, ultimus);
addCard(Zone.BATTLEFIELD, playerA, coatOfArms);
addCard(Zone.BATTLEFIELD, playerA, lordOfAtlantis);
addCard(Zone.BATTLEFIELD, playerA, lordOfUnreal);
addCard(Zone.BATTLEFIELD, playerA, mutavault);
addCard(Zone.BATTLEFIELD, playerA, copter);
addCard(Zone.BATTLEFIELD, playerA, woodlandChangeling, 2);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: Until end of turn {this} becomes");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew");
setChoice(playerA, ultimus);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
@ -54,10 +59,15 @@ public class ChangelingTest extends CardTestPlayerBase {
coat of arms: +5
*/
assertPowerToughness(playerA, mutavault, 9, 9);
/*
smuggler's copter; +3
*/
assertType(copter, CardType.CREATURE, true);
assertPowerToughness(playerA, copter, 3, 3);
}
@Test
public void testMetallicMimicChangelingTrigger(){
public void testMetallicMimicChangelingTrigger() {
// all creatures with the chosen subtype come into play with a +1/+1 counter
final String mimic = "Metallic Mimic";

View file

@ -82,7 +82,6 @@ public class GainAbilityDependenciesTest extends CardTestPlayerBase {
// equip
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {3}", "Elephant");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkAbility("must have all type ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elephant", ChangelingAbility.class, true);
// attack with +1 token
attack(3, playerA, "Elephant", playerB);

View file

@ -1,6 +1,7 @@
package org.mage.test.cards.continuous;
import mage.cards.Card;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.SubType;
import mage.constants.Zone;
@ -296,6 +297,49 @@ public class SubTypeChangingEffectsTest extends CardTestPlayerBase {
}
}
}
@Test
public void testKeepOtherTypes() {
// Dragonshift (2013-04-15)
// Each affected creature will lose all other colors and creature types and be only red, blue, and a Dragon.
// Each will retain any other types it may have had, such as artifact.
addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 3);
addCard(Zone.BATTLEFIELD, playerA, "Gingerbrute");
addCard(Zone.HAND, playerA, "Dragonshift");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dragonshift", "Gingerbrute");
setStopAt(1, PhaseStep.END_TURN);
execute();
// Food is an artifact subtype and should not be removed
assertType("Gingerbrute", CardType.ARTIFACT, SubType.FOOD);
// Golem is a creature subtype and should be removed
assertType("Gingerbrute", CardType.CREATURE, SubType.DRAGON);
assertNotSubtype("Gingerbrute", SubType.GOLEM);
}
@Test
public void testKeepOtherTypes2() {
// Dragonshift (2013-04-15)
// Each affected creature will lose all other colors and creature types and be only red, blue, and a Dragon.
// Each will retain any other types it may have had, such as artifact.
addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 7);
addCard(Zone.BATTLEFIELD, playerA, "Gingerbrute");
addCard(Zone.HAND, playerA, "Dragonshift");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dragonshift with overload");
setStopAt(1, PhaseStep.END_TURN);
execute();
// Food is an artifact subtype and should not be removed
assertType("Gingerbrute", CardType.ARTIFACT, SubType.FOOD);
// Golem is a creature subtype and should be removed
assertType("Gingerbrute", CardType.CREATURE, SubType.DRAGON);
assertNotSubtype("Gingerbrute", SubType.GOLEM);
}
}

View file

@ -1,25 +1,25 @@
package org.mage.test.cards.copy;
import mage.abilities.common.BecomesTargetTriggeredAbility;
import mage.abilities.keyword.IndestructibleAbility;
import mage.abilities.keyword.LifelinkAbility;
import mage.constants.PhaseStep;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import static org.junit.Assert.*;
/**
* @author noxx
*
* <p>
* Card: You may have {this} 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."
*
*/
public class PhantasmalImageTest extends CardTestPlayerBase {
@ -130,6 +130,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
// CardTestPlayerAPIImpl.assertPowerToughness:351->CardTestPlayerAPIImpl.assertPowerToughness:337
// There is no such creature under player's control with specified power&toughness, player=PlayerA,
// cardName=Ravager of the Fells (found similar: 1, one of them: power=8 toughness=8)
/**
* Tests copying already transformed creature Makes sure it still has "When
* this creature becomes the target of a spell or ability, sacrifice it"
@ -313,7 +314,6 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
* the "When this creature becomes the target of a spell or ability,
* sacrifice it." ability. I did not pay attention to see if it failed to
* become an illusion too.
*
*/
@Test
public void testCopiedFrostTitan() {
@ -426,13 +426,13 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
/**
* Action Game State 1 -----------------> Game State 2 (On 'field) (Move to
* GY) (In graveyard)
*
* <p>
* 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]
*
* <p>
* 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
@ -446,13 +446,12 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
* 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.
*
* <p>
* 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 artifact's ability triggers twice, even though the artifact goes to
* its owner's graveyard at the same time as the creatures.
*
*/
@Test
public void testPersist() {
@ -549,7 +548,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
* 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
*
* <p>
* 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?
@ -610,4 +609,69 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Elemental", 1);
}
@Test
public void testAnimatedArtifact() {
addCard(Zone.BATTLEFIELD, playerB, "Chimeric Staff");
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.HAND, playerA, "Phantasmal Image");
setChoice(playerB, "X=1");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{X}");
setChoice(playerA, "Chimeric Staff");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Phantasmal Image");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
Permanent staffA = getPermanent("Chimeric Staff", playerA);
assertTrue("Phantasmal Image should be an artifact", staffA.isArtifact());
assertTrue("Phantasmal Image should not be a creature", !staffA.isCreature());
assertTrue("Phantasmal Image should not be an Illusion", !staffA.hasSubtype(SubType.ILLUSION, currentGame));
assertTrue("Phantasmal Image should not be a Construct", !staffA.hasSubtype(SubType.CONSTRUCT, currentGame));
assertTrue("Phantasmal Image should have the sacrifice trigger", staffA.getAbilities(currentGame).containsClass(BecomesTargetTriggeredAbility.class));
Permanent staffB = getPermanent("Chimeric Staff", playerB);
assertTrue("Chimeric Staff should be an artifact", staffB.isArtifact());
assertTrue("Chimeric Staff should be a creature", staffB.isCreature());
assertTrue("Chimeric Staff should be a Construct", staffB.hasSubtype(SubType.CONSTRUCT, currentGame));
}
@Test
public void testAnimatedTribal() {
addCard(Zone.BATTLEFIELD, playerB, "Cloak and Dagger");
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
addCard(Zone.HAND, playerA, "Karn's Touch");
addCard(Zone.HAND, playerA, "Phantasmal Image");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Karn's Touch", "Cloak and Dagger");
setChoice(playerA, "Cloak and Dagger");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Phantasmal Image");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
Permanent cloakA = getPermanent("Cloak and Dagger", playerA);
assertTrue("Phantasmal Image should be an artifact", cloakA.isArtifact());
assertTrue("Phantasmal Image should be tribal", cloakA.isTribal());
assertTrue("Phantasmal Image should not be a creature", !cloakA.isCreature());
assertTrue("Phantasmal Image should be a Rogue", cloakA.hasSubtype(SubType.ROGUE, currentGame));
assertTrue("Phantasmal Image should be an Illusion", cloakA.hasSubtype(SubType.ILLUSION, currentGame));
assertTrue("Phantasmal Image should be an Equipment", cloakA.hasSubtype(SubType.EQUIPMENT, currentGame));
assertTrue("Phantasmal Image should have the sacrifice trigger", cloakA.getAbilities(currentGame).containsClass(BecomesTargetTriggeredAbility.class));
Permanent cloakB = getPermanent("Cloak and Dagger", playerB);
assertTrue("Cloak and Dagger should be an artifact", cloakB.isArtifact());
assertTrue("Cloak and Dagger should be a creature", cloakB.isCreature());
assertTrue("Cloak and Dagger should be tribal", cloakB.isTribal());
assertTrue("Cloak and Dagger should be a Rogue", cloakB.hasSubtype(SubType.ROGUE, currentGame));
assertTrue("Cloak and Dagger should be an Equipment", cloakB.hasSubtype(SubType.EQUIPMENT, currentGame));
}
}

View file

@ -0,0 +1,42 @@
package org.mage.test.cards.single.lgn;
import mage.abilities.keyword.HasteAbility;
import mage.constants.PhaseStep;
import mage.constants.SubType;
import mage.constants.Zone;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author TheElk801
*/
public class MistformUltimusTest extends CardTestPlayerBase {
private static final String ultimus = "Mistform Ultimus";
private static final String chieftain = "Goblin Chieftain";
private static final String gametrail = "Game-Trail Changeling";
private static final String inversion = "Nameless Inversion";
@Test
public void testMistformUltimus() {
addCard(Zone.BATTLEFIELD, playerA, ultimus);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertSubtype(ultimus, SubType.GOBLIN);
}
@Test
public void testGoblinChieftain() {
addCard(Zone.BATTLEFIELD, playerA, ultimus);
addCard(Zone.BATTLEFIELD, playerA, chieftain);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPowerToughness(playerA, ultimus, 4, 4);
assertAbility(playerA, ultimus, HasteAbility.getInstance(), true);
}
}

View file

@ -0,0 +1,103 @@
package org.mage.test.cards.single.thb;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.mana.AnyColorManaAbility;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.SubType;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author TheElk801
*/
public class OneWithTheStarsTest extends CardTestPlayerBase {
private static final String stars = "One with the Stars";
private static final String knight = "Dragonsoul Knight";
private static final String brute = "Gingerbrute";
private static final String shrine = "Honden of Cleansing Fire";
private static final String blossom = "Bitterblossom";
private void makeRainbowLand(int count) {
for (int i = 0; i < count; i++) {
addCustomCardWithAbility(
"Rainbow", playerA, new AnyColorManaAbility(),
null, CardType.LAND, "", Zone.BATTLEFIELD
);
}
}
@Test
public void testDragonsoulKnight() {
addCard(Zone.HAND, playerA, stars);
addCard(Zone.BATTLEFIELD, playerA, knight);
makeRainbowLand(9);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, stars, knight);
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{W}{U}");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertType(knight, CardType.ENCHANTMENT, true);
assertType(knight, CardType.CREATURE, false);
assertNotSubtype(knight, SubType.HUMAN);
assertNotSubtype(knight, SubType.KNIGHT);
assertNotSubtype(knight, SubType.DRAGON);
assertAbility(playerA, knight, FlyingAbility.getInstance(), true);
assertAbility(playerA, knight, TrampleAbility.getInstance(), true);
}
@Test
public void testGingerbrute() {
addCard(Zone.HAND, playerA, stars);
addCard(Zone.BATTLEFIELD, playerA, brute);
makeRainbowLand(4);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, stars, brute);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertType(brute, CardType.ENCHANTMENT, true);
assertType(brute, CardType.ARTIFACT, false);
assertType(brute, CardType.CREATURE, false);
assertNotSubtype(brute, SubType.GOLEM);
assertNotSubtype(brute, SubType.FOOD);
}
@Test
public void testShrine() {
addCard(Zone.HAND, playerA, stars);
addCard(Zone.BATTLEFIELD, playerA, shrine);
makeRainbowLand(4);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, stars, shrine);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertType(shrine, CardType.ENCHANTMENT, SubType.SHRINE);
}
@Test
public void testBitterblossom() {
addCard(Zone.HAND, playerA, stars);
addCard(Zone.BATTLEFIELD, playerA, blossom);
makeRainbowLand(4);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, stars, blossom);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertType(blossom, CardType.ENCHANTMENT, true);
assertType(blossom, CardType.TRIBAL, false);
assertNotSubtype(blossom, SubType.FAERIE);
}
}

View file

@ -1116,7 +1116,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
//Assert.assertNotEquals("", cardName);
Permanent found = getPermanent(cardName);
if (subType != null) {
Assert.assertFalse("(Battlefield) card sub-type equal (" + cardName + ':' + subType.getDescription() + ')', found.getSubtype(currentGame).contains(subType));
Assert.assertFalse("(Battlefield) card sub-type equal (" + cardName + ':' + subType.getDescription() + ')', found.hasSubtype(subType, currentGame));
}
}
@ -1130,7 +1130,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
//Assert.assertNotEquals("", cardName);
Permanent found = getPermanent(cardName);
if (subType != null) {
Assert.assertTrue("(Battlefield) card sub-type equal (" + cardName + ':' + subType.getDescription() + ')', found.getSubtype(currentGame).contains(subType));
Assert.assertTrue("(Battlefield) card sub-type equal (" + cardName + ':' + subType.getDescription() + ')', found.hasSubtype(subType, currentGame));
}
}