diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java index a390ef48ff2..6d18f7535e4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java @@ -123,14 +123,23 @@ public class CleverImpersonatorTest extends CardTestPlayerBase { } /** - * So I copied Jace, Vryns Prodigy with Clever Impersonator (it was tapped - * and I needed a blocker for a token...), and Jace got to survive until the - * next turn. When I looted, he flipped, and I got an error message I - * couldn't get rid of, forcing me to concede. I'm not sure what the correct - * outcome is rules-wise. + * 712.14a. If a spell or ability puts a transforming double-faced card onto the battlefield "transformed" + * or "converted," it enters the battlefield with its back face up. If a player is instructed to put a card + * that isn't a transforming double-faced card onto the battlefield transformed or converted, that card stays in + * its current zone. + *

+ * * So I copied Jace, Vryns Prodigy with Clever Impersonator (it was tapped + * * and I needed a blocker for a token...), and Jace got to survive until the + * * next turn. When I looted, he flipped, and I got an error message I + * * couldn't get rid of, forcing me to concede. I'm not sure what the correct + * * outcome is rules-wise. */ @Test public void testCopyCreatureOfFlipPlaneswalker() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.LIBRARY, playerA, "Swamp"); // for discard addCard(Zone.BATTLEFIELD, playerA, "Island", 4); // {T}: Draw a card, then discard a card. If there are five or more cards in your graveyard, exile Jace, Vryn's Prodigy, then return him to the battefield transformed under his owner's control. @@ -143,23 +152,24 @@ public class CleverImpersonatorTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Clever Impersonator"); - setChoice(playerA, "Jace, Vryn's Prodigy"); - setChoice(playerA, "Jace, Vryn's Prodigy[only copy]"); // keep the copied Jace + setChoice(playerA, true); // yes to copy + setChoice(playerA, "Jace, Vryn's Prodigy"); // copy Jace + setChoice(playerA, "Jace, Vryn's Prodigy[only copy]"); // keep the copied Jace under legend rule activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card"); - setChoice(playerA, "Pillarfield Ox"); + setChoice(playerA, "Swamp"); setStopAt(3, PhaseStep.BEGIN_COMBAT); execute(); assertGraveyardCount(playerA, "Jace, Vryn's Prodigy", 1); - assertPermanentCount(playerA, "Pillarfield Ox", 1); - + assertPermanentCount(playerA, "Pillarfield Ox", 0); + assertExileCount(playerA, "Clever Impersonator", 1); // Clone does not come back as per 712.14a.. } /** * Reported bug: - * Could not use Clever Impersonator to copy Dawn's Reflection + * Could not use Clever Impersonator to copy Dawn's Reflection */ @Test public void dawnsReflectionCopiedByImpersonator() { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lci/OjerAxonilDeepestMightTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lci/OjerAxonilDeepestMightTest.java index 72e06471c00..b5601a7cc66 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/lci/OjerAxonilDeepestMightTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lci/OjerAxonilDeepestMightTest.java @@ -212,6 +212,37 @@ public class OjerAxonilDeepestMightTest extends CardTestPlayerBase { assertTapped(ojer, true); } + /** + * 712.14a. If a spell or ability puts a transforming double-faced card onto the battlefield "transformed" + * or "converted," it enters the battlefield with its back face up. If a player is instructed to put a card + * that isn't a transforming double-faced card onto the battlefield transformed or converted, that card stays in + * its current zone. + */ + @Test + public void test_CloneDoNotTransform() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, ojer, 1); + addCard(Zone.HAND, playerA, "Sakashima the Impostor", 1); // Clone keeping its name for easier test. + addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 6); + addCard(Zone.HAND, playerA, "Doom Blade", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sakashima the Impostor"); + setChoice(playerA, true); // yes to clone + setChoice(playerA, ojer); // clone Ojer + + checkPermanentCount("Sakashima in play", 1, PhaseStep.BEGIN_COMBAT, playerA, "Sakashima the Impostor", 1); + checkPT("PT 4/4 so copy happened", 1, PhaseStep.BEGIN_COMBAT, playerA, "Sakashima the Impostor", 4, 4); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Doom Blade", "Sakashima the Impostor"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, ojer, 1); + assertGraveyardCount(playerA, "Sakashima the Impostor", 1); // is not transformable, so didn't return. + } + @Test public void test_watching_Chandra_emblem_damage() { setStrictChooseMode(true); diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java index d741e831cf9..f234e74a580 100644 --- a/Mage/src/main/java/mage/game/ZonesHandler.java +++ b/Mage/src/main/java/mage/game/ZonesHandler.java @@ -334,13 +334,19 @@ public final class ZonesHandler { isGoodToMove = true; } else if (event.getToZone().equals(Zone.BATTLEFIELD)) { // non-permanents can't move to battlefield - // "return to battlefield transformed" abilities uses game state value instead "info.transformed", so check it too - // TODO: possible bug with non permanent on second side like Life // Death, see https://github.com/magefree/mage/issues/11573 - // need to check second side here, not status only // TODO: possible bug with Nightbound, search all usage of getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED and insert additional check Ability.checkCard - boolean wantToPutTransformed = card.isTransformable() - && Boolean.TRUE.equals(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId())); - isGoodToMove = card.isPermanent(game) || wantToPutTransformed; + /* + * 712.14a. If a spell or ability puts a transforming double-faced card onto the battlefield "transformed" + * or "converted," it enters the battlefield with its back face up. If a player is instructed to put a card + * that isn't a transforming double-faced card onto the battlefield transformed or converted, that card stays in + * its current zone. + */ + boolean wantToTransform = Boolean.TRUE.equals(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId())); + if (wantToTransform) { + isGoodToMove = card.isTransformable() && card.getSecondCardFace().isPermanent(game); + } else { + isGoodToMove = card.isPermanent(game); + } } else { // other zones allows to move isGoodToMove = true; diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index d465f350a48..82eb398d3eb 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -4708,6 +4708,14 @@ public abstract class PlayerImpl implements Player, Serializable { List infoList = new ArrayList<>(); for (Card card : cards) { fromZone = game.getState().getZone(card.getId()); + // 712.14a. If a spell or ability puts a transforming double-faced card onto the battlefield "transformed" + // or "converted," it enters the battlefield with its back face up. If a player is instructed to put a card + // that isn't a transforming double-faced card onto the battlefield transformed or converted, that card stays in + // its current zone. + Boolean enterTransformed = (Boolean) game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId()); + if (enterTransformed != null && enterTransformed && !card.isTransformable()) { + continue; + } ZoneChangeEvent event = new ZoneChangeEvent(card.getId(), source, byOwner ? card.getOwnerId() : getId(), fromZone, Zone.BATTLEFIELD, appliedEffects); infoList.add(new ZoneChangeInfo.Battlefield(event, faceDown, tapped, source));