From 02968c9cc12de53875d590c9a322c41f4bfa4bbe Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 5 Sep 2022 16:14:16 -0400 Subject: [PATCH] [DMU] Implemented Sphinx of Clear Skies (#9474) * create common class for effects similar to Fact or Fiction * [DMU] Implemented Sphinx of Clear Skies * change text generation to staticText --- .../mage/cards/e/EpiphanyAtTheDrownyard.java | 135 ++----------- Mage.Sets/src/mage/cards/f/FactOrFiction.java | 116 +---------- .../mage/cards/j/JaceArchitectOfThought.java | 83 +------- .../src/mage/cards/s/SphinxOfClearSkies.java | 52 +++++ .../src/mage/cards/s/SphinxOfUthuun.java | 121 +----------- Mage.Sets/src/mage/cards/s/SteamAugury.java | 137 +------------ .../cards/u/UneshCriosphinxSovereign.java | 116 +---------- Mage.Sets/src/mage/sets/DominariaUnited.java | 1 + .../common/RevealAndSeparatePilesEffect.java | 183 ++++++++++++++++++ 9 files changed, 281 insertions(+), 663 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/s/SphinxOfClearSkies.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/RevealAndSeparatePilesEffect.java diff --git a/Mage.Sets/src/mage/cards/e/EpiphanyAtTheDrownyard.java b/Mage.Sets/src/mage/cards/e/EpiphanyAtTheDrownyard.java index 33bf814258..aab16a260b 100644 --- a/Mage.Sets/src/mage/cards/e/EpiphanyAtTheDrownyard.java +++ b/Mage.Sets/src/mage/cards/e/EpiphanyAtTheDrownyard.java @@ -1,23 +1,16 @@ package mage.cards.e; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.abilities.dynamicvalue.AdditiveDynamicValue; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.RevealAndSeparatePilesEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; -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.GameLog; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; import java.util.UUID; /** @@ -25,11 +18,16 @@ import java.util.UUID; */ public final class EpiphanyAtTheDrownyard extends CardImpl { + private static final DynamicValue xValue = new AdditiveDynamicValue(ManacostVariableValue.REGULAR, StaticValue.get(1)); + public EpiphanyAtTheDrownyard(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{U}"); // Reveal the top X plus one cards of your library and separate them into two piles. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard. - this.getSpellAbility().addEffect(new EpiphanyAtTheDrownyardEffect()); + this.getSpellAbility().addEffect(new RevealAndSeparatePilesEffect( + xValue, TargetController.YOU, TargetController.OPPONENT, Zone.GRAVEYARD + ).setText("reveal the top X plus one cards of your library and separate them into two piles. " + + "An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard")); } private EpiphanyAtTheDrownyard(final EpiphanyAtTheDrownyard card) { @@ -41,108 +39,3 @@ public final class EpiphanyAtTheDrownyard extends CardImpl { return new EpiphanyAtTheDrownyard(this); } } - -class EpiphanyAtTheDrownyardEffect extends OneShotEffect { - - EpiphanyAtTheDrownyardEffect() { - super(Outcome.DrawCard); - this.staticText = "Reveal the top X plus one cards of your library and separate them into two piles. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard"; - } - - private EpiphanyAtTheDrownyardEffect(final EpiphanyAtTheDrownyardEffect effect) { - super(effect); - } - - @Override - public EpiphanyAtTheDrownyardEffect copy() { - return new EpiphanyAtTheDrownyardEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source); - if (controller == null || sourceObject == null) { - return false; - } - - Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, source.getManaCostsToPay().getX() + 1)); - controller.revealCards(sourceObject.getIdName(), cards, game); - - Player opponent; - Set opponents = game.getOpponents(controller.getId()); - if (opponents.size() == 1) { - opponent = game.getPlayer(opponents.iterator().next()); - } else { - Target target = new TargetOpponent(true); - controller.chooseTarget(Outcome.Detriment, target, source, game); - opponent = game.getPlayer(target.getFirstTarget()); - } - - if (opponent != null) { - TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile")); - List pile1 = new ArrayList<>(); - Cards pile1CardsIds = new CardsImpl(); - target.setRequired(false); - if (controller.choose(Outcome.Neutral, cards, target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - pile1.add(card); - pile1CardsIds.add(card.getId()); - } - } - } - List pile2 = new ArrayList<>(); - Cards pile2CardsIds = new CardsImpl(); - for (UUID cardId : cards) { - Card card = game.getCard(cardId); - if (card != null && !pile1.contains(card)) { - pile2.add(card); - pile2CardsIds.add(card.getId()); - } - } - boolean choice = opponent.choosePile(Outcome.Detriment, "Choose a pile to put into " + controller.getName() + "'s hand.", pile1, pile2, game); - - Zone pile1Zone = Zone.GRAVEYARD; - Zone pile2Zone = Zone.HAND; - if (choice) { - pile1Zone = Zone.HAND; - pile2Zone = Zone.GRAVEYARD; - } - - StringBuilder sb = new StringBuilder(sourceObject.getLogName() + ": Pile 1, going to ").append(pile1Zone == Zone.HAND ? "Hand" : "Graveyard").append(": "); - int i = 0; - for (UUID cardUuid : pile1CardsIds) { - i++; - Card card = game.getCard(cardUuid); - if (card != null) { - sb.append(GameLog.getColoredObjectName(card)); - if (i < pile1CardsIds.size()) { - sb.append(", "); - } - } - } - controller.moveCards(new CardsImpl(pile1CardsIds), pile1Zone, source, game); - game.informPlayers(sb.toString()); - - sb = new StringBuilder(sourceObject.getLogName() + ": Pile 2, going to ").append(pile2Zone == Zone.HAND ? "Hand" : "Graveyard").append(':'); - i = 0; - for (UUID cardUuid : pile2CardsIds) { - Card card = game.getCard(cardUuid); - if (card != null) { - i++; - sb.append(' ').append(GameLog.getColoredObjectName(card)); - if (i < pile2CardsIds.size()) { - sb.append(", "); - } - } - } - controller.moveCards(new CardsImpl(pile2CardsIds), pile2Zone, source, game); - game.informPlayers(sb.toString()); - } - - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/f/FactOrFiction.java b/Mage.Sets/src/mage/cards/f/FactOrFiction.java index 28241804d6..f5745259b4 100644 --- a/Mage.Sets/src/mage/cards/f/FactOrFiction.java +++ b/Mage.Sets/src/mage/cards/f/FactOrFiction.java @@ -1,22 +1,12 @@ package mage.cards.f; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.abilities.effects.common.RevealAndSeparatePilesEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; -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 java.util.ArrayList; -import java.util.List; -import java.util.Set; import java.util.UUID; /** @@ -28,7 +18,9 @@ public final class FactOrFiction extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}"); // Reveal the top five cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard. - this.getSpellAbility().addEffect(new FactOrFictionEffect()); + this.getSpellAbility().addEffect(new RevealAndSeparatePilesEffect( + 5, TargetController.OPPONENT, TargetController.YOU, Zone.GRAVEYARD + )); } private FactOrFiction(final FactOrFiction card) { @@ -40,97 +32,3 @@ public final class FactOrFiction extends CardImpl { return new FactOrFiction(this); } } - -class FactOrFictionEffect extends OneShotEffect { - - FactOrFictionEffect() { - super(Outcome.DrawCard); - this.staticText = "Reveal the top five cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard"; - } - - private FactOrFictionEffect(final FactOrFictionEffect effect) { - super(effect); - } - - @Override - public FactOrFictionEffect copy() { - return new FactOrFictionEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller == null || sourceObject == null) { - return false; - } - - Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 5)); - controller.revealCards(sourceObject.getName(), cards, game); - - Set opponents = game.getOpponents(source.getControllerId()); - if (!opponents.isEmpty()) { - Player opponent = game.getPlayer(opponents.iterator().next()); - if (opponents.size() > 1) { - Target targetOpponent = new TargetOpponent(true); - if (controller.chooseTarget(Outcome.Neutral, targetOpponent, source, game)) { - opponent = game.getPlayer(targetOpponent.getFirstTarget()); - game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " to separate the revealed cards"); - } - } - TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile")); - List pile1 = new ArrayList<>(); - if (opponent.choose(Outcome.Neutral, cards, target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = cards.get(targetId, game); - if (card != null) { - pile1.add(card); - cards.remove(card); - } - } - } - List pile2 = new ArrayList<>(); - pile2.addAll(cards.getCards(game)); - - boolean choice = controller.choosePile(outcome, "Choose a pile to put into your hand.", pile1, pile2, game); - - Zone pile1Zone = Zone.GRAVEYARD; - Zone pile2Zone = Zone.HAND; - if (choice) { - pile1Zone = Zone.HAND; - pile2Zone = Zone.GRAVEYARD; - } - - StringBuilder sb = new StringBuilder("Pile 1, going to ").append(pile1Zone == Zone.HAND ? "Hand" : "Graveyard").append(": "); - int i = 0; - for (Card card : pile1) { - i++; - sb.append(card.getName()); - if (i < pile1.size()) { - sb.append(", "); - } - } - cards.clear(); - cards.addAll(pile1); - controller.moveCards(cards, pile1Zone, source, game); - game.informPlayers(sb.toString()); - - sb = new StringBuilder("Pile 2, going to ").append(pile2Zone == Zone.HAND ? "Hand" : "Graveyard").append(':'); - i = 0; - for (Card card : pile2) { - i++; - sb.append(' ').append(card.getName()); - if (i < pile2.size()) { - sb.append(", "); - } - } - cards.clear(); - cards.addAll(pile2); - controller.moveCards(cards, pile2Zone, source, game); - game.informPlayers(sb.toString()); - } - - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index b14153cddb..79c61dc041 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -4,22 +4,18 @@ import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.RevealAndSeparatePilesEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.*; import mage.constants.*; -import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; -import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetOpponent; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; -import java.util.ArrayList; -import java.util.Set; import java.util.UUID; /** @@ -39,7 +35,9 @@ public final class JaceArchitectOfThought extends CardImpl { // -2: Reveal the top three cards of your library. An opponent separates those cards into two piles. // Put one pile into your hand and the other on the bottom of your library in any order. - this.addAbility(new LoyaltyAbility(new JaceArchitectOfThoughtEffect2(), -2)); + this.addAbility(new LoyaltyAbility(new RevealAndSeparatePilesEffect( + 3, TargetController.OPPONENT, TargetController.YOU, Zone.LIBRARY + ), -2)); // -8: For each player, search that player's library for a nonland card and exile it, // then that player shuffles their library. You may cast those cards without paying their mana costs. @@ -128,79 +126,6 @@ class JaceArchitectOfThoughtDelayedTriggeredAbility extends DelayedTriggeredAbil } } -class JaceArchitectOfThoughtEffect2 extends OneShotEffect { - - public JaceArchitectOfThoughtEffect2() { - super(Outcome.DrawCard); - this.staticText = "Reveal the top three cards of your library. An opponent separates those cards " - + "into two piles. Put one pile into your hand and the other on the bottom of your library in any order"; - } - - public JaceArchitectOfThoughtEffect2(final JaceArchitectOfThoughtEffect2 effect) { - super(effect); - } - - @Override - public JaceArchitectOfThoughtEffect2 copy() { - return new JaceArchitectOfThoughtEffect2(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - - Cards allCards = new CardsImpl(player.getLibrary().getTopCards(game, 3)); - player.revealCards(source, allCards, game); - Set opponents = game.getOpponents(source.getControllerId()); - if (!opponents.isEmpty()) { - Player opponent = null; - if (opponents.size() > 1) { - TargetOpponent targetOpponent = new TargetOpponent(); - if (player.chooseTarget(Outcome.Neutral, targetOpponent, source, game)) { - opponent = game.getPlayer(targetOpponent.getFirstTarget()); - } - } - if (opponent == null) { - opponent = game.getPlayer(opponents.iterator().next()); - } - TargetCard target = new TargetCard(0, allCards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile")); - target.setNotTarget(true); - opponent.choose(Outcome.Neutral, allCards, target, game); - Cards pile1 = new CardsImpl(target.getTargets()); - Cards pile2 = new CardsImpl(allCards); - pile2.removeAll(pile1); - player.revealCards(source, "Pile 1", pile1, game); - player.revealCards(source, "Pile 2", pile2, game); - - postPileToLog("Pile 1", pile1.getCards(game), game); - postPileToLog("Pile 2", pile2.getCards(game), game); - - boolean pileChoice = player.choosePile(Outcome.Neutral, "Choose a pile to to put into your hand.", - new ArrayList<>(pile1.getCards(game)), - new ArrayList<>(pile2.getCards(game)), game); - game.informPlayers(player.getLogName() + " chose pile" + (pileChoice ? "1" : "2")); - player.moveCards(pileChoice ? pile1 : pile2, Zone.HAND, source, game); - player.putCardsOnBottomOfLibrary(pileChoice ? pile2 : pile1, game, source, true); - return true; - } - return false; - } - - private void postPileToLog(String pileName, Set cards, Game game) { - StringBuilder message = new StringBuilder(pileName).append(": "); - cards.forEach((card) -> { - message.append(card.getName()).append(' '); - }); - if (cards.isEmpty()) { - message.append(" (empty)"); - } - game.informPlayers(message.toString()); - } -} - class JaceArchitectOfThoughtEffect3 extends OneShotEffect { public JaceArchitectOfThoughtEffect3() { diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfClearSkies.java b/Mage.Sets/src/mage/cards/s/SphinxOfClearSkies.java new file mode 100644 index 0000000000..644eac8403 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SphinxOfClearSkies.java @@ -0,0 +1,52 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.DomainValue; +import mage.abilities.effects.common.RevealAndSeparatePilesEffect; +import mage.abilities.hint.common.DomainHint; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SphinxOfClearSkies extends CardImpl { + + public SphinxOfClearSkies(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.SPHINX); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"), false)); + + // Domain -- Whenever Sphinx of Clear Skies deals combat damage to a player, reveal the top X cards of your library, where X is the number of basic land types among lands you control. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new RevealAndSeparatePilesEffect( + DomainValue.REGULAR, TargetController.OPPONENT, TargetController.YOU, Zone.GRAVEYARD + ).setText("reveal the top X cards of your library, where X is the number of basic land types " + + "among lands you control. An opponent separates those cards into two piles. " + + "Put one pile into your hand and the other into your graveyard"), false + ).setAbilityWord(AbilityWord.DOMAIN).addHint(DomainHint.instance)); + } + + private SphinxOfClearSkies(final SphinxOfClearSkies card) { + super(card); + } + + @Override + public SphinxOfClearSkies copy() { + return new SphinxOfClearSkies(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfUthuun.java b/Mage.Sets/src/mage/cards/s/SphinxOfUthuun.java index defe183fde..2f7bc05988 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxOfUthuun.java +++ b/Mage.Sets/src/mage/cards/s/SphinxOfUthuun.java @@ -1,34 +1,19 @@ - package mage.cards.s; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.RevealAndSeparatePilesEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; +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 java.util.UUID; /** - * * @author North */ public final class SphinxOfUthuun extends CardImpl { @@ -41,7 +26,9 @@ public final class SphinxOfUthuun extends CardImpl { this.toughness = new MageInt(6); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new EntersBattlefieldTriggeredAbility(new SphinxOfUthuunEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new RevealAndSeparatePilesEffect( + 5, TargetController.OPPONENT, TargetController.YOU, Zone.GRAVEYARD + ))); } private SphinxOfUthuun(final SphinxOfUthuun card) { @@ -53,97 +40,3 @@ public final class SphinxOfUthuun extends CardImpl { return new SphinxOfUthuun(this); } } - -class SphinxOfUthuunEffect extends OneShotEffect { - - public SphinxOfUthuunEffect() { - super(Outcome.DrawCard); - this.staticText = "reveal the top five cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard"; - } - - public SphinxOfUthuunEffect(final SphinxOfUthuunEffect effect) { - super(effect); - } - - @Override - public SphinxOfUthuunEffect copy() { - return new SphinxOfUthuunEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller == null || sourceObject == null) { - return false; - } - Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 5)); - controller.revealCards(source, cards, game); - - Set opponents = game.getOpponents(source.getControllerId()); - if (!opponents.isEmpty()) { - Player opponent = game.getPlayer(opponents.iterator().next()); - if (opponents.size() > 1) { - Target targetOpponent = new TargetOpponent(true); - if (controller.chooseTarget(Outcome.Neutral, targetOpponent, source, game)) { - opponent = game.getPlayer(targetOpponent.getFirstTarget()); - game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " to separate the revealed cards"); - } - } - - TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile")); - target.setRequired(false); - List pile1 = new ArrayList<>(); - Cards pile1CardsIds = new CardsImpl(); - if (opponent.choose(Outcome.Neutral, cards, target, game)) { - pile1CardsIds.addAll(target.getTargets()); - pile1.addAll(pile1CardsIds.getCards(game)); - } - List pile2 = new ArrayList<>(); - Cards pile2CardsIds = new CardsImpl(cards); - pile2CardsIds.removeAll(pile1CardsIds); - pile2.addAll(pile2CardsIds.getCards(game)); - - boolean choice = controller.choosePile(Outcome.DestroyPermanent, "Choose a pile to put into hand.", pile1, pile2, game); - - Zone pile1Zone = Zone.GRAVEYARD; - Zone pile2Zone = Zone.HAND; - if (choice) { - pile1Zone = Zone.HAND; - pile2Zone = Zone.GRAVEYARD; - } - - StringBuilder sb = new StringBuilder(sourceObject.getLogName()).append(": Pile 1, going to ").append(pile1Zone == Zone.HAND ? "Hand" : "Graveyard").append(": "); - int i = 0; - for (UUID cardUuid : pile1CardsIds) { - i++; - Card card = game.getCard(cardUuid); - if (card != null) { - sb.append(card.getName()); - if (i < pile1CardsIds.size()) { - sb.append(", "); - } - } - } - controller.moveCards(pile1CardsIds, pile1Zone, source, game); - game.informPlayers(sb.toString()); - - sb = new StringBuilder(sourceObject.getLogName()).append(": Pile 2, going to ").append(pile2Zone == Zone.HAND ? "Hand" : "Graveyard").append(':'); - i = 0; - for (UUID cardUuid : pile2CardsIds) { - Card card = game.getCard(cardUuid); - if (card != null) { - i++; - sb.append(' ').append(card.getName()); - if (i < pile2CardsIds.size()) { - sb.append(", "); - } - } - } - controller.moveCards(pile2CardsIds, pile2Zone, source, game); - game.informPlayers(sb.toString()); - } - - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SteamAugury.java b/Mage.Sets/src/mage/cards/s/SteamAugury.java index 0c7792c7ed..650f7002e7 100644 --- a/Mage.Sets/src/mage/cards/s/SteamAugury.java +++ b/Mage.Sets/src/mage/cards/s/SteamAugury.java @@ -1,31 +1,15 @@ package mage.cards.s; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.RevealAndSeparatePilesEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; -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.GameLog; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class SteamAugury extends CardImpl { @@ -34,7 +18,9 @@ public final class SteamAugury extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{R}"); // Reveal the top five cards of your library and separate them into two piles. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard. - this.getSpellAbility().addEffect(new SteamAuguryEffect()); + this.getSpellAbility().addEffect(new RevealAndSeparatePilesEffect( + 5, TargetController.YOU, TargetController.OPPONENT, Zone.GRAVEYARD + )); } private SteamAugury(final SteamAugury card) { @@ -46,112 +32,3 @@ public final class SteamAugury extends CardImpl { return new SteamAugury(this); } } - -class SteamAuguryEffect extends OneShotEffect { - - public SteamAuguryEffect() { - super(Outcome.DrawCard); - this.staticText = "Reveal the top five cards of your library and separate them into two piles. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard"; - } - - public SteamAuguryEffect(final SteamAuguryEffect effect) { - super(effect); - } - - @Override - public SteamAuguryEffect copy() { - return new SteamAuguryEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source); - Set cardsToGraveyard = new LinkedHashSet<>(); - Set cardsToHand = new LinkedHashSet<>(); - if (controller == null || sourceObject == null) { - return false; - } - - Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 5)); - controller.revealCards(sourceObject.getIdName(), cards, game); - - Player opponent; - Set opponents = game.getOpponents(controller.getId()); - if (opponents.size() == 1) { - opponent = game.getPlayer(opponents.iterator().next()); - } else { - Target target = new TargetOpponent(true); - controller.chooseTarget(Outcome.Detriment, target, source, game); - opponent = game.getPlayer(target.getFirstTarget()); - } - - if (opponent != null) { - TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile")); - List pile1 = new ArrayList<>(); - Cards pile1CardsIds = new CardsImpl(); - target.setRequired(false); - if (controller.choose(Outcome.Neutral, cards, target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - pile1.add(card); - pile1CardsIds.add(card.getId()); - } - } - } - List pile2 = new ArrayList<>(); - Cards pile2CardsIds = new CardsImpl(); - for (UUID cardId : cards) { - Card card = game.getCard(cardId); - if (card != null && !pile1.contains(card)) { - pile2.add(card); - pile2CardsIds.add(card.getId()); - } - } - boolean choice = opponent.choosePile(Outcome.Detriment, "Choose a pile to put into " + controller.getName() + "'s hand.", pile1, pile2, game); - - Zone pile1Zone = Zone.GRAVEYARD; - Zone pile2Zone = Zone.HAND; - if (choice) { - pile1Zone = Zone.HAND; - pile2Zone = Zone.GRAVEYARD; - } - - StringBuilder sb = new StringBuilder(sourceObject.getLogName() + ": Pile 1, going to ").append(pile1Zone == Zone.HAND ? "Hand" : "Graveyard").append(": "); - int i = 0; - for (UUID cardUuid : pile1CardsIds) { - i++; - Card card = game.getCard(cardUuid); - if (card != null) { - sb.append(GameLog.getColoredObjectName(card)); - if (i < pile1CardsIds.size()) { - sb.append(", "); - } - cardsToGraveyard.add(card); - } - } - controller.moveCards(cardsToGraveyard, pile1Zone, source, game); - game.informPlayers(sb.toString()); - - sb = new StringBuilder(sourceObject.getLogName() + ": Pile 2, going to ").append(pile2Zone == Zone.HAND ? "Hand" : "Graveyard").append(':'); - i = 0; - for (UUID cardUuid : pile2CardsIds) { - Card card = game.getCard(cardUuid); - if (card != null) { - i++; - sb.append(' ').append(GameLog.getColoredObjectName(card)); - if (i < pile2CardsIds.size()) { - sb.append(", "); - } - cardsToHand.add(card); - } - } - controller.moveCards(cardsToHand, pile2Zone, source, game); - game.informPlayers(sb.toString()); - } - - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java b/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java index 50f04bb915..12b9dcf5a9 100644 --- a/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java +++ b/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java @@ -1,27 +1,17 @@ package mage.cards.u; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.RevealAndSeparatePilesEffect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.players.Player; -import mage.target.Target; -import mage.target.TargetCard; -import mage.target.common.TargetOpponent; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; import java.util.UUID; /** @@ -51,9 +41,9 @@ public final class UneshCriosphinxSovereign extends CardImpl { this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 2))); // Whenever Unesh, Criosphinx Sovereign or another Sphinx enters the battlefield under your control, reveal the top four cards of your library. An opponent seperates those cards into two piles. Put one pile into your hand and the other into your graveyard. - this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( - new UneshCriosphinxSovereignEffect(), filter2, false, true - )); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(new RevealAndSeparatePilesEffect( + 4, TargetController.OPPONENT, TargetController.YOU, Zone.GRAVEYARD + ), filter2, false, true)); } private UneshCriosphinxSovereign(final UneshCriosphinxSovereign card) { @@ -65,97 +55,3 @@ public final class UneshCriosphinxSovereign extends CardImpl { return new UneshCriosphinxSovereign(this); } } - -class UneshCriosphinxSovereignEffect extends OneShotEffect { - - UneshCriosphinxSovereignEffect() { - super(Outcome.DrawCard); - this.staticText = "reveal the top four cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard"; - } - - private UneshCriosphinxSovereignEffect(final UneshCriosphinxSovereignEffect effect) { - super(effect); - } - - @Override - public UneshCriosphinxSovereignEffect copy() { - return new UneshCriosphinxSovereignEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Set cardsToGraveyard = new LinkedHashSet<>(); - Set cardsToHand = new LinkedHashSet<>(); - MageObject sourceObject = source.getSourceObject(game); - if (controller == null || sourceObject == null) { - return false; - } - - Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 4)); - controller.revealCards(sourceObject.getName(), cards, game); - - Set opponents = game.getOpponents(source.getControllerId()); - if (!opponents.isEmpty()) { - Player opponent = game.getPlayer(opponents.iterator().next()); - if (opponents.size() > 1) { - Target targetOpponent = new TargetOpponent(true); - if (controller.chooseTarget(Outcome.Neutral, targetOpponent, source, game)) { - opponent = game.getPlayer(targetOpponent.getFirstTarget()); - game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " to separate the revealed cards"); - } - } - TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile")); - List pile1 = new ArrayList<>(); - if (opponent.choose(Outcome.Neutral, cards, target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = cards.get(targetId, game); - if (card != null) { - pile1.add(card); - cards.remove(card); - } - } - } - List pile2 = new ArrayList<>(); - pile2.addAll(cards.getCards(game)); - - boolean choice = controller.choosePile(outcome, "Choose a pile to put into your hand.", pile1, pile2, game); - - Zone pile1Zone = Zone.GRAVEYARD; - Zone pile2Zone = Zone.HAND; - if (choice) { - pile1Zone = Zone.HAND; - pile2Zone = Zone.GRAVEYARD; - } - - StringBuilder sb = new StringBuilder("Pile 1, going to ").append(pile1Zone == Zone.HAND ? "Hand" : "Graveyard").append(": "); - int i = 0; - for (Card card : pile1) { - i++; - sb.append(card.getName()); - if (i < pile1.size()) { - sb.append(", "); - } - cardsToGraveyard.add(card); - } - controller.moveCards(cardsToGraveyard, pile1Zone, source, game); - game.informPlayers(sb.toString()); - - sb = new StringBuilder("Pile 2, going to ").append(pile2Zone == Zone.HAND ? "Hand" : "Graveyard").append(':'); - i = 0; - for (Card card : pile2) { - i++; - sb.append(' ').append(card.getName()); - if (i < pile2.size()) { - sb.append(", "); - } - cardsToHand.add(card); - } - controller.moveCards(cardsToHand, pile2Zone, source, game); - game.informPlayers(sb.toString()); - } - - return true; - } -} diff --git a/Mage.Sets/src/mage/sets/DominariaUnited.java b/Mage.Sets/src/mage/sets/DominariaUnited.java index 7ec8112e27..6decabe21c 100644 --- a/Mage.Sets/src/mage/sets/DominariaUnited.java +++ b/Mage.Sets/src/mage/sets/DominariaUnited.java @@ -212,6 +212,7 @@ public final class DominariaUnited extends ExpansionSet { cards.add(new SetCardInfo("Snarespinner", 179, Rarity.COMMON, mage.cards.s.Snarespinner.class)); cards.add(new SetCardInfo("Soaring Drake", 66, Rarity.COMMON, mage.cards.s.SoaringDrake.class)); cards.add(new SetCardInfo("Soul of Windgrace", 220, Rarity.MYTHIC, mage.cards.s.SoulOfWindgrace.class)); + cards.add(new SetCardInfo("Sphinx of Clear Skies", 67, Rarity.MYTHIC, mage.cards.s.SphinxOfClearSkies.class)); cards.add(new SetCardInfo("Splatter Goblin", 109, Rarity.COMMON, mage.cards.s.SplatterGoblin.class)); cards.add(new SetCardInfo("Sprouting Goblin", 145, Rarity.UNCOMMON, mage.cards.s.SproutingGoblin.class)); cards.add(new SetCardInfo("Stall for Time", 34, Rarity.COMMON, mage.cards.s.StallForTime.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/RevealAndSeparatePilesEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RevealAndSeparatePilesEffect.java new file mode 100644 index 0000000000..f3cce3d00e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/RevealAndSeparatePilesEffect.java @@ -0,0 +1,183 @@ +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()); + game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " 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, 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 " + targetZone + ".", pile1, pile2, game); + + Zone pile1Zone = choice ? targetZone : Zone.HAND; + Zone pile2Zone = choice ? Zone.HAND : targetZone; + + game.informPlayers("Pile 1, going to " + pile1Zone + ": " + (pile1.isEmpty() ? " (none)" : pile1.stream().map(MageObject::getName).collect(Collectors.joining(", ")))); + cards.clear(); + cards.addAll(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.addAll(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(); + } +}