package mage.abilities.effects.common; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; import mage.target.Target; import mage.target.TargetCard; import mage.target.common.TargetOpponent; import mage.util.CardUtil; import java.util.*; import java.util.stream.Collectors; /** * @author TheElk801 */ public class RevealAndSeparatePilesEffect extends OneShotEffect { private static final FilterCard filter = new FilterCard("cards to put in the first pile"); private final DynamicValue amount; private final TargetController playerWhoSeparates; private final TargetController playerWhoChooses; private final Zone targetZone; private final boolean anyOrder; public RevealAndSeparatePilesEffect(int amount, TargetController playerWhoSeparates, TargetController playerWhoChooses, Zone targetZone) { this(StaticValue.get(amount), playerWhoSeparates, playerWhoChooses, targetZone); } public RevealAndSeparatePilesEffect(DynamicValue amount, TargetController playerWhoSeparates, TargetController playerWhoChooses, Zone targetZone) { this(amount, playerWhoSeparates, playerWhoChooses, targetZone, true); } public RevealAndSeparatePilesEffect(DynamicValue amount, TargetController playerWhoSeparates, TargetController playerWhoChooses, Zone targetZone, boolean anyOrder) { super(Outcome.DrawCard); this.amount = amount; this.playerWhoSeparates = playerWhoSeparates; this.playerWhoChooses = playerWhoChooses; this.targetZone = targetZone; this.anyOrder = anyOrder; this.staticText = this.generateText(); } private RevealAndSeparatePilesEffect(final RevealAndSeparatePilesEffect effect) { super(effect); this.amount = effect.amount; this.playerWhoSeparates = effect.playerWhoSeparates; this.playerWhoChooses = effect.playerWhoChooses; this.targetZone = effect.targetZone; this.anyOrder = effect.anyOrder; } @Override public RevealAndSeparatePilesEffect copy() { return new RevealAndSeparatePilesEffect(this); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); int toReveal = amount.calculate(game, source, this); if (controller == null || toReveal < 1) { return false; } return doPiles(controller, source, game, toReveal); } private static Player getExecutingPlayer(Player controller, Game game, Ability source, TargetController targetController, String message) { switch (targetController) { case YOU: return controller; case OPPONENT: Player opponent; Set opponents = game.getOpponents(source.getControllerId()); if (opponents.isEmpty()) { return null; } if (opponents.size() == 1) { opponent = game.getPlayer(opponents.iterator().next()); } else { Target targetOpponent = new TargetOpponent(true); controller.chooseTarget(Outcome.Neutral, targetOpponent, source, game); opponent = game.getPlayer(targetOpponent.getFirstTarget()); if (opponent != null) { game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " to " + message); } else { game.informPlayers(controller.getLogName() + " chose nothing" + " to " + message); } } return opponent; } return null; } private boolean doPiles(Player controller, Ability source, Game game, int toReveal) { Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, toReveal)); controller.revealCards(source, cards, game); Player separatingPlayer = this.getExecutingPlayer(controller, game, source, playerWhoSeparates, "separate the revealed cards"); TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, filter); List pile1 = new ArrayList<>(); separatingPlayer.choose(Outcome.Neutral, cards, target, source, game); target.getTargets() .stream() .map(game::getCard) .filter(Objects::nonNull) .forEach(pile1::add); cards.removeIf(target.getTargets()::contains); List pile2 = new ArrayList<>(); pile2.addAll(cards.getCards(game)); Player choosingPlayer = this.getExecutingPlayer(controller, game, source, playerWhoChooses, "choose the piles"); boolean choice = choosingPlayer.choosePile(outcome, "Choose a pile to put into hand.", pile1, pile2, game); Zone pile1Zone = choice ? Zone.HAND : targetZone; Zone pile2Zone = choice ? targetZone : Zone.HAND; game.informPlayers("Pile 1, going to " + pile1Zone + ": " + (pile1.isEmpty() ? " (none)" : pile1.stream().map(MageObject::getName).collect(Collectors.joining(", ")))); cards.clear(); cards.addAllCards(pile1); if (pile1Zone == Zone.LIBRARY) { controller.putCardsOnBottomOfLibrary(cards, game, source, anyOrder); } else { controller.moveCards(cards, pile1Zone, source, game); } game.informPlayers("Pile 2, going to " + pile2Zone + ": " + (pile2.isEmpty() ? " (none)" : pile2.stream().map(MageObject::getName).collect(Collectors.joining(", ")))); cards.clear(); cards.addAllCards(pile2); if (pile2Zone == Zone.LIBRARY) { controller.putCardsOnBottomOfLibrary(cards, game, source, anyOrder); } else { controller.moveCards(cards, pile2Zone, source, game); } return true; } private String generateText() { StringBuilder sb = new StringBuilder("reveal the top "); if (amount instanceof StaticValue) { sb.append(CardUtil.numberToText(((StaticValue) amount).getValue())); sb.append(" cards of your library"); } else { sb.append("X cards of your library, where X is the number of "); sb.append(amount.getMessage()); } switch (playerWhoSeparates) { case YOU: sb.append(" and separate them"); break; case OPPONENT: sb.append(". An opponent separates those cards"); break; } sb.append(" into two piles. "); switch (playerWhoChooses) { case YOU: sb.append("Put one pile into your hand and the other "); break; case OPPONENT: sb.append("An opponent chooses one of those piles. Put that pile into your hand and the other "); break; } switch (targetZone) { case GRAVEYARD: sb.append("into your graveyard"); break; case LIBRARY: sb.append("on the bottom of your library in a"); sb.append(anyOrder ? "ny" : " random"); sb.append(" order"); break; } return sb.toString(); } }