diff --git a/Mage.Sets/src/mage/cards/y/YixlidJailer.java b/Mage.Sets/src/mage/cards/y/YixlidJailer.java index 2f5bf3d367..359ab2f0e5 100644 --- a/Mage.Sets/src/mage/cards/y/YixlidJailer.java +++ b/Mage.Sets/src/mage/cards/y/YixlidJailer.java @@ -4,12 +4,15 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; +import mage.game.events.GameEvent; import mage.players.Player; /** @@ -28,6 +31,7 @@ public final class YixlidJailer extends CardImpl { // Cards in graveyards lose all abilities. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YixlidJailerEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YixlidJailerRulesEffect())); } private YixlidJailer(final YixlidJailer card) { @@ -38,54 +42,85 @@ public final class YixlidJailer extends CardImpl { public YixlidJailer copy() { return new YixlidJailer(this); } +} - static class YixlidJailerEffect extends ContinuousEffectImpl { +class YixlidJailerEffect extends ContinuousEffectImpl { - YixlidJailerEffect() { - super(Duration.WhileOnBattlefield, Outcome.LoseAbility); - staticText = "Cards in graveyards lose all abilities."; + public YixlidJailerEffect() { + super(Duration.WhileOnBattlefield, Outcome.LoseAbility); + staticText = "Cards in graveyards lose all abilities."; - this.dependencyTypes.add(DependencyType.AddingAbility); // Necrotic Ooze - } + this.dependencyTypes.add(DependencyType.AddingAbility); // Necrotic Ooze + } - YixlidJailerEffect(final YixlidJailerEffect effect) { - super(effect); - } + private YixlidJailerEffect(final YixlidJailerEffect effect) { + super(effect); + } - @Override - public YixlidJailerEffect copy() { - return new YixlidJailerEffect(this); - } + @Override + public YixlidJailerEffect copy() { + return new YixlidJailerEffect(this); + } - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - if (layer == Layer.AbilityAddingRemovingEffects_6) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - for (Card card : player.getGraveyard().getCards(game)) { - if (card != null) { - card.looseAllAbilities(game); - } + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + if (layer == Layer.AbilityAddingRemovingEffects_6) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + for (Card card : player.getGraveyard().getCards(game)) { + if (card != null) { + card.looseAllAbilities(game); } } } - return true; } + return true; } - return false; } + return false; + } - @Override - public boolean apply(Game game, Ability source) { - return false; - } + @Override + public boolean apply(Game game, Ability source) { + return false; + } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6; - } + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.AbilityAddingRemovingEffects_6; + } +} + +class YixlidJailerRulesEffect extends ContinuousRuleModifyingEffectImpl { + + public YixlidJailerRulesEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment, false, false); + } + + private YixlidJailerRulesEffect(final YixlidJailerRulesEffect effect) { + super(effect); + } + + @Override + public YixlidJailerRulesEffect copy() { + return new YixlidJailerRulesEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Object targetAbility = getValue("targetAbility"); + if (targetAbility instanceof ZoneChangeTriggeredAbility) { + ZoneChangeTriggeredAbility zoneAbility = (ZoneChangeTriggeredAbility) targetAbility; + return zoneAbility.getFromZone() != Zone.BATTLEFIELD && zoneAbility.getToZone() == Zone.GRAVEYARD; + } + return false; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/YixlidJailerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/YixlidJailerTest.java new file mode 100644 index 0000000000..426ca61013 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/YixlidJailerTest.java @@ -0,0 +1,169 @@ +package org.mage.test.cards.single.fut; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class YixlidJailerTest extends CardTestPlayerBase { + + // Rulings on Yixlid Jailer + // 1. If an ability triggers when the object that has it is put into a graveyard from the battlefield, that ability triggers from the battlefield and isn’t affected by Yixlid Jailer. (2021-03-19) + // 2. If an ability triggers when the object that has it is put into a graveyard from anywhere other than the battlefield, such as Krosan Tusker or Narcomoeba, that ability triggers from the graveyard. + // Yixlid Jailer stops those abilities from triggering at all. This includes abilities that trigger when a card is put into a graveyard “from anywhere,” even if that card was on the battlefield. (2021-03-19) + + @Test + public void narcomoebaBaseCase() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Narcomoeba", 1); + addCard(Zone.HAND, playerA, "Thought Scour", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thought Scour"); + addTarget(playerA, playerA); + setChoice(playerA, true); // Use Narcomoeba's ability + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Narcomoeba", 1); + assertGraveyardCount(playerA, "Thought Scour", 1); + assertGraveyardCount(playerA, "Narcomoeba", 0); + } + + @Test + public void emrakulBaseCase() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Emrakul, the Aeons Torn", 1); + addCard(Zone.HAND, playerA, "Thought Scour", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thought Scour"); + addTarget(playerA, playerA); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, 0); // Emrakul should shuffle graveyard into library + } + + @Test + public void emrakulWrathBaseCase() { + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Emrakul, the Aeons Torn", 1); + addCard(Zone.HAND, playerA, "Wrath of God", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wrath of God"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, 0); // Emrakul should shuffle graveyard into library + assertPermanentCount(playerA, "Emrakul, the Aeons Torn", 0); + } + + @Test + public void narcomoebaWithJailer() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Narcomoeba", 1); + addCard(Zone.HAND, playerA, "Thought Scour", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerB, "Yixlid Jailer", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thought Scour"); + addTarget(playerA, playerA); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Narcomoeba", 0); + assertGraveyardCount(playerA, "Thought Scour", 1); + assertGraveyardCount(playerA, "Narcomoeba", 1); + } + + @Test + public void emrakulWithJailer() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Emrakul, the Aeons Torn", 1); + addCard(Zone.HAND, playerA, "Thought Scour", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerB, "Yixlid Jailer", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thought Scour"); + addTarget(playerA, playerA); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, "Emrakul, the Aeons Torn", 1); + } + + @Test + public void emrakulWrathWithJailer() { + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Emrakul, the Aeons Torn", 1); + addCard(Zone.HAND, playerA, "Wrath of God", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Yixlid Jailer", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wrath of God"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, "Emrakul, the Aeons Torn", 1); // Emrakul should not trigger even if removed from the battlefield + assertPermanentCount(playerA, "Emrakul, the Aeons Torn", 0); + } + + @Test + public void midnightReaperWithJailer() { + addCard(Zone.BATTLEFIELD, playerA, "Midnight Reaper", 1); + addCard(Zone.BATTLEFIELD, playerB, "Yixlid Jailer", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt"); + addTarget(playerA, "Midnight Reaper"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, "Midnight Reaper", 1); + assertPermanentCount(playerB, "Yixlid Jailer", 1); + assertLife(playerA, 19); // Midnight Reaper should still trigger + } + + @Test + public void midnightReaperWrathWithJailer() { + addCard(Zone.BATTLEFIELD, playerA, "Midnight Reaper", 1); + addCard(Zone.BATTLEFIELD, playerB, "Yixlid Jailer", 1); + addCard(Zone.HAND, playerA, "Wrath of God", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wrath of God"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, "Midnight Reaper", 1); + assertGraveyardCount(playerB, "Yixlid Jailer", 1); + assertLife(playerA, 19); // Midnight Reaper should still trigger + } +}