From 264eb586441fa6377e4ff0faf9e0a49763807c34 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Tue, 6 May 2025 17:49:43 -0400 Subject: [PATCH] Rework cards which exile cards and add suspend to them (Ready for review) (#13600) * rework effects which exile cards and give suspend * fix test failure * remove extra zone change * [WHO] Implement The Wedding of River Song * [WHO] Implement The Eleventh Doctor * [WHO] Implement The Parting of the Ways --- Mage.Sets/src/mage/cards/d/Delay.java | 36 ++----- .../mage/cards/g/GandalfOfTheSecretFire.java | 12 +-- .../src/mage/cards/j/JhoiraOfTheGhitu.java | 62 ++++-------- .../src/mage/cards/s/SibyllineSoothsayer.java | 38 ++----- .../src/mage/cards/s/SinisterConcierge.java | 56 +++-------- Mage.Sets/src/mage/cards/s/Suspend.java | 25 +---- .../mage/cards/t/TaigamMasterOpportunist.java | 35 ++----- .../src/mage/cards/t/TheEleventhDoctor.java | 99 +++++++++++++++++++ .../src/mage/cards/t/ThePartingOfTheWays.java | 98 ++++++++++++++++++ .../src/mage/cards/t/TheTenthDoctor.java | 25 ++--- .../mage/cards/t/TheWeddingOfRiverSong.java | 92 +++++++++++++++++ Mage.Sets/src/mage/sets/DoctorWho.java | 26 ++--- .../single/ncc/SinisterConciergeTest.java | 2 +- .../ExileSourceWithTimeCountersCost.java | 88 ----------------- .../ExileSpellWithTimeCountersEffect.java | 3 +- .../abilities/keyword/SuspendAbility.java | 32 +++++- 16 files changed, 406 insertions(+), 323 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/t/TheEleventhDoctor.java create mode 100644 Mage.Sets/src/mage/cards/t/ThePartingOfTheWays.java create mode 100644 Mage.Sets/src/mage/cards/t/TheWeddingOfRiverSong.java delete mode 100644 Mage/src/main/java/mage/abilities/costs/common/ExileSourceWithTimeCountersCost.java diff --git a/Mage.Sets/src/mage/cards/d/Delay.java b/Mage.Sets/src/mage/cards/d/Delay.java index ee21e510be2..f00b81d9b47 100644 --- a/Mage.Sets/src/mage/cards/d/Delay.java +++ b/Mage.Sets/src/mage/cards/d/Delay.java @@ -1,28 +1,21 @@ package mage.cards.d; -import java.util.UUID; -import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CounterTargetWithReplacementEffect; -import mage.abilities.effects.common.continuous.GainSuspendEffect; import mage.abilities.keyword.SuspendAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.PutCards; -import mage.constants.Zone; -import mage.counters.CounterType; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Delay extends CardImpl { @@ -49,7 +42,8 @@ class DelayEffect extends OneShotEffect { DelayEffect() { super(Outcome.Benefit); - this.staticText = "Counter target spell. If the spell is countered this way, exile it with three time counters on it instead of putting it into its owner's graveyard. If it doesn't have suspend, it gains suspend"; + this.staticText = "counter target spell. If the spell is countered this way, exile it with three time counters " + + "on it instead of putting it into its owner's graveyard. If it doesn't have suspend, it gains suspend"; } private DelayEffect(final DelayEffect effect) { @@ -65,23 +59,9 @@ class DelayEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); - if (controller != null && spell != null) { - Effect effect = new CounterTargetWithReplacementEffect(PutCards.EXILED); - effect.setTargetPointer(this.getTargetPointer().copy()); - Card card = spell.getMainCard(); - if (card != null && effect.apply(game, source) && game.getState().getZone(card.getId()) == Zone.EXILED) { - boolean hasSuspend = card.getAbilities(game).containsClass(SuspendAbility.class); - UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); - if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source, game, Zone.HAND, true)) { - card.addCounters(CounterType.TIME.createInstance(3), source.getControllerId(), source, game); - if (!hasSuspend) { - game.addEffect(new GainSuspendEffect(new MageObjectReference(card, game)), source); - } - game.informPlayers(controller.getLogName() + " suspends 3 - " + card.getName()); - } - } - return true; - } - return false; + return controller != null + && spell != null + && game.getStack().counter(spell.getId(), source, game, PutCards.EXILED) + && SuspendAbility.addTimeCountersAndSuspend(spell.getMainCard(), 3, source, game); } } diff --git a/Mage.Sets/src/mage/cards/g/GandalfOfTheSecretFire.java b/Mage.Sets/src/mage/cards/g/GandalfOfTheSecretFire.java index b7b93aa887f..48b78922b53 100644 --- a/Mage.Sets/src/mage/cards/g/GandalfOfTheSecretFire.java +++ b/Mage.Sets/src/mage/cards/g/GandalfOfTheSecretFire.java @@ -5,12 +5,10 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.continuous.GainSuspendEffect; import mage.abilities.keyword.SuspendAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -113,14 +111,8 @@ class GandalfOfTheSecretFireEffect extends ReplacementEffectImpl { if (controller == null || sourceSpell == null || sourceSpell.isCopy()) { return false; } - UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); - if (controller.moveCardsToExile(sourceSpell, source, game, true, exileId, "Suspended cards of " + controller.getName())) { - sourceSpell.addCounters(CounterType.TIME.createInstance(3), controller.getId(), source, game); - game.informPlayers(controller.getLogName() + " exiles " + sourceSpell.getLogName() + " with 3 time counters on it"); - } - if (!sourceSpell.getAbilities(game).containsClass(SuspendAbility.class)) { - game.addEffect(new GainSuspendEffect(new MageObjectReference(sourceSpell.getMainCard(), game)), source); - } + controller.moveCards(sourceSpell, Zone.EXILED, source, game); + SuspendAbility.addTimeCountersAndSuspend(sourceSpell.getMainCard(), 3, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java b/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java index 5f81e40efde..44716f7ae23 100644 --- a/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java +++ b/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java @@ -1,27 +1,25 @@ package mage.cards.j; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; import mage.abilities.costs.common.ExileFromHandCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.GainSuspendEffect; import mage.abilities.keyword.SuspendAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.counters.CounterType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.common.FilterNonlandCard; import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; + +import java.util.Collection; +import java.util.UUID; /** * @author LevelX2 @@ -41,7 +39,6 @@ public final class JhoiraOfTheGhitu extends CardImpl { Ability ability = new SimpleActivatedAbility(new JhoiraOfTheGhituSuspendEffect(), new GenericManaCost(2)); ability.addCost(new ExileFromHandCost(new TargetCardInHand(new FilterNonlandCard("a nonland card from your hand")))); this.addAbility(ability); - } private JhoiraOfTheGhitu(final JhoiraOfTheGhitu card) { @@ -58,7 +55,9 @@ class JhoiraOfTheGhituSuspendEffect extends OneShotEffect { JhoiraOfTheGhituSuspendEffect() { super(Outcome.PutCardInPlay); - this.staticText = "Put four time counters on the exiled card. If it doesn't have suspend, it gains suspend. (At the beginning of your upkeep, remove a time counter from that card. When the last is removed, cast it without paying its mana cost. If it's a creature, it has haste.)"; + this.staticText = "Put four time counters on the exiled card. If it doesn't have suspend, " + + "it gains suspend. (At the beginning of your upkeep, remove a time counter from that card. " + + "When the last is removed, cast it without paying its mana cost. If it's a creature, it has haste.)"; } private JhoiraOfTheGhituSuspendEffect(final JhoiraOfTheGhituSuspendEffect effect) { @@ -72,36 +71,13 @@ class JhoiraOfTheGhituSuspendEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - List cards = new ArrayList<>(); - for (Cost cost : source.getCosts()) { - if (cost instanceof ExileFromHandCost) { - cards = ((ExileFromHandCost) cost).getCards(); - } - } - if (cards != null && !cards.isEmpty()) { - // always one card to exile - Card card = game.getCard(cards.get(0).getId()); - if (card == null) { - return false; - } - card = card.getMainCard(); - - boolean hasSuspend = card.getAbilities(game).containsClass(SuspendAbility.class); - - UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); - if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source, game, Zone.HAND, true)) { - card.addCounters(CounterType.TIME.createInstance(4), source.getControllerId(), source, game); - if (!hasSuspend) { - game.addEffect(new GainSuspendEffect(new MageObjectReference(card, game)), source); - } - game.informPlayers(controller.getLogName() + " suspends 4 - " + card.getName()); - return true; - } - } - return false; + return SuspendAbility.addTimeCountersAndSuspend( + CardUtil.castStream(source.getCosts(), ExileFromHandCost.class) + .map(ExileFromHandCost::getCards) + .flatMap(Collection::stream) + .findFirst() + .orElse(null), + 4, source, game + ); } } diff --git a/Mage.Sets/src/mage/cards/s/SibyllineSoothsayer.java b/Mage.Sets/src/mage/cards/s/SibyllineSoothsayer.java index 64b31875f49..cad2c8ada40 100644 --- a/Mage.Sets/src/mage/cards/s/SibyllineSoothsayer.java +++ b/Mage.Sets/src/mage/cards/s/SibyllineSoothsayer.java @@ -1,20 +1,16 @@ package mage.cards.s; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.GainSuspendEffect; import mage.abilities.keyword.SuspendAbility; import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.counters.CounterType; import mage.game.Game; -import mage.players.Library; import mage.players.Player; import java.util.UUID; @@ -49,8 +45,10 @@ class SibyllineSoothsayerEffect extends OneShotEffect { SibyllineSoothsayerEffect() { super(Outcome.Benefit); - staticText = "reveal cards from the top of your library until you reveal a nonland card with mana value 3 or greater. Exile that card with three time " + - "counters on it. If it doesn't have suspend, it gains suspend. Put the rest of the revealed cards on the bottom of your library in a random order."; + staticText = "reveal cards from the top of your library until you reveal a nonland card " + + "with mana value 3 or greater. Exile that card with three time counters on it. " + + "If it doesn't have suspend, it gains suspend. Put the rest of the revealed cards " + + "on the bottom of your library in a random order."; } private SibyllineSoothsayerEffect(final SibyllineSoothsayerEffect effect) { @@ -65,36 +63,20 @@ class SibyllineSoothsayerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player == null) { + if (player == null || !player.getLibrary().hasCards()) { return false; } - Library library = player.getLibrary(); - if (!library.hasCards()) { - return true; - } Cards cards = new CardsImpl(); - Card toSuspend = null; - for (Card card : library.getCards(game)) { + for (Card card : player.getLibrary().getCards(game)) { cards.add(card); if (!card.isLand(game) && card.getManaValue() >= 3) { - toSuspend = card; + player.revealCards(source, cards, game); + player.moveCards(card, Zone.EXILED, source, game); + SuspendAbility.addTimeCountersAndSuspend(card, 3, source, game); break; } } - - player.revealCards(source, cards, game); - if (toSuspend != null) { - boolean hasSuspend = toSuspend.getAbilities(game).containsClass(SuspendAbility.class); - UUID exileId = SuspendAbility.getSuspendExileId(player.getId(), game); - if (player.moveCardToExileWithInfo(toSuspend, exileId, "Suspended cards of " + player.getName(), source, game, Zone.LIBRARY, true)) { - toSuspend.addCounters(CounterType.TIME.createInstance(3), source.getControllerId(), source, game); - if (!hasSuspend) { - game.addEffect(new GainSuspendEffect(new MageObjectReference(toSuspend, game)), source); - } - game.informPlayers(player.getLogName() + " suspends 3 - " + toSuspend.getName()); - } - } - cards.remove(toSuspend); + cards.retainZone(Zone.LIBRARY, game); if (!cards.isEmpty()) { player.putCardsOnBottomOfLibrary(cards, game, source, false); } diff --git a/Mage.Sets/src/mage/cards/s/SinisterConcierge.java b/Mage.Sets/src/mage/cards/s/SinisterConcierge.java index 294258a3ec4..f7ae1aced8b 100644 --- a/Mage.Sets/src/mage/cards/s/SinisterConcierge.java +++ b/Mage.Sets/src/mage/cards/s/SinisterConcierge.java @@ -1,19 +1,9 @@ package mage.cards.s; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.ExileSourceWithTimeCountersCost; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.effects.common.continuous.GainSuspendEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.SuspendAbility; import mage.cards.Card; import mage.cards.CardImpl; @@ -22,7 +12,6 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -47,15 +36,7 @@ public class SinisterConcierge extends CardImpl { // Each card exiled this way that doesn't have suspend gains suspend. // (For each card with suspend, its owner removes a time counter from it at the beginning of their upkeep. // When the last is removed, they may cast it without paying its mana cost. Those creature spells have haste.) - Cost cost = new ExileSourceWithTimeCountersCost(3, false, true, Zone.GRAVEYARD); - // Paying the cost sends the Concierge to the right exile zone (Suspended cards of…) and gives it suspend. - cost.setText("exile it and put three time counters on it"); - Ability ability = new DiesSourceTriggeredAbility( - new DoIfCostPaid( - new SinisterConciergeEffect(), - cost - ) - ); + Ability ability = new DiesSourceTriggeredAbility(new SinisterConciergeEffect(), true); ability.addTarget(new TargetCreaturePermanent(0, 1)); this.addAbility(ability); } @@ -73,8 +54,8 @@ public class SinisterConcierge extends CardImpl { class SinisterConciergeEffect extends OneShotEffect { public SinisterConciergeEffect() { super(Outcome.Removal); - this.staticText = "exile up to one target creature and put three time counters on it. " + - "Each card exiled this way that doesn't have suspend gains suspend. " + + this.staticText = "exile it and put three time counters on it. If you do, exile up to one target creature " + + "and put three time counters on it. Each card exiled this way that doesn't have suspend gains suspend. " + "(For each card with suspend, its owner removes a time counter from it at the beginning of their upkeep. " + "When the last is removed, they may cast it without paying its mana cost. Those creature spells have haste.)"; } @@ -87,33 +68,18 @@ class SinisterConciergeEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Card card = game.getCard(source.getSourceId()); - if (controller == null || card == null) { + if (controller == null || card == null + || card.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter() + || !Zone.GRAVEYARD.match(game.getState().getZone(card.getId()))) { return false; } - + controller.moveCards(card, Zone.EXILED, source, game); + SuspendAbility.addTimeCountersAndSuspend(card, 3, source, game); Permanent targetCreature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (targetCreature == null){ - return false; + if (targetCreature != null) { + controller.moveCards(targetCreature, Zone.EXILED, source, game); + SuspendAbility.addTimeCountersAndSuspend(targetCreature.getMainCard(), 3, source, game); } - - // Exile, put time counters, and give suspend for target - Player controllerTarget = game.getPlayer(targetCreature.getControllerId()); - UUID exileId = SuspendAbility.getSuspendExileId(controllerTarget.getId(), game); - Effect exileTarget = new ExileTargetEffect(exileId, "Suspended cards of " + controllerTarget.getName()); - exileTarget.setTargetPointer(this.getTargetPointer().copy()); - if (exileTarget.apply(game, source)) { - Effect addCountersTargetEffect = new AddCountersTargetEffect(CounterType.TIME.createInstance(3)); - addCountersTargetEffect.setTargetPointer(this.getTargetPointer().copy()); - boolean targetCardShouldGetSuspend = addCountersTargetEffect.apply(game, source); - - if (targetCardShouldGetSuspend && !targetCreature.getAbilities(game).containsClass(SuspendAbility.class)) { - Card targetCard = game.getCard(getTargetPointer().getFirst(game, source)); - if (!targetCard.getAbilities(game).containsClass(SuspendAbility.class)) { - game.addEffect(new GainSuspendEffect(new MageObjectReference(targetCard, game)), source); - } - } - } - return true; } diff --git a/Mage.Sets/src/mage/cards/s/Suspend.java b/Mage.Sets/src/mage/cards/s/Suspend.java index 3f59bbf0d83..6a0cad5b8c8 100644 --- a/Mage.Sets/src/mage/cards/s/Suspend.java +++ b/Mage.Sets/src/mage/cards/s/Suspend.java @@ -1,9 +1,7 @@ package mage.cards.s; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.GainSuspendEffect; import mage.abilities.keyword.SuspendAbility; import mage.cards.Card; import mage.cards.CardImpl; @@ -11,7 +9,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -62,27 +59,13 @@ class SuspendEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getFirstTarget()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (controller == null || permanent == null) { return false; } Card card = permanent.getMainCard(); - if (!controller.moveCards(permanent, Zone.EXILED, source, game) - || game.getState().getZone(card.getId()) != Zone.EXILED) { - return true; - } - UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); - if (!controller.moveCardToExileWithInfo( - card, exileId, "Suspended cards of " + controller.getName(), - source, game, Zone.HAND, true - )) { - return true; - } - card.addCounters(CounterType.TIME.createInstance(2), source.getControllerId(), source, game); - if (!card.getAbilities(game).containsClass(SuspendAbility.class)) { - game.addEffect(new GainSuspendEffect(new MageObjectReference(card, game)), source); - } - game.informPlayers(controller.getLogName() + " suspends 2 - " + card.getName()); - return true; + return controller.moveCards(permanent, Zone.EXILED, source, game) + && game.getState().getZone(card.getId()) == Zone.EXILED + && SuspendAbility.addTimeCountersAndSuspend(card, 3, source, game); } } diff --git a/Mage.Sets/src/mage/cards/t/TaigamMasterOpportunist.java b/Mage.Sets/src/mage/cards/t/TaigamMasterOpportunist.java index 025b1df41c3..76724079539 100644 --- a/Mage.Sets/src/mage/cards/t/TaigamMasterOpportunist.java +++ b/Mage.Sets/src/mage/cards/t/TaigamMasterOpportunist.java @@ -1,31 +1,28 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.FlurryAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.GainSuspendEffect; import mage.abilities.keyword.SuspendAbility; import mage.cards.Card; -import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.counters.CounterType; +import mage.constants.*; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; +import java.util.UUID; + /** - * * @author Jmlundeen */ public final class TaigamMasterOpportunist extends CardImpl { public TaigamMasterOpportunist(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.MONK); @@ -65,30 +62,18 @@ class TaigamMasterOpportunistEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Spell spell = (Spell) this.getValue("spellCast"); Player controller = game.getPlayer(source.getControllerId()); - if (spell == null || controller == null) { + Spell spell = (Spell) this.getValue("spellCast"); + if (controller == null || spell == null) { return false; } - // copy it spell.createCopyOnStack(game, source, source.getControllerId(), false); // exile it, if it doesn't have suspend, it gains suspend // get main card to work with adventure/omen/split Card card = spell.getMainCard(); - if (card == null) { - return false; - } - UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); - if (controller.moveCardsToExile(card, source, game, true, exileId, "Suspended cards of " + controller.getName())) { - boolean hasSuspend = card.getAbilities(game).containsClass(SuspendAbility.class); - card.addCounters(CounterType.TIME.createInstance(4), source, game); - game.informPlayers(controller.getLogName() + " exiles " + spell.getLogName() + " with 3 time counters on it"); - if (!hasSuspend) { - game.addEffect(new GainSuspendEffect(new MageObjectReference(card, game)), source); - } - return true; - } - return false; + controller.moveCards(card, Zone.EXILED, source, game); + SuspendAbility.addTimeCountersAndSuspend(card, 4, source, game); + return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/t/TheEleventhDoctor.java b/Mage.Sets/src/mage/cards/t/TheEleventhDoctor.java new file mode 100644 index 00000000000..dee8e56b852 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheEleventhDoctor.java @@ -0,0 +1,99 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.abilities.keyword.SuspendAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheEleventhDoctor extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature with power 3 or less"); + + static { + filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 4)); + } + + public TheEleventhDoctor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.TIME_LORD); + this.subtype.add(SubType.DOCTOR); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // I. AM. TALKING! -- Whenever The Eleventh Doctor deals combat damage to a player, you may exile a card from your hand with a number of time counters on it equal to its mana value. If it doesn't have suspend, it gains suspend. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new TheEleventhDoctorEffect()).setTriggerPhrase("I. AM. TALKING!")); + + // {2}: Target creature with power 3 or less can't be blocked this turn. + Ability ability = new SimpleActivatedAbility(new CantBeBlockedTargetEffect(Duration.EndOfTurn), new GenericManaCost(2)); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private TheEleventhDoctor(final TheEleventhDoctor card) { + super(card); + } + + @Override + public TheEleventhDoctor copy() { + return new TheEleventhDoctor(this); + } +} + +class TheEleventhDoctorEffect extends OneShotEffect { + + TheEleventhDoctorEffect() { + super(Outcome.Benefit); + staticText = "you may exile a card from your hand with a number of time counters on it " + + "equal to its mana value. If it doesn't have suspend, it gains suspend"; + } + + private TheEleventhDoctorEffect(final TheEleventhDoctorEffect effect) { + super(effect); + } + + @Override + public TheEleventhDoctorEffect copy() { + return new TheEleventhDoctorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCard target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD); + player.choose(Outcome.PlayForFree, player.getHand(), target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + SuspendAbility.addTimeCountersAndSuspend(card, card.getManaValue(), source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThePartingOfTheWays.java b/Mage.Sets/src/mage/cards/t/ThePartingOfTheWays.java new file mode 100644 index 00000000000..c65cefa54da --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThePartingOfTheWays.java @@ -0,0 +1,98 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.counter.TimeTravelEffect; +import mage.abilities.keyword.SuspendAbility; +import mage.cards.*; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetArtifactPermanent; +import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThePartingOfTheWays extends CardImpl { + + public ThePartingOfTheWays(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}{R}"); + + this.subtype.add(SubType.SAGA); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- Exile the top five cards of your library. For each nonland card exiled this way, put a number of time counters on that card equal to its mana value. If it doesn't have suspend, it gains suspend. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new ThePartingOfTheWaysEffect()); + + // II -- Time travel, then time travel. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, + new TimeTravelEffect(false), + new TimeTravelEffect(false) + .concatBy(", then") + ); + + // III -- For each opponent, destroy up to one target artifact that player controls. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, + ability -> { + ability.addEffect(new DestroyTargetEffect() + .setText("For each opponent, destroy up to one target artifact that player controls")); + ability.addTarget(new TargetArtifactPermanent(0, 1)); + ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + } + ); + this.addAbility(sagaAbility); + } + + private ThePartingOfTheWays(final ThePartingOfTheWays card) { + super(card); + } + + @Override + public ThePartingOfTheWays copy() { + return new ThePartingOfTheWays(this); + } +} + +class ThePartingOfTheWaysEffect extends OneShotEffect { + + ThePartingOfTheWaysEffect() { + super(Outcome.Benefit); + staticText = "exile the top five cards of your library. For each nonland card exiled this way, " + + "put a number of time counters on that card equal to its mana value. " + + "If it doesn't have suspend, it gains suspend"; + } + + private ThePartingOfTheWaysEffect(final ThePartingOfTheWaysEffect effect) { + super(effect); + } + + @Override + public ThePartingOfTheWaysEffect copy() { + return new ThePartingOfTheWaysEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 5)); + player.moveCards(cards, Zone.EXILED, source, game); + cards.retainZone(Zone.EXILED, game); + for (Card card : cards.getCards(StaticFilters.FILTER_CARD_NON_LAND, game)) { + SuspendAbility.addTimeCountersAndSuspend(card, card.getManaValue(), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheTenthDoctor.java b/Mage.Sets/src/mage/cards/t/TheTenthDoctor.java index 80306587ab2..5afb16eb8bb 100644 --- a/Mage.Sets/src/mage/cards/t/TheTenthDoctor.java +++ b/Mage.Sets/src/mage/cards/t/TheTenthDoctor.java @@ -1,21 +1,17 @@ package mage.cards.t; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.InfoEffect; -import mage.abilities.effects.common.continuous.GainSuspendEffect; import mage.abilities.effects.common.counter.TimeTravelEffect; import mage.abilities.keyword.SuspendAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.CounterType; import mage.game.Game; import mage.players.Player; @@ -38,9 +34,8 @@ public final class TheTenthDoctor extends CardImpl { // Timey-Wimey — {7}: Time travel three times. Activate only as a sorcery. (For each suspended card you own and each permanent you control with a time counter on it, you may add or remove a time counter. Then do it two more times.) Ability ability = new ActivateAsSorceryActivatedAbility(new TimeTravelEffect().setText("Time travel three times"), new GenericManaCost(7)); - ability.addEffect(new TimeTravelEffect().setText("")); - ability.addEffect(new TimeTravelEffect().setText("")); - ability.addEffect(new InfoEffect("(For each suspended card you own and each permanent you control with a time counter on it, you may add or remove a time counter. Then do it two more times.)")); + ability.addEffect(new TimeTravelEffect().setText("(For each suspended card you own and each permanent you control with a time counter on it")); + ability.addEffect(new TimeTravelEffect().setText(", you may add or remove a time counter. Then do it two more times.)")); this.addAbility(ability.withFlavorWord("Timey-Wimey")); } @@ -73,19 +68,11 @@ class TheTenthDoctorEffect extends OneShotEffect { return false; } for (Card card : controller.getLibrary().getCards(game)) { - if (!card.isLand(game)) { - boolean hasSuspend = card.getAbilities(game).containsClass(SuspendAbility.class); - UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); - if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source, game, Zone.LIBRARY, true)) { - card.addCounters(CounterType.TIME.createInstance(3), source.getControllerId(), source, game); - if (!hasSuspend) { - game.addEffect(new GainSuspendEffect(new MageObjectReference(card, game)), source); - } - game.informPlayers(controller.getLogName() + " suspends 3 - " + card.getName()); - } - break; - } controller.moveCards(card, Zone.EXILED, source, game); + if (!card.isLand(game)) { + SuspendAbility.addTimeCountersAndSuspend(card, 3, source, game); + return true; + } } return true; } diff --git a/Mage.Sets/src/mage/cards/t/TheWeddingOfRiverSong.java b/Mage.Sets/src/mage/cards/t/TheWeddingOfRiverSong.java new file mode 100644 index 00000000000..78cdb25f6ec --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheWeddingOfRiverSong.java @@ -0,0 +1,92 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.TimeTravelEffect; +import mage.abilities.keyword.SuspendAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetOpponent; + +import java.util.Arrays; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheWeddingOfRiverSong extends CardImpl { + + public TheWeddingOfRiverSong(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}"); + + // Draw two cards, then you may exile a nonland card from your hand with a number of time counters on it equal to its mana value. Then target opponent does the same. Cards exiled this way that don't have suspend gain suspend. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); + this.getSpellAbility().addEffect(new TheWeddingOfRiverSongEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + + // Time travel. + this.getSpellAbility().addEffect(new TimeTravelEffect().concatBy("
")); + } + + private TheWeddingOfRiverSong(final TheWeddingOfRiverSong card) { + super(card); + } + + @Override + public TheWeddingOfRiverSong copy() { + return new TheWeddingOfRiverSong(this); + } +} + +class TheWeddingOfRiverSongEffect extends OneShotEffect { + + TheWeddingOfRiverSongEffect() { + super(Outcome.Benefit); + staticText = ", then you may exile a nonland card from your hand " + + "with a number of time counters on it equal to its mana value. Then target opponent does the same. " + + "Cards exiled this way that don't have suspend gain suspend"; + } + + private TheWeddingOfRiverSongEffect(final TheWeddingOfRiverSongEffect effect) { + super(effect); + } + + @Override + public TheWeddingOfRiverSongEffect copy() { + return new TheWeddingOfRiverSongEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Player player : Arrays.asList( + game.getPlayer(source.getControllerId()), + game.getPlayer(this.getTargetPointer().getFirst(game, source)) + )) { + if (player == null + || player.getHand().count(StaticFilters.FILTER_CARD_NON_LAND, game) < 1 + || source.isControlledBy(player.getId()) + && !player.chooseUse(outcome, "Suspend a nonland card from your hand?", source, game)) { + continue; + } + TargetCard target = new TargetCardInHand(StaticFilters.FILTER_CARD_NON_LAND); + player.choose(Outcome.PlayForFree, player.getHand(), target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + continue; + } + player.moveCards(card, Zone.EXILED, source, game); + SuspendAbility.addTimeCountersAndSuspend(card, card.getManaValue(), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/DoctorWho.java b/Mage.Sets/src/mage/sets/DoctorWho.java index 28179878807..774318dee67 100644 --- a/Mage.Sets/src/mage/sets/DoctorWho.java +++ b/Mage.Sets/src/mage/sets/DoctorWho.java @@ -900,13 +900,13 @@ public final class DoctorWho extends ExpansionSet { //cards.add(new SetCardInfo("The Eighth Doctor", 410, Rarity.RARE, mage.cards.t.TheEighthDoctor.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Eighth Doctor", 559, Rarity.RARE, mage.cards.t.TheEighthDoctor.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Eighth Doctor", 729, Rarity.RARE, mage.cards.t.TheEighthDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Eleventh Doctor", "562z", Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Eleventh Doctor", 1002, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Eleventh Doctor", 1153, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS));// - //cards.add(new SetCardInfo("The Eleventh Doctor", 125, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Eleventh Doctor", 411, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Eleventh Doctor", 562, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Eleventh Doctor", 730, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Eleventh Doctor", "562z", Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Eleventh Doctor", 1002, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Eleventh Doctor", 1153, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS));// + cards.add(new SetCardInfo("The Eleventh Doctor", 125, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Eleventh Doctor", 411, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Eleventh Doctor", 562, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Eleventh Doctor", 730, Rarity.RARE, mage.cards.t.TheEleventhDoctor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Eleventh Hour", 41, Rarity.RARE, mage.cards.t.TheEleventhHour.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Eleventh Hour", 646, Rarity.RARE, mage.cards.t.TheEleventhHour.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Face of Boe", 1003, Rarity.RARE, mage.cards.t.TheFaceOfBoe.class, NON_FULL_USE_VARIOUS)); @@ -1001,8 +1001,8 @@ public final class DoctorWho extends ExpansionSet { //cards.add(new SetCardInfo("The Pandorica", 343, Rarity.RARE, mage.cards.t.ThePandorica.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Pandorica", 630, Rarity.RARE, mage.cards.t.ThePandorica.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Pandorica", 934, Rarity.RARE, mage.cards.t.ThePandorica.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Parting of the Ways", 696, Rarity.RARE, mage.cards.t.ThePartingOfTheWays.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Parting of the Ways", 91, Rarity.RARE, mage.cards.t.ThePartingOfTheWays.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Parting of the Ways", 696, Rarity.RARE, mage.cards.t.ThePartingOfTheWays.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Parting of the Ways", 91, Rarity.RARE, mage.cards.t.ThePartingOfTheWays.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Pyramid of Mars", 597, Rarity.COMMON, mage.cards.t.ThePyramidOfMars.class)); cards.add(new SetCardInfo("The Rani", 1024, Rarity.RARE, mage.cards.t.TheRani.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Rani", 149, Rarity.RARE, mage.cards.t.TheRani.class, NON_FULL_USE_VARIOUS)); @@ -1081,10 +1081,10 @@ public final class DoctorWho extends ExpansionSet { //cards.add(new SetCardInfo("The War Doctor", 772, Rarity.RARE, mage.cards.t.TheWarDoctor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The War Games", 30, Rarity.RARE, mage.cards.t.TheWarGames.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The War Games", 635, Rarity.RARE, mage.cards.t.TheWarGames.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Wedding of River Song", 31, Rarity.RARE, mage.cards.t.TheWeddingOfRiverSong.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Wedding of River Song", 349, Rarity.RARE, mage.cards.t.TheWeddingOfRiverSong.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Wedding of River Song", 636, Rarity.RARE, mage.cards.t.TheWeddingOfRiverSong.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Wedding of River Song", 940, Rarity.RARE, mage.cards.t.TheWeddingOfRiverSong.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wedding of River Song", 31, Rarity.RARE, mage.cards.t.TheWeddingOfRiverSong.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wedding of River Song", 349, Rarity.RARE, mage.cards.t.TheWeddingOfRiverSong.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wedding of River Song", 636, Rarity.RARE, mage.cards.t.TheWeddingOfRiverSong.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wedding of River Song", 940, Rarity.RARE, mage.cards.t.TheWeddingOfRiverSong.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thespian's Stage", 1122, Rarity.RARE, mage.cards.t.ThespiansStage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thespian's Stage", 323, Rarity.RARE, mage.cards.t.ThespiansStage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thespian's Stage", 531, Rarity.RARE, mage.cards.t.ThespiansStage.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ncc/SinisterConciergeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ncc/SinisterConciergeTest.java index 1aef884b088..ba02d5587e5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ncc/SinisterConciergeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ncc/SinisterConciergeTest.java @@ -36,7 +36,7 @@ public class SinisterConciergeTest extends CardTestPlayerBase { setStrictChooseMode(true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, sinisterConcierge); - setChoice(playerA, "Yes"); + setChoice(playerA, true); addTarget(playerA, bondedConstruct); setStopAt(1, PhaseStep.END_COMBAT); diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileSourceWithTimeCountersCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileSourceWithTimeCountersCost.java deleted file mode 100644 index 4ce88003b7a..00000000000 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileSourceWithTimeCountersCost.java +++ /dev/null @@ -1,88 +0,0 @@ -package mage.abilities.costs.common; - -import java.util.Locale; -import java.util.UUID; - -import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; -import mage.abilities.effects.common.continuous.GainSuspendEffect; -import mage.abilities.keyword.SuspendAbility; -import mage.cards.Card; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.game.Game; -import mage.MageObjectReference; -import mage.players.Player; - - -/** - * @author padfoot - */ -public class ExileSourceWithTimeCountersCost extends CostImpl { - - private final int counters; - private final boolean checksSuspend; - private final boolean givesSuspend; - private final Zone fromZone; - - public ExileSourceWithTimeCountersCost(int counters) { - this (counters, true, false, null); - } - - public ExileSourceWithTimeCountersCost(int counters, boolean givesSuspend, boolean checksSuspend, Zone fromZone) { - this.counters = counters; - this.givesSuspend = givesSuspend; - this.checksSuspend = checksSuspend; - this.fromZone = fromZone; - this.text = "exile {this} " + - ((fromZone != null) ? " from your " + fromZone.toString().toLowerCase(Locale.ENGLISH) : "") + - " and put " + counters + " time counters on it" + - (givesSuspend ? ". It gains suspend" : "") + - (checksSuspend ? ". If it doesn't have suspend, it gains suspend" : ""); - } - - private ExileSourceWithTimeCountersCost(final ExileSourceWithTimeCountersCost cost) { - super(cost); - this.counters = cost.counters; - this.givesSuspend = cost.givesSuspend; - this.checksSuspend = cost.checksSuspend; - this.fromZone = cost.fromZone; - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Player controller = game.getPlayer(controllerId); - if (controller == null) { - return paid; - } - Card card = game.getCard(source.getSourceId()); - boolean hasSuspend = card.getAbilities(game).containsClass(SuspendAbility.class); - if (card != null && (fromZone == null || fromZone == game.getState().getZone(source.getSourceId()))) { - UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); - if (controller.moveCardsToExile(card, source, game, true, exileId, "Suspended cards of " + controller.getName())) { - card.addCounters(CounterType.TIME.createInstance(counters), controller.getId(), source, game); - game.informPlayers(controller.getLogName() + " exiles " + card.getLogName() + ((fromZone != null) ? " from their " + fromZone.toString().toLowerCase(Locale.ENGLISH) : "") + " with " + counters + " time counters on it."); - if (givesSuspend || (checksSuspend && !hasSuspend)) { - game.addEffect(new GainSuspendEffect(new MageObjectReference(card, game)), source); - } - } - // 117.11. The actions performed when paying a cost may be modified by effects. - // Even if they are, meaning the actions that are performed don't match the actions - // that are called for, the cost has still been paid. - // so return state here is not important because the user indended to exile the target anyway - paid = true; - } - return paid; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return (game.getCard(source.getSourceId()) != null && (fromZone == null || fromZone == game.getState().getZone(source.getSourceId()))); - } - - @Override - public ExileSourceWithTimeCountersCost copy() { - return new ExileSourceWithTimeCountersCost(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileSpellWithTimeCountersEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileSpellWithTimeCountersEffect.java index 95f9e58327a..6e63b969f58 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileSpellWithTimeCountersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileSpellWithTimeCountersEffect.java @@ -23,7 +23,7 @@ public class ExileSpellWithTimeCountersEffect extends OneShotEffect { private final boolean gainsSuspend; public ExileSpellWithTimeCountersEffect(int counters) { - this (counters, false); + this(counters, false); } public ExileSpellWithTimeCountersEffect(int counters, boolean gainsSuspend) { @@ -33,6 +33,7 @@ public class ExileSpellWithTimeCountersEffect extends OneShotEffect { this.staticText = "exile {this} with " + CardUtil.numberToText(this.counters) + " time counters on it" + (gainsSuspend ? ". It gains suspend" : ""); } + private ExileSpellWithTimeCountersEffect(final ExileSpellWithTimeCountersEffect effect) { super(effect); this.counters = effect.counters; diff --git a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java index 1114ecebc64..86b584fddf3 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java @@ -1,10 +1,10 @@ package mage.abilities.keyword; import mage.MageIdentifier; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.SpecialAction; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.common.SuspendedCondition; import mage.abilities.costs.VariableCostType; import mage.abilities.costs.mana.ManaCost; @@ -14,7 +14,9 @@ import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainSuspendEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.Card; import mage.cards.CardsImpl; import mage.cards.ModalDoubleFacedCard; @@ -230,6 +232,34 @@ public class SuspendAbility extends SpecialAction { return super.canActivate(playerId, game); } + public static boolean addTimeCountersAndSuspend(Card card, int amount, Ability source, Game game) { + if (card == null || card.isCopy()) { + return false; + } + if (!Zone.EXILED.match(game.getState().getZone(card.getId()))) { + return false; + } + Player owner = game.getPlayer(card.getOwnerId()); + if (owner == null) { + return false; + } + game.getExile().moveToAnotherZone( + card.getMainCard(), game, + game.getExile().createZone( + SuspendAbility.getSuspendExileId(owner.getId(), game), + "Suspended cards of " + owner.getName() + ) + ); + if (amount > 0) { + card.addCounters(CounterType.TIME.createInstance(amount), owner.getId(), source, game); + } + if (!card.getAbilities(game).containsClass(SuspendAbility.class)) { + game.addEffect(new GainSuspendEffect(new MageObjectReference(card, game)), source); + } + game.informPlayers(owner.getLogName() + " suspends " + amount + " - " + card.getName()); + return true; + } + @Override public String getRule() { return ruleText;