Fixed Yixlid Jailer interaction with cards moving to graveyard (#8402)

* Fixed Yixlid Jailer interaction with cards moving to graveyard (fixes #8311)

* Yixlid Jailer - Revert previous workaround and add rules modifying effect
This commit is contained in:
Daniel Bomar 2021-10-24 02:57:02 -05:00 committed by GitHub
parent b63623b40f
commit 83d37a7f35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 239 additions and 35 deletions

View file

@ -4,12 +4,15 @@ import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.ZoneChangeTriggeredAbility;
import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player; import mage.players.Player;
/** /**
@ -28,6 +31,7 @@ public final class YixlidJailer extends CardImpl {
// Cards in graveyards lose all abilities. // Cards in graveyards lose all abilities.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YixlidJailerEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YixlidJailerEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YixlidJailerRulesEffect()));
} }
private YixlidJailer(final YixlidJailer card) { private YixlidJailer(final YixlidJailer card) {
@ -38,54 +42,85 @@ public final class YixlidJailer extends CardImpl {
public YixlidJailer copy() { public YixlidJailer copy() {
return new YixlidJailer(this); return new YixlidJailer(this);
} }
}
static class YixlidJailerEffect extends ContinuousEffectImpl { class YixlidJailerEffect extends ContinuousEffectImpl {
YixlidJailerEffect() { public YixlidJailerEffect() {
super(Duration.WhileOnBattlefield, Outcome.LoseAbility); super(Duration.WhileOnBattlefield, Outcome.LoseAbility);
staticText = "Cards in graveyards lose all abilities."; 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) { private YixlidJailerEffect(final YixlidJailerEffect effect) {
super(effect); super(effect);
} }
@Override @Override
public YixlidJailerEffect copy() { public YixlidJailerEffect copy() {
return new YixlidJailerEffect(this); return new YixlidJailerEffect(this);
} }
@Override @Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
if (layer == Layer.AbilityAddingRemovingEffects_6) { if (layer == Layer.AbilityAddingRemovingEffects_6) {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null) { if (controller != null) {
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId); Player player = game.getPlayer(playerId);
if (player != null) { if (player != null) {
for (Card card : player.getGraveyard().getCards(game)) { for (Card card : player.getGraveyard().getCards(game)) {
if (card != null) { if (card != null) {
card.looseAllAbilities(game); card.looseAllAbilities(game);
}
} }
} }
} }
return true;
} }
return true;
} }
return false;
} }
return false;
}
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
return false; return false;
} }
@Override @Override
public boolean hasLayer(Layer layer) { public boolean hasLayer(Layer layer) {
return layer == Layer.AbilityAddingRemovingEffects_6; 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;
} }
} }

View file

@ -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 isnt 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
}
}