diff --git a/Mage.Sets/src/mage/cards/t/TheTemporalAnchor.java b/Mage.Sets/src/mage/cards/t/TheTemporalAnchor.java new file mode 100644 index 0000000000..e24fbad8d3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheTemporalAnchor.java @@ -0,0 +1,166 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class TheTemporalAnchor extends CardImpl { + + public TheTemporalAnchor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}{U}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + + // At the beginning of your upkeep, scry 2. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new ScryEffect(2), TargetController.YOU, false + )); + + // Whenever you choose to put one or more cards on the bottom of your library while scrying, exile that many cards from the bottom of your library. + this.addAbility(new TheTemporalAnchorTriggeredAbility()); + + // During your turn, you may play cards exiled with The Temporal Anchor. + this.addAbility(new SimpleStaticAbility(new TheTemporalAnchorPlayEffect())); + } + + private TheTemporalAnchor(final TheTemporalAnchor card) { + super(card); + } + + @Override + public TheTemporalAnchor copy() { + return new TheTemporalAnchor(this); + } +} + +class TheTemporalAnchorTriggeredAbility extends TriggeredAbilityImpl { + + TheTemporalAnchorTriggeredAbility() { + super(Zone.BATTLEFIELD, new TheTemporalAnchorExileEffect()); + } + + private TheTemporalAnchorTriggeredAbility(final TheTemporalAnchorTriggeredAbility ability) { + super(ability); + } + + @Override + public TheTemporalAnchorTriggeredAbility copy() { + return new TheTemporalAnchorTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SCRY_TO_BOTTOM; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (isControlledBy(event.getPlayerId()) && event.getAmount() > 0) { + this.getEffects().setValue("amount", event.getAmount()); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever you choose to put one or more cards on the bottom of your library while scrying, " + + "exile that many cards from the bottom of your library."; + } +} + +class TheTemporalAnchorExileEffect extends OneShotEffect { + + TheTemporalAnchorExileEffect() { + super(Outcome.Benefit); + } + + private TheTemporalAnchorExileEffect(final TheTemporalAnchorExileEffect effect) { + super(effect); + } + + @Override + public TheTemporalAnchorExileEffect copy() { + return new TheTemporalAnchorExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + int amount = (Integer) getValue("amount"); + if (player == null || amount < 1) { + return false; + } + int toSkip = Math.max(player.getLibrary().size() - amount, 0); + Set cards = player + .getLibrary() + .getCards(game) + .stream() + .skip(toSkip) + .collect(Collectors.toSet()); + return !cards.isEmpty() && player.moveCardsToExile( + cards, source, game, true, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + } +} + +class TheTemporalAnchorPlayEffect extends AsThoughEffectImpl { + + TheTemporalAnchorPlayEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "during your turn, you may play cards exiled with {this}"; + } + + private TheTemporalAnchorPlayEffect(final TheTemporalAnchorPlayEffect effect) { + super(effect); + } + + @Override + public TheTemporalAnchorPlayEffect copy() { + return new TheTemporalAnchorPlayEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (!source.isControlledBy(affectedControllerId) || !game.isActivePlayer(affectedControllerId)) { + return false; + } + Card card = game.getCard(objectId); + if (card == null) { + return false; + } + UUID mainId = card.getMainCard().getId(); // for split cards + + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return exileZone != null + && exileZone.contains(mainId) + && game.getCard(mainId) != null; + } +} diff --git a/Mage.Sets/src/mage/sets/TheBrothersWar.java b/Mage.Sets/src/mage/sets/TheBrothersWar.java index 276aa8b061..1b12e896bc 100644 --- a/Mage.Sets/src/mage/sets/TheBrothersWar.java +++ b/Mage.Sets/src/mage/sets/TheBrothersWar.java @@ -249,6 +249,7 @@ public final class TheBrothersWar extends ExpansionSet { cards.add(new SetCardInfo("Terror Ballista", 290, Rarity.RARE, mage.cards.t.TerrorBallista.class)); cards.add(new SetCardInfo("The Mightstone and Weakstone", "238a", Rarity.RARE, mage.cards.t.TheMightstoneAndWeakstone.class)); cards.add(new SetCardInfo("The Stasis Coffin", 245, Rarity.RARE, mage.cards.t.TheStasisCoffin.class)); + cards.add(new SetCardInfo("The Temporal Anchor", 82, Rarity.RARE, mage.cards.t.TheTemporalAnchor.class)); cards.add(new SetCardInfo("Third Path Iconoclast", 223, Rarity.UNCOMMON, mage.cards.t.ThirdPathIconoclast.class)); cards.add(new SetCardInfo("Third Path Savant", 67, Rarity.COMMON, mage.cards.t.ThirdPathSavant.class)); cards.add(new SetCardInfo("Thopter Architect", 29, Rarity.UNCOMMON, mage.cards.t.ThopterArchitect.class)); diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index c1352ef2f2..6dab52fb77 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -305,7 +305,7 @@ public class GameEvent implements Serializable { SHUFFLE_LIBRARY, LIBRARY_SHUFFLED, ENCHANT_PLAYER, ENCHANTED_PLAYER, CAN_TAKE_MULLIGAN, - SCRY, SCRIED, + SCRY, SCRIED, SCRY_TO_BOTTOM, SURVEIL, SURVEILED, FATESEALED, FLIP_COIN, COIN_FLIPPED, diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 2587d1cd24..ceb1b252e0 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -4989,6 +4989,12 @@ public abstract class PlayerImpl implements Player, Serializable { + " to PUT on the BOTTOM of your library (Scry)")); chooseTarget(Outcome.Benefit, cards, target, source, game); putCardsOnBottomOfLibrary(new CardsImpl(target.getTargets()), game, source, true); + if (!target.getTargets().isEmpty()) { + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.SCRY_TO_BOTTOM, getId(), + source, getId(), target.getTargets().size() + )); + } cards.removeIf(target.getTargets()::contains); putCardsOnTopOfLibrary(cards, game, source, true); }