mirror of
https://github.com/magefree/mage.git
synced 2025-12-22 03:22:00 -08:00
Rework face down effect to layer 1b (#11689)
* add test case for face-down permanent losing abilities * rework BecomesFaceDownCreatureEffect to layer 1b * add test for becoming Treasure * small refactor: Minimus Containment * add mycosynth lattice test
This commit is contained in:
parent
ba0c3baf6c
commit
e431cd90ab
3 changed files with 161 additions and 81 deletions
|
|
@ -2,12 +2,10 @@ package mage.cards.m;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.abilities.mana.AnyColorManaAbility;
|
||||
import mage.abilities.token.TreasureAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
|
|
@ -51,16 +49,10 @@ public final class MinimusContainment extends CardImpl {
|
|||
|
||||
class MinimusContainmentEffect extends ContinuousEffectImpl {
|
||||
|
||||
private static final Ability ability = new AnyColorManaAbility();
|
||||
|
||||
static {
|
||||
Cost cost = new SacrificeSourceCost();
|
||||
cost.setText("sacrifice this artifact");
|
||||
ability.addCost(cost);
|
||||
}
|
||||
private static final Ability ability = new TreasureAbility(false);
|
||||
|
||||
MinimusContainmentEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit);
|
||||
super(Duration.WhileOnBattlefield, Outcome.LoseAbility);
|
||||
staticText = "enchanted permanent is a Treasure artifact with " +
|
||||
"\"{T}, Sacrifice this artifact: Add one mana of any color,\" and it loses all other abilities";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -418,7 +418,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
for (Card card : currentGame.getExile().getAllCards(currentGame)) {
|
||||
if (card.getName().equals("Birchlore Rangers")) {
|
||||
Assert.assertEquals("Birchlore Rangers has to be face up in exile", false, card.isFaceDown(currentGame));
|
||||
Assert.assertFalse("Birchlore Rangers has to be face up in exile", card.isFaceDown(currentGame));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -457,7 +457,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
for (Card card : playerA.getGraveyard().getCards(currentGame)) {
|
||||
if (card.getName().equals("Ashcloud Phoenix")) {
|
||||
Assert.assertEquals("Ashcloud Phoenix has to be face up in graveyard", false, card.isFaceDown(currentGame));
|
||||
Assert.assertFalse("Ashcloud Phoenix has to be face up in graveyard", card.isFaceDown(currentGame));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -493,7 +493,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
for (Card card : playerA.getGraveyard().getCards(currentGame)) {
|
||||
if (card.getName().equals("Ashcloud Phoenix")) {
|
||||
Assert.assertEquals("Ashcloud Phoenix has to be face up in graveyard", false, card.isFaceDown(currentGame));
|
||||
Assert.assertFalse("Ashcloud Phoenix has to be face up in graveyard", card.isFaceDown(currentGame));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -772,7 +772,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertPermanentCount(playerA, "Brine Elemental", 1);
|
||||
assertPermanentCount(playerB, "Brine Elemental", 1);
|
||||
Assert.assertTrue("Skip next turn has to be added to TurnMods", currentGame.getState().getTurnMods().size() == 1);
|
||||
Assert.assertEquals("Skip next turn has to be added to TurnMods", 1, currentGame.getState().getTurnMods().size());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -963,7 +963,6 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
@Test
|
||||
public void test_LandWithMorph_MorphAfterLand() {
|
||||
removeAllCardsFromHand(playerA);
|
||||
|
||||
// Morph {2}
|
||||
addCard(Zone.HAND, playerA, "Zoetic Cavern");
|
||||
|
|
@ -1118,4 +1117,108 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoseAbilities() {
|
||||
addCard(Zone.HAND, playerA, "Monastery Flock");
|
||||
addCard(Zone.HAND, playerA, "Tamiyo's Compleation");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Secret Plans"); // face-down creatures get +0/+1
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 7);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Monastery Flock using Morph");
|
||||
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.BEGIN_COMBAT, playerA, "{U}: Turn this", true);
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Tamiyo's Compleation", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{U}: Turn this", false);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertTapped(EmptyNames.FACE_DOWN_CREATURE.toString(), true);
|
||||
assertAttachedTo(playerA, "Tamiyo's Compleation", EmptyNames.FACE_DOWN_CREATURE.toString(), true);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBecomeTreasure() {
|
||||
addCard(Zone.HAND, playerA, "Sage-Eye Harrier"); // 1/5 Flying, Morph 3W
|
||||
addCard(Zone.HAND, playerA, "Minimus Containment"); // 2W Aura
|
||||
// Enchant nonland permanent
|
||||
// Enchanted permanent is a Treasure artifact with “{T},Sacrifice this artifact: Add one mana of any color,”
|
||||
// and it loses all other abilities. (If it was a creature, it’s no longer a creature.)
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 7);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sage-Eye Harrier using Morph");
|
||||
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.BEGIN_COMBAT, playerA, "{3}{W}: Turn this", true);
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Minimus Containment", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}{W}: Turn this", false);
|
||||
|
||||
checkPlayableAbility("treasure", 1, PhaseStep.END_TURN, playerA, "{T}, Sacrifice ", true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.UPKEEP);
|
||||
execute();
|
||||
|
||||
assertSubtype(EmptyNames.FACE_DOWN_CREATURE.toString(), SubType.TREASURE);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.ARTIFACT, true);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.CREATURE, false);
|
||||
assertAttachedTo(playerA, "Minimus Containment", EmptyNames.FACE_DOWN_CREATURE.toString(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMycosynthAfter() {
|
||||
addCard(Zone.HAND, playerA, "Monastery Flock");
|
||||
addCard(Zone.HAND, playerA, "Mycosynth Lattice");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Secret Plans"); // face-down creatures get +0/+1
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 10);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Monastery Flock using Morph");
|
||||
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.BEGIN_COMBAT, playerA, "{U}: Turn this", true);
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Mycosynth Lattice");
|
||||
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{U}: Turn this", true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.ARTIFACT, true);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.CREATURE, true);
|
||||
assertNotSubtype(EmptyNames.FACE_DOWN_CREATURE.toString(), SubType.BIRD);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMycosynthBefore() {
|
||||
addCard(Zone.HAND, playerA, "Monastery Flock");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mycosynth Lattice");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Secret Plans"); // face-down creatures get +0/+1
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Monastery Flock using Morph");
|
||||
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.BEGIN_COMBAT, playerA, "{U}: Turn this", true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.ARTIFACT, true);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.CREATURE, true);
|
||||
assertNotSubtype(EmptyNames.FACE_DOWN_CREATURE.toString(), SubType.BIRD);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
|||
MANIFESTED,
|
||||
MANUAL,
|
||||
MEGAMORPHED,
|
||||
MORPHED
|
||||
MORPHED,
|
||||
DISGUISED,
|
||||
CLOAKED
|
||||
}
|
||||
|
||||
protected int zoneChangeCounter;
|
||||
|
|
@ -55,7 +57,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
public BecomesFaceDownCreatureEffect(Costs<Cost> turnFaceUpCosts, MageObjectReference objectReference, Duration duration, FaceDownType faceDownType) {
|
||||
super(duration, Outcome.BecomeCreature);
|
||||
super(duration, Layer.CopyEffects_1, SubLayer.FaceDownEffects_1b, Outcome.BecomeCreature);
|
||||
this.objectReference = objectReference;
|
||||
this.zoneChangeCounter = Integer.MIN_VALUE;
|
||||
if (turnFaceUpCosts != null) {
|
||||
|
|
@ -77,20 +79,20 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
|||
this.faceDownType = effect.faceDownType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BecomesFaceDownCreatureEffect copy() {
|
||||
return new BecomesFaceDownCreatureEffect(this);
|
||||
}
|
||||
|
||||
private static Costs<Cost> createCosts(Cost cost) {
|
||||
if (cost == null) {
|
||||
return null;
|
||||
return null; // ignore warning, null is used specifically
|
||||
}
|
||||
Costs<Cost> costs = new CostsImpl<>();
|
||||
costs.add(cost);
|
||||
return costs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BecomesFaceDownCreatureEffect copy() {
|
||||
return new BecomesFaceDownCreatureEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
|
|
@ -108,7 +110,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent;
|
||||
if (objectReference != null) {
|
||||
permanent = objectReference.getPermanent(game);
|
||||
|
|
@ -128,72 +130,55 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
|||
case MEGAMORPHED:
|
||||
permanent.setMorphed(true);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("FaceDownType not yet supported: " + faceDownType);
|
||||
}
|
||||
}
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
permanent.setName("");
|
||||
permanent.removeAllSuperTypes(game);
|
||||
permanent.removeAllCardTypes(game);
|
||||
permanent.addCardType(game, CardType.CREATURE);
|
||||
permanent.removeAllSubTypes(game);
|
||||
break;
|
||||
case ColorChangingEffects_5:
|
||||
permanent.getColor(game).setColor(new ObjectColor());
|
||||
break;
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
Card card = game.getCard(permanent.getId()); //
|
||||
List<Ability> abilitiesToRemove = new ArrayList<>();
|
||||
for (Ability ability : permanent.getAbilities()) {
|
||||
|
||||
// keep gained abilities from other sources, removes only own (card text)
|
||||
if (card != null && !card.getAbilities().contains(ability)) {
|
||||
continue;
|
||||
}
|
||||
permanent.setName(EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
permanent.removeAllSuperTypes(game);
|
||||
permanent.removeAllCardTypes(game);
|
||||
permanent.addCardType(game, CardType.CREATURE);
|
||||
permanent.removeAllSubTypes(game);
|
||||
permanent.getColor(game).setColor(ObjectColor.COLORLESS);
|
||||
Card card = game.getCard(permanent.getId());
|
||||
|
||||
// 701.33c
|
||||
// If a card with morph is manifested, its controller may turn that card face up using
|
||||
// either the procedure described in rule 702.36e to turn a face-down permanent with morph face up
|
||||
// or the procedure described above to turn a manifested permanent face up.
|
||||
//
|
||||
// so keep all tune face up abilities and other face down compatible
|
||||
if (ability.getWorksFaceDown()) {
|
||||
ability.setRuleVisible(false);
|
||||
continue;
|
||||
}
|
||||
List<Ability> abilitiesToRemove = new ArrayList<>();
|
||||
for (Ability ability : permanent.getAbilities()) {
|
||||
|
||||
if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
|
||||
if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
abilitiesToRemove.add(ability);
|
||||
}
|
||||
permanent.removeAbilities(abilitiesToRemove, source.getSourceId(), game);
|
||||
if (turnFaceUpAbility != null) {
|
||||
permanent.addAbility(turnFaceUpAbility, source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case PTChangingEffects_7:
|
||||
if (sublayer == SubLayer.SetPT_7b) {
|
||||
permanent.getPower().setModifiedBaseValue(2);
|
||||
permanent.getToughness().setModifiedBaseValue(2);
|
||||
// keep gained abilities from other sources, removes only own (card text)
|
||||
if (card != null && !card.getAbilities().contains(ability)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 701.33c
|
||||
// If a card with morph is manifested, its controller may turn that card face up using
|
||||
// either the procedure described in rule 702.36e to turn a face-down permanent with morph face up
|
||||
// or the procedure described above to turn a manifested permanent face up.
|
||||
//
|
||||
// so keep all tune face up abilities and other face down compatible
|
||||
if (ability.getWorksFaceDown()) {
|
||||
ability.setRuleVisible(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
|
||||
if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
abilitiesToRemove.add(ability);
|
||||
}
|
||||
} else if (duration == Duration.Custom && foundPermanent == true) {
|
||||
permanent.removeAbilities(abilitiesToRemove, source.getSourceId(), game);
|
||||
if (turnFaceUpAbility != null) { // TODO: shouldn't be added by this effect, but separately
|
||||
permanent.addAbility(turnFaceUpAbility, source.getSourceId(), game);
|
||||
}
|
||||
permanent.getPower().setModifiedBaseValue(2);
|
||||
permanent.getToughness().setModifiedBaseValue(2);
|
||||
} else if (duration == Duration.Custom && foundPermanent) {
|
||||
discard();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLayer(Layer layer) {
|
||||
return layer == Layer.PTChangingEffects_7 || layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue