From a3ca9fc03a7d4fda05095aa8508b949d43b16eca Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 19 Sep 2021 04:01:01 +0400 Subject: [PATCH] Dauthi Voidwalker - correct code for #8141 and same cards (see comments in fd719ad287484a50c97378a293ece5aa93c1d7e2); --- .../mage/cards/d/DarigaazReincarnated.java | 34 ++++------- .../src/mage/cards/d/DauthiVoidwalker.java | 19 ++----- .../src/mage/cards/d/DraugrNecromancer.java | 7 +-- Mage.Sets/src/mage/cards/e/Epochrasite.java | 38 +++++++------ .../src/mage/cards/j/JhoiraOfTheGhitu.java | 2 + .../src/mage/cards/m/MairsilThePretender.java | 56 +++++++++---------- .../single/mh2/DauthiVoidwalkerTest.java | 42 ++++++++++++++ Mage/src/main/java/mage/util/CardUtil.java | 32 +++++++++++ 8 files changed, 142 insertions(+), 88 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/mh2/DauthiVoidwalkerTest.java diff --git a/Mage.Sets/src/mage/cards/d/DarigaazReincarnated.java b/Mage.Sets/src/mage/cards/d/DarigaazReincarnated.java index a4363062da..5a7a0756ea 100644 --- a/Mage.Sets/src/mage/cards/d/DarigaazReincarnated.java +++ b/Mage.Sets/src/mage/cards/d/DarigaazReincarnated.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -18,22 +16,18 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class DarigaazReincarnated extends CardImpl { @@ -92,15 +86,11 @@ class DarigaazReincarnatedDiesEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent permanent = ((ZoneChangeEvent) event).getTarget(); Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && permanent != null) { - Card permCard = game.getCard(permanent.getId()); - if (permCard == null) { - return false; - } - return controller.moveCardToExileWithInfo(permanent, null, null, source, game, Zone.BATTLEFIELD, true) - && permCard.addCounters(CounterType.EGG.createInstance(3), source.getControllerId(), source, game); + if (permanent == null || controller == null) { + return false; } - return false; + + return CardUtil.moveCardWithCounter(game, source, controller, permanent, Zone.EXILED, CounterType.EGG.createInstance(3)); } @Override @@ -125,7 +115,7 @@ class DarigaazReincarnatedInterveningIfTriggeredAbility extends ConditionalInter super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new DarigaazReincarnatedReturnEffect(), TargetController.YOU, false), DarigaazReincarnatedCondition.instance, "At the beginning of your upkeep, if {this} is exiled with an egg counter on it, " - + "remove an egg counter from it. Then if {this} has no egg counters on it, return it to the battlefield"); + + "remove an egg counter from it. Then if {this} has no egg counters on it, return it to the battlefield"); } public DarigaazReincarnatedInterveningIfTriggeredAbility(final DarigaazReincarnatedInterveningIfTriggeredAbility effect) { @@ -181,10 +171,8 @@ enum DarigaazReincarnatedCondition implements Condition { public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getSourceId()); if (card != null) { - if (game.getState().getZone(card.getId()) == Zone.EXILED - && card.getCounters(game).getCount(CounterType.EGG) > 0) { - return true; - } + return game.getState().getZone(card.getId()) == Zone.EXILED + && card.getCounters(game).getCount(CounterType.EGG) > 0; } return false; } diff --git a/Mage.Sets/src/mage/cards/d/DauthiVoidwalker.java b/Mage.Sets/src/mage/cards/d/DauthiVoidwalker.java index 45e6a97c65..47f3e6acff 100644 --- a/Mage.Sets/src/mage/cards/d/DauthiVoidwalker.java +++ b/Mage.Sets/src/mage/cards/d/DauthiVoidwalker.java @@ -19,9 +19,11 @@ import mage.filter.FilterCard; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInExile; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -80,21 +82,12 @@ class DauthiVoidwalkerReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); - Card card = ((ZoneChangeEvent) event).getTarget(); - if (card == null) { - card = game.getCard(event.getTargetId()); - } - if (controller == null - || card == null) { + Permanent permanent = ((ZoneChangeEvent) event).getTarget(); + if (controller == null || permanent == null) { return false; } - controller.moveCards(card, Zone.EXILED, source, game); - // okay, not sure why this needs to be done to work correctly with creature permanents - Card cardFromObject = (Card) game.getObject(card.getId()); - if (cardFromObject != null) { - return cardFromObject.addCounters(CounterType.VOID.createInstance(), source.getControllerId(), source, game); - } - return false; + CardUtil.moveCardWithCounter(game, source, controller, permanent, Zone.EXILED, CounterType.VOID.createInstance()); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/d/DraugrNecromancer.java b/Mage.Sets/src/mage/cards/d/DraugrNecromancer.java index 3e8d08a85f..b664c9e230 100644 --- a/Mage.Sets/src/mage/cards/d/DraugrNecromancer.java +++ b/Mage.Sets/src/mage/cards/d/DraugrNecromancer.java @@ -17,6 +17,7 @@ import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; import mage.players.ManaPoolItem; import mage.players.Player; +import mage.util.CardUtil; import java.util.UUID; @@ -79,10 +80,8 @@ class DraugrNecromancerReplacementEffect extends ReplacementEffectImpl { || !controller.hasOpponent(permanent.getControllerId(), game)) { return false; } - Card card = game.getCard(permanent.getId()); - controller.moveCards(permanent, Zone.EXILED, source, game); - card.getMainCard().addCounters(CounterType.ICE.createInstance(), source.getControllerId(), source, game); - return true; + + return CardUtil.moveCardWithCounter(game, source, controller, permanent, Zone.EXILED, CounterType.ICE.createInstance()); } @Override diff --git a/Mage.Sets/src/mage/cards/e/Epochrasite.java b/Mage.Sets/src/mage/cards/e/Epochrasite.java index a6122b684e..6c6b15ee5b 100644 --- a/Mage.Sets/src/mage/cards/e/Epochrasite.java +++ b/Mage.Sets/src/mage/cards/e/Epochrasite.java @@ -1,7 +1,5 @@ - package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; @@ -17,22 +15,23 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.players.Player; import mage.watchers.common.CastFromHandWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Epochrasite extends CardImpl { public Epochrasite(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(1); @@ -40,9 +39,9 @@ public final class Epochrasite extends CardImpl { // Epochrasite enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand. this.addAbility(new EntersBattlefieldAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), - new InvertCondition(CastFromHandSourcePermanentCondition.instance), - "{this} enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand",""), + new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), + new InvertCondition(CastFromHandSourcePermanentCondition.instance), + "{this} enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand", ""), new CastFromHandWatcher()); // When Epochrasite dies, exile it with three time counters on it and it gains suspend. @@ -79,15 +78,20 @@ class EpochrasiteEffect 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 (game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { - UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); - controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source, game, Zone.GRAVEYARD, true); - card.addCounters(CounterType.TIME.createInstance(3), source.getControllerId(), source, game); - game.addEffect(new GainSuspendEffect(new MageObjectReference(card, game)), source); - } - return true; + if (controller == null || card == null) { + return false; } - return false; + card = card.getMainCard(); + + if (game.getState().getZone(card.getId()) != Zone.GRAVEYARD) { + return false; + } + + UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); + controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source, game, Zone.GRAVEYARD, true); + card.addCounters(CounterType.TIME.createInstance(3), source.getControllerId(), source, game); + game.addEffect(new GainSuspendEffect(new MageObjectReference(card, game)), source); + + return true; } } diff --git a/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java b/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java index bfc7e937e9..c2b5cb9ef5 100644 --- a/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java +++ b/Mage.Sets/src/mage/cards/j/JhoiraOfTheGhitu.java @@ -88,6 +88,8 @@ class JhoiraOfTheGhituSuspendEffect extends OneShotEffect { if (card == null) { return false; } + card = card.getMainCard(); + boolean hasSuspend = card.getAbilities(game).containsClass(SuspendAbility.class); UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); diff --git a/Mage.Sets/src/mage/cards/m/MairsilThePretender.java b/Mage.Sets/src/mage/cards/m/MairsilThePretender.java index c1e0146f00..7329c29521 100644 --- a/Mage.Sets/src/mage/cards/m/MairsilThePretender.java +++ b/Mage.Sets/src/mage/cards/m/MairsilThePretender.java @@ -1,7 +1,5 @@ package mage.cards.m; -import java.util.Objects; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; @@ -12,14 +10,7 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; @@ -29,9 +20,12 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.UUID; /** - * * @author TheElk801 */ public final class MairsilThePretender extends CardImpl { @@ -90,27 +84,27 @@ class MairsilThePretenderExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - if (controller.chooseUse(Outcome.PutCardInPlay, "Exile a card from your hand? (No = from graveyard)", source, game)) { - Target target = new TargetCardInHand(0, 1, filter); - controller.choose(outcome, target, source.getSourceId(), game); - Card card = controller.getHand().get(target.getFirstTarget(), game); - if (card != null) { - controller.moveCards(card, Zone.EXILED, source, game); - card.addCounters(CounterType.CAGE.createInstance(), source.getControllerId(), source, game); - } - } else { - Target target = new TargetCardInYourGraveyard(0, 1, filter); - target.choose(Outcome.PutCardInPlay, source.getControllerId(), source.getSourceId(), game); - Card card = controller.getGraveyard().get(target.getFirstTarget(), game); - if (card != null) { - controller.moveCards(card, Zone.EXILED, source, game); - card.addCounters(CounterType.CAGE.createInstance(), source.getControllerId(), source, game); - } - } - return true; + if (controller == null) { + return false; } - return false; + + // Outcome.Detriment - AI must exile from grave only, not hand + Target target; + if (controller.chooseUse(Outcome.Detriment, "Exile a card from your hand? (No = from graveyard)", source, game)) { + // from hand + target = new TargetCardInHand(0, 1, filter); + controller.choose(outcome, target, source.getSourceId(), game); + } else { + // from graveyard + target = new TargetCardInYourGraveyard(0, 1, filter); + target.choose(outcome, source.getControllerId(), source.getSourceId(), game); + } + + Card card = controller.getHand().get(target.getFirstTarget(), game); + if (card != null) { + CardUtil.moveCardWithCounter(game, source, controller, card, Zone.EXILED, CounterType.CAGE.createInstance()); + } + return true; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh2/DauthiVoidwalkerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh2/DauthiVoidwalkerTest.java new file mode 100644 index 0000000000..cb3aa068f8 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh2/DauthiVoidwalkerTest.java @@ -0,0 +1,42 @@ +package org.mage.test.cards.single.mh2; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class DauthiVoidwalkerTest extends CardTestPlayerBase { + + @Test + public void test_Play() { + // If a card would be put into an opponent's graveyard from anywhere, instead exile it with a void counter on it. + // {T}, Sacrifice Dauthi Voidwalker: Choose an exiled card an opponent owns with a void counter on it. You may play it this turn without paying its mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Dauthi Voidwalker", 1); + // + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + // + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + // kill B's creature and exile with void counter + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkExileCount("after exile", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1); + + // can play it for free + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice"); + setChoice(playerA, "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } +} diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index b9837f5343..fdbae85486 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -11,12 +11,16 @@ import mage.abilities.condition.Condition; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.*; import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.asthought.CanPlayCardControllerEffect; import mage.abilities.effects.common.asthought.YouMaySpendManaAsAnyColorToCastTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.hint.Hint; import mage.abilities.hint.HintUtils; import mage.cards.*; import mage.constants.*; +import mage.counters.Counter; +import mage.counters.CounterType; import mage.filter.Filter; import mage.filter.predicate.mageobject.NamePredicate; import mage.game.CardState; @@ -1371,4 +1375,32 @@ public final class CardUtil { return stream.filter(clazz::isInstance).map(clazz::cast); } + /** + * Move card or permanent to dest zone and add counter to it + * + * @param game + * @param source + * @param controller + * @param card can be card or permanent + * @param toZone + * @param counter + */ + public static boolean moveCardWithCounter(Game game, Ability source, Player controller, Card card, Zone toZone, Counter counter) { + if (toZone == Zone.BATTLEFIELD) { + throw new IllegalArgumentException("Wrong code usage - method doesn't support moving to battlefield zone"); + } + + // move to zone + if (!controller.moveCards(card, toZone, source, game)) { + return false; + } + + // add counter + // after move it's a new object (not a permanent), so must work with main card + Effect effect = new AddCountersTargetEffect(counter); + effect.setTargetPointer(new FixedTarget(card.getMainCard(), game)); + effect.apply(game, source); + return true; + } + }