diff --git a/Mage.Sets/src/mage/cards/g/GhastlyConscription.java b/Mage.Sets/src/mage/cards/g/GhastlyConscription.java index d87fdb48a4b..4c2984be612 100644 --- a/Mage.Sets/src/mage/cards/g/GhastlyConscription.java +++ b/Mage.Sets/src/mage/cards/g/GhastlyConscription.java @@ -1,18 +1,12 @@ package mage.cards.g; -import java.util.*; -import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect; -import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect.FaceDownType; +import mage.abilities.effects.keyword.ManifestEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.StaticFilters; @@ -20,6 +14,8 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.*; + /** * * @author LevelX2 @@ -48,7 +44,10 @@ class GhastlyConscriptionEffect extends OneShotEffect { GhastlyConscriptionEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Exile all creature cards from target player's graveyard in a face-down pile, shuffle that pile, then manifest those cards. (To manifest a card, put it onto the battlefield face down as a 2/2 creature. Turn it face up at any time for its mana cost if it's a creature card.)"; + this.staticText = "Exile all creature cards from target player's graveyard in a face-down pile, " + + "shuffle that pile, then manifest those cards. " + + "(To manifest a card, put it onto the battlefield face down as a 2/2 creature. " + + "Turn it face up at any time for its mana cost if it's a creature card.)"; } private GhastlyConscriptionEffect(final GhastlyConscriptionEffect effect) { @@ -62,39 +61,24 @@ class GhastlyConscriptionEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // TODO: migrate to shared manifested code Player controller = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (controller != null && targetPlayer != null) { - List cardsToManifest = new ArrayList<>(); - for (Card card : targetPlayer.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)) { - cardsToManifest.add(card); - controller.moveCardToExileWithInfo(card, null, "", source, game, Zone.GRAVEYARD, true); - } - if (cardsToManifest.isEmpty()) { - return true; - } - Collections.shuffle(cardsToManifest); - game.informPlayers(controller.getLogName() + " shuffles the face-down pile"); - Ability newSource = source.copy(); - newSource.setWorksFaceDown(true); - for (Card card : cardsToManifest) { - ManaCosts manaCosts = null; - if (card.isCreature(game)) { - manaCosts = card.getSpellAbility() != null ? card.getSpellAbility().getManaCosts() : null; - if (manaCosts == null) { - manaCosts = new ManaCostsImpl<>("{0}"); - } - } - Card battlefieldCard = BecomesFaceDownCreatureEffect.findDefaultCardSideForFaceDown(game, card); - MageObjectReference objectReference = new MageObjectReference(battlefieldCard.getId(), battlefieldCard.getZoneChangeCounter(game) + 1, game); - game.addEffect(new BecomesFaceDownCreatureEffect(manaCosts, objectReference, Duration.Custom, FaceDownType.MANIFESTED), newSource); - } - Set toBattlefield = new LinkedHashSet(); - toBattlefield.addAll(cardsToManifest); - controller.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game, false, true, false, null); + if (controller == null || targetPlayer == null) { + return false; + } + List cardsToManifest = new ArrayList<>(); + for (Card card : targetPlayer.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)) { + cardsToManifest.add(card); + controller.moveCardToExileWithInfo(card, null, "", source, game, Zone.GRAVEYARD, true); + card.setFaceDown(true, game); + } + if (cardsToManifest.isEmpty()) { return true; } - return false; + Collections.shuffle(cardsToManifest); + game.informPlayers(controller.getLogName() + " shuffles the face-down pile"); + game.getState().processAction(game); + ManifestEffect.doManifestCards(game, source, controller, new LinkedHashSet<>(cardsToManifest)); + return true; } } diff --git a/Mage.Sets/src/mage/cards/j/JeskaiInfiltrator.java b/Mage.Sets/src/mage/cards/j/JeskaiInfiltrator.java index 7a81445c79e..58679c1649f 100644 --- a/Mage.Sets/src/mage/cards/j/JeskaiInfiltrator.java +++ b/Mage.Sets/src/mage/cards/j/JeskaiInfiltrator.java @@ -1,36 +1,25 @@ - package mage.cards.j; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CreatureCountCondition; -import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalRestrictionEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; -import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect; -import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect.FaceDownType; +import mage.abilities.effects.keyword.ManifestEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.game.ExileZone; +import mage.constants.*; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.*; + /** * * @author emerald000 @@ -81,38 +70,31 @@ class JeskaiInfiltratorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // TODO: migrade to shared manifest code Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Set cardsToManifest = new HashSet<>(); - cardsToManifest.add(source.getSourcePermanentIfItStillExists(game)); - cardsToManifest.add(controller.getLibrary().getFromTop(game)); - UUID exileId = UUID.randomUUID(); - controller.moveCardsToExile(cardsToManifest, source, game, false, exileId, ""); - ExileZone exileZone = game.getExile().getExileZone(exileId); - for (Card card : exileZone.getCards(game)) { - card.setFaceDown(true, game); - } - game.fireUpdatePlayersEvent(); // removes Jeskai Infiltrator from Battlefield, so Jeskai Infiltrator returns as a fresh permanent to the battlefield with new position - - Ability newSource = source.copy(); - newSource.setWorksFaceDown(true); - //the Set will mimic the Shuffling - exileZone.getCards(game).forEach(card -> { - ManaCosts manaCosts = null; - if (card.isCreature(game)) { - manaCosts = card.getSpellAbility() != null ? card.getSpellAbility().getManaCosts() : null; - if (manaCosts == null) { - manaCosts = new ManaCostsImpl<>("{0}"); - } - } - Card battlefieldCard = BecomesFaceDownCreatureEffect.findDefaultCardSideForFaceDown(game, card); - MageObjectReference objectReference = new MageObjectReference(battlefieldCard.getId(), battlefieldCard.getZoneChangeCounter(game) + 1, game); - game.addEffect(new BecomesFaceDownCreatureEffect(manaCosts, objectReference, Duration.Custom, FaceDownType.MANIFESTED), newSource); - }); - controller.moveCards(exileZone.getCards(game), Zone.BATTLEFIELD, source, game, false, true, false, null); - return true; + if (controller == null) { + return false; } - return false; + UUID exileId = UUID.randomUUID(); + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (sourcePermanent != null) { + controller.moveCardsToExile(sourcePermanent, source, game, false, exileId, ""); + } + Card topCard = controller.getLibrary().getFromTop(game); + if (topCard != null) { + controller.moveCardsToExile(topCard, source, game, false, exileId, ""); + } + // need to get source permanent as card rather than permanent for next steps, hence this convoluted code + List cardsToManifest = new ArrayList<>(game.getExile().getExileZone(exileId).getCards(game)); + if (cardsToManifest.isEmpty()) { + return false; + } + for (Card card : cardsToManifest) { + card.setFaceDown(true, game); + } + Collections.shuffle(cardsToManifest); + game.informPlayers(controller.getLogName() + " shuffles the face-down pile"); + game.getState().processAction(game); + ManifestEffect.doManifestCards(game, source, controller, new LinkedHashSet<>(cardsToManifest)); + return true; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java index 363e4f5b327..39bee4954fc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java @@ -151,8 +151,8 @@ public class ManifestTest extends CardTestPlayerBase { } else { String realPermanentName = currentGame.getBattlefield().getAllPermanents() .stream() - .filter(p -> p.getName().equals(cardAfterBlink)) .map(MageObject::getName) + .filter(name -> name.equals(cardAfterBlink)) .findFirst() .orElse(null); Assert.assertEquals("after blink card must go to battlefield", @@ -818,4 +818,36 @@ public class ManifestTest extends CardTestPlayerBase { permanent = findFaceDownPermanent(currentGame, playerB, playerB); assertFaceDownManifest("end game: must show for yourself", permanent, "Mountain", true); } + + @Test + public void testJeskaiInfiltrator() { + // Whenever Jeskai Infiltrator deals combat damage to a player, + // exile it and the top card of your library in a face-down pile, shuffle that pile, then manifest those cards. + String infiltrator = "Jeskai Infiltrator"; // 2/3 + String excommunicate = "Excommunicate"; // 2W Sorcery, Put target creature on top of its owner's library + String missionary = "Lone Missionary"; // 2/1 for 1W, ETB gain 4 life + + addCard(Zone.BATTLEFIELD, playerA, "Tundra", 8); + addCard(Zone.BATTLEFIELD, playerA, infiltrator); + addCard(Zone.BATTLEFIELD, playerA, missionary); + addCard(Zone.HAND, playerA, excommunicate); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, excommunicate, missionary); + + attack(1, playerA, infiltrator, playerB); + + checkPlayableAbility("missionary manifest",1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{1}{W}: Turn ", true); + checkPlayableAbility("infiltrator manifest",1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{2}{U}: Turn ", true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, excommunicate, 1); + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2); + assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); + assertLife(playerA, 20); + assertLife(playerB, 18); + } + } diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/ManifestEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/ManifestEffect.java index 6eb8a15eb36..52c9b8d86b3 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/ManifestEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/ManifestEffect.java @@ -148,7 +148,7 @@ public class ManifestEffect extends OneShotEffect { permanent.setManifested(true); } else { // TODO: looks buggy, card can't be moved to battlefield, but face down effect already active - // or it can be face down on another move to battalefield + // or it can be face down on another move to battlefield } }