From b4da5267706983621d915107ce6e9e883fd5b631 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Mon, 27 May 2024 19:39:45 -0400 Subject: [PATCH] refactor: common class DrawCardTargetControllerEffect tests for Ob Nixilis the Hate-Twisted; Dream Fracture resolves #12292 --- Mage.Sets/src/mage/cards/a/ArgentDais.java | 38 +------- Mage.Sets/src/mage/cards/c/CallToHeel.java | 49 +---------- Mage.Sets/src/mage/cards/d/DreamFracture.java | 49 ++--------- .../src/mage/cards/e/ErtaiResurrected.java | 88 +++---------------- .../src/mage/cards/g/GwafaHazidProfiteer.java | 52 +++-------- .../cards/i/IntroductionToAnnihilation.java | 44 +--------- .../src/mage/cards/n/NinThePainArtist.java | 48 ++-------- .../mage/cards/o/ObNixilisTheHateTwisted.java | 42 +-------- Mage.Sets/src/mage/cards/t/TriadOfFates.java | 36 +------- .../mage/test/cards/mana/CryptGhastTest.java | 3 + .../cards/single/eve/DreamFractureTest.java | 67 ++++++++++++++ .../war/ObNixilisTheHateTwistedTest.java | 65 ++++++++++++++ .../DrawCardTargetControllerEffect.java | 61 +++++++++++++ 13 files changed, 250 insertions(+), 392 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/eve/DreamFractureTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/war/ObNixilisTheHateTwistedTest.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/DrawCardTargetControllerEffect.java diff --git a/Mage.Sets/src/mage/cards/a/ArgentDais.java b/Mage.Sets/src/mage/cards/a/ArgentDais.java index e2e8a47bda0..fd1818417b3 100644 --- a/Mage.Sets/src/mage/cards/a/ArgentDais.java +++ b/Mage.Sets/src/mage/cards/a/ArgentDais.java @@ -7,21 +7,18 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardTargetControllerEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetNonlandPermanent; import java.util.UUID; @@ -57,7 +54,7 @@ public final class ArgentDais extends CardImpl { ability.addCost(new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.OIL.createInstance(2))); ability.addTarget(new TargetNonlandPermanent(filter)); - ability.addEffect(new ArgentDaisTargetEffect()); + ability.addEffect(new DrawCardTargetControllerEffect(2)); this.addAbility(ability); } @@ -97,34 +94,3 @@ class ArgentDaisTriggeredAbility extends TriggeredAbilityImpl { return game.getCombat().getAttackers().size() >= 2; } } - -class ArgentDaisTargetEffect extends OneShotEffect { - - ArgentDaisTargetEffect() { - super(Outcome.DrawCard); - this.staticText = "its controller draws two cards"; - } - - private ArgentDaisTargetEffect(final ArgentDaisTargetEffect effect) { - super(effect); - } - - @Override - public ArgentDaisTargetEffect copy() { - return new ArgentDaisTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); - if (permanent == null) { - return false; - } - Player controllerOfTarget = game.getPlayer(permanent.getControllerId()); - if (controllerOfTarget == null) { - return false; - } - controllerOfTarget.drawCards(2, source, game); - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CallToHeel.java b/Mage.Sets/src/mage/cards/c/CallToHeel.java index a1002eb16e0..470c9eee3d8 100644 --- a/Mage.Sets/src/mage/cards/c/CallToHeel.java +++ b/Mage.Sets/src/mage/cards/c/CallToHeel.java @@ -1,20 +1,14 @@ - package mage.cards.c; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardTargetControllerEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author North @@ -24,10 +18,9 @@ public final class CallToHeel extends CardImpl { public CallToHeel(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); - // Return target creature to its owner's hand. Its controller draws a card. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); - this.getSpellAbility().addEffect(new CallToHeelEffect()); + this.getSpellAbility().addEffect(new DrawCardTargetControllerEffect(1)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } @@ -40,37 +33,3 @@ public final class CallToHeel extends CardImpl { return new CallToHeel(this); } } - -class CallToHeelEffect extends OneShotEffect { - - CallToHeelEffect() { - super(Outcome.Neutral); - this.staticText = "Its controller draws a card"; - } - - private CallToHeelEffect(final CallToHeelEffect effect) { - super(effect); - } - - @Override - public CallToHeelEffect copy() { - return new CallToHeelEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); - } - - if (permanent != null) { - Player controller = game.getPlayer(permanent.getControllerId()); - if (controller != null) { - controller.drawCards(1, source, game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DreamFracture.java b/Mage.Sets/src/mage/cards/d/DreamFracture.java index 363ef741d0e..6ef8dacd22b 100644 --- a/Mage.Sets/src/mage/cards/d/DreamFracture.java +++ b/Mage.Sets/src/mage/cards/d/DreamFracture.java @@ -1,18 +1,15 @@ - package mage.cards.d; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.DrawCardTargetControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetSpell; +import java.util.UUID; + /** * * @author jeffwadsworth @@ -23,8 +20,9 @@ public final class DreamFracture extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{U}"); // Counter target spell. Its controller draws a card. - this.getSpellAbility().addEffect(new DreamFractureEffect()); + this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addTarget(new TargetSpell()); + this.getSpellAbility().addEffect(new DrawCardTargetControllerEffect(1)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); @@ -40,38 +38,3 @@ public final class DreamFracture extends CardImpl { return new DreamFracture(this); } } - -class DreamFractureEffect extends OneShotEffect { - - DreamFractureEffect() { - super(Outcome.Neutral); - this.staticText = "Counter target spell. Its controller draws a card"; - } - - private DreamFractureEffect(final DreamFractureEffect effect) { - super(effect); - } - - @Override - public DreamFractureEffect copy() { - return new DreamFractureEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - UUID targetId = source.getFirstTarget(); - Player controller = null; - boolean countered = false; - if (targetId != null) { - controller = game.getPlayer(game.getControllerId(targetId)); - } - if (targetId != null - && game.getStack().counter(targetId, source, game)) { - countered = true; - } - if (controller != null) { - controller.drawCards(1, source, game); - } - return countered; - } -} diff --git a/Mage.Sets/src/mage/cards/e/ErtaiResurrected.java b/Mage.Sets/src/mage/cards/e/ErtaiResurrected.java index 6d8b47ca322..a385ff7639c 100644 --- a/Mage.Sets/src/mage/cards/e/ErtaiResurrected.java +++ b/Mage.Sets/src/mage/cards/e/ErtaiResurrected.java @@ -1,27 +1,25 @@ package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DrawCardTargetControllerEffect; import mage.abilities.keyword.FlashAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.TargetStackObject; +import java.util.UUID; + /** * * @author weirddan455 @@ -29,7 +27,7 @@ import mage.target.TargetStackObject; public final class ErtaiResurrected extends CardImpl { private static final FilterCreatureOrPlaneswalkerPermanent filter = - new FilterCreatureOrPlaneswalkerPermanent("another creature or planeswalker"); + new FilterCreatureOrPlaneswalkerPermanent("another target creature or planeswalker"); static { filter.add(AnotherPredicate.instance); @@ -50,14 +48,17 @@ public final class ErtaiResurrected extends CardImpl { // When Ertai Resurrected enters the battlefield, choose up to one -- // * Counter target spell, activated ability, or triggered ability. Its controller draws a card. - Ability ability = new EntersBattlefieldTriggeredAbility(new ErtaiResurrectedCounterEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new CounterTargetEffect() + .setText("Counter target spell, activated ability, or triggered ability")); ability.addTarget(new TargetStackObject()); + ability.addEffect(new DrawCardTargetControllerEffect(1)); ability.getModes().setMinModes(0); ability.getModes().setMaxModes(1); // * Destroy another target creature or planeswalker. Its controller draws a card. - Mode mode = new Mode(new ErtaiResurrectedDestroyEffect()); + Mode mode = new Mode(new DestroyTargetEffect()); mode.addTarget(new TargetPermanent(filter)); + mode.addEffect(new DrawCardTargetControllerEffect(1)); ability.addMode(mode); this.addAbility(ability); } @@ -71,66 +72,3 @@ public final class ErtaiResurrected extends CardImpl { return new ErtaiResurrected(this); } } - -class ErtaiResurrectedCounterEffect extends OneShotEffect { - - ErtaiResurrectedCounterEffect() { - super(Outcome.Detriment); - this.staticText = "Counter target spell, activated ability, or triggered ability. Its controller draws a card."; - } - - private ErtaiResurrectedCounterEffect(final ErtaiResurrectedCounterEffect effect) { - super(effect); - } - - @Override - public ErtaiResurrectedCounterEffect copy() { - return new ErtaiResurrectedCounterEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - UUID targetId = source.getFirstTarget(); - StackObject stackObject = game.getStack().getStackObject(targetId); - if (stackObject == null) { - return false; - } - Player player = game.getPlayer(stackObject.getControllerId());; - game.getStack().counter(targetId, source, game); - if (player != null) { - player.drawCards(1, source, game); - } - return true; - } -} - -class ErtaiResurrectedDestroyEffect extends OneShotEffect { - - ErtaiResurrectedDestroyEffect() { - super(Outcome.DestroyPermanent); - this.staticText = "Destroy another target creature or planeswalker. Its controller draws a card."; - } - - private ErtaiResurrectedDestroyEffect(final ErtaiResurrectedDestroyEffect effect) { - super(effect); - } - - @Override - public ErtaiResurrectedDestroyEffect copy() { - return new ErtaiResurrectedDestroyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent == null) { - return false; - } - Player player = game.getPlayer(permanent.getControllerId()); - permanent.destroy(source, game); - if (player != null) { - player.drawCards(1, source, game); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/g/GwafaHazidProfiteer.java b/Mage.Sets/src/mage/cards/g/GwafaHazidProfiteer.java index a9c0f6539d2..874b272b4a3 100644 --- a/Mage.Sets/src/mage/cards/g/GwafaHazidProfiteer.java +++ b/Mage.Sets/src/mage/cards/g/GwafaHazidProfiteer.java @@ -6,8 +6,9 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.DrawCardTargetControllerEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -15,7 +16,6 @@ import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -35,13 +35,14 @@ public final class GwafaHazidProfiteer extends CardImpl { this.toughness = new MageInt(2); // {W}{U}, {tap}: Put a bribery counter on target creature you don't control. Its controller draws a card. - Ability ability = new SimpleActivatedAbility(new GwafaHazidProfiteerEffect1(), new ManaCostsImpl<>("{W}{U}")); + Ability ability = new SimpleActivatedAbility(new AddCountersTargetEffect(CounterType.BRIBERY.createInstance()), new ManaCostsImpl<>("{W}{U}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + ability.addEffect(new DrawCardTargetControllerEffect(1)); this.addAbility(ability); // Creatures with bribery counters on them can't attack or block. - this.addAbility(new SimpleStaticAbility(new GwafaHazidProfiteerEffect2())); + this.addAbility(new SimpleStaticAbility(new GwafaHazidProfiteerEffect())); } private GwafaHazidProfiteer(final GwafaHazidProfiteer card) { @@ -54,47 +55,14 @@ public final class GwafaHazidProfiteer extends CardImpl { } } -class GwafaHazidProfiteerEffect1 extends OneShotEffect { +class GwafaHazidProfiteerEffect extends RestrictionEffect { - GwafaHazidProfiteerEffect1() { - super(Outcome.Detriment); - staticText = "Put a bribery counter on target creature you don't control. Its controller draws a card"; - } - - private GwafaHazidProfiteerEffect1(final GwafaHazidProfiteerEffect1 effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanent(source.getFirstTarget()); - if (targetCreature != null) { - Player controller = game.getPlayer(targetCreature.getControllerId()); - targetCreature.addCounters(CounterType.BRIBERY.createInstance(), source.getControllerId(), source, game); - if (controller != null) { - controller.drawCards(1, source, game); - } - return true; - } - return false; - } - - @Override - public GwafaHazidProfiteerEffect1 copy() { - return new GwafaHazidProfiteerEffect1(this); - } - - -} - -class GwafaHazidProfiteerEffect2 extends RestrictionEffect { - - GwafaHazidProfiteerEffect2() { + GwafaHazidProfiteerEffect() { super(Duration.WhileOnBattlefield); staticText = "Creatures with bribery counters on them can't attack or block"; } - private GwafaHazidProfiteerEffect2(final GwafaHazidProfiteerEffect2 effect) { + private GwafaHazidProfiteerEffect(final GwafaHazidProfiteerEffect effect) { super(effect); } @@ -114,7 +82,7 @@ class GwafaHazidProfiteerEffect2 extends RestrictionEffect { } @Override - public GwafaHazidProfiteerEffect2 copy() { - return new GwafaHazidProfiteerEffect2(this); + public GwafaHazidProfiteerEffect copy() { + return new GwafaHazidProfiteerEffect(this); } } diff --git a/Mage.Sets/src/mage/cards/i/IntroductionToAnnihilation.java b/Mage.Sets/src/mage/cards/i/IntroductionToAnnihilation.java index eb78b60cf53..fce26a353b0 100644 --- a/Mage.Sets/src/mage/cards/i/IntroductionToAnnihilation.java +++ b/Mage.Sets/src/mage/cards/i/IntroductionToAnnihilation.java @@ -1,16 +1,11 @@ package mage.cards.i; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardTargetControllerEffect; +import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetNonlandPermanent; import java.util.UUID; @@ -26,7 +21,8 @@ public final class IntroductionToAnnihilation extends CardImpl { this.subtype.add(SubType.LESSON); // Exile target nonland permanent. Its controller draws a card. - this.getSpellAbility().addEffect(new IntroductionToAnnihilationEffect()); + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addEffect(new DrawCardTargetControllerEffect(1)); this.getSpellAbility().addTarget(new TargetNonlandPermanent()); } @@ -39,35 +35,3 @@ public final class IntroductionToAnnihilation extends CardImpl { return new IntroductionToAnnihilation(this); } } - -class IntroductionToAnnihilationEffect extends OneShotEffect { - - IntroductionToAnnihilationEffect() { - super(Outcome.Benefit); - staticText = "exile target nonland permanent. Its controller draws a card"; - } - - private IntroductionToAnnihilationEffect(final IntroductionToAnnihilationEffect effect) { - super(effect); - } - - @Override - public IntroductionToAnnihilationEffect copy() { - return new IntroductionToAnnihilationEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (controller == null || permanent == null) { - return false; - } - controller.moveCards(permanent, Zone.EXILED, source, game); - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null) { - player.drawCards(1, source, game); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/n/NinThePainArtist.java b/Mage.Sets/src/mage/cards/n/NinThePainArtist.java index 6fc122686bf..b5571dce4a7 100644 --- a/Mage.Sets/src/mage/cards/n/NinThePainArtist.java +++ b/Mage.Sets/src/mage/cards/n/NinThePainArtist.java @@ -1,25 +1,23 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DrawCardTargetControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.SuperType; import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author emerald000 @@ -36,9 +34,12 @@ public final class NinThePainArtist extends CardImpl { this.toughness = new MageInt(1); // {X}{U}{R}, {tap}: Nin, the Pain Artist deals X damage to target creature. That creature's controller draws X cards. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new NinThePainArtistEffect(), new ManaCostsImpl<>("{X}{U}{R}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new DamageTargetEffect(ManacostVariableValue.REGULAR), new ManaCostsImpl<>("{X}{U}{R}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); + ability.addEffect(new DrawCardTargetControllerEffect(ManacostVariableValue.REGULAR) + .setText("that creature's controller draws X cards")); this.addAbility(ability); } @@ -51,34 +52,3 @@ public final class NinThePainArtist extends CardImpl { return new NinThePainArtist(this); } } - -class NinThePainArtistEffect extends OneShotEffect { - - NinThePainArtistEffect() { - super(Outcome.Damage); - this.staticText = "{this} deals X damage to target creature. That creature's controller draws X cards."; - } - - private NinThePainArtistEffect(final NinThePainArtistEffect effect) { - super(effect); - } - - @Override - public NinThePainArtistEffect copy() { - return new NinThePainArtistEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (permanent != null) { - permanent.damage(source.getManaCostsToPay().getX(), source.getSourceId(), source, game, false, true); - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null) { - player.drawCards(source.getManaCostsToPay().getX(), source, game); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java b/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java index 8ff6e1e3e02..09a51a27ee6 100644 --- a/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java +++ b/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java @@ -3,17 +3,14 @@ package mage.cards.o; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.DrawCardOpponentTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DrawCardTargetControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.SuperType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -36,8 +33,9 @@ public final class ObNixilisTheHateTwisted extends CardImpl { ), false, true)); // -2: Destroy target creature. Its controller draws two cards. - Ability ability = new LoyaltyAbility(new ObNixilisTheHateTwistedEffect(), -2); + Ability ability = new LoyaltyAbility(new DestroyTargetEffect(), -2); ability.addTarget(new TargetCreaturePermanent()); + ability.addEffect(new DrawCardTargetControllerEffect(2)); this.addAbility(ability); } @@ -50,35 +48,3 @@ public final class ObNixilisTheHateTwisted extends CardImpl { return new ObNixilisTheHateTwisted(this); } } - -class ObNixilisTheHateTwistedEffect extends OneShotEffect { - - ObNixilisTheHateTwistedEffect() { - super(Outcome.Benefit); - staticText = "Destroy target creature. Its controller draws two cards"; - } - - private ObNixilisTheHateTwistedEffect(final ObNixilisTheHateTwistedEffect effect) { - super(effect); - } - - @Override - public ObNixilisTheHateTwistedEffect copy() { - return new ObNixilisTheHateTwistedEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent == null) { - return false; - } - permanent.destroy(source, game, false); - Player player = game.getPlayer(permanent.getControllerId()); - if (player == null) { - return false; - } - player.drawCards(2, source, game); - return true; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TriadOfFates.java b/Mage.Sets/src/mage/cards/t/TriadOfFates.java index c8022edb232..f2b5f392f1c 100644 --- a/Mage.Sets/src/mage/cards/t/TriadOfFates.java +++ b/Mage.Sets/src/mage/cards/t/TriadOfFates.java @@ -5,7 +5,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardTargetControllerEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.ExileThenReturnTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -15,9 +15,6 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; @@ -62,7 +59,7 @@ public final class TriadOfFates extends CardImpl { ability.addCost(new TapSourceCost()); target = new TargetCreaturePermanent(filterCounter); ability.addTarget(target); - ability.addEffect(new DrawCardControllerTargetEffect()); + ability.addEffect(new DrawCardTargetControllerEffect(2)); this.addAbility(ability); } @@ -75,32 +72,3 @@ public final class TriadOfFates extends CardImpl { return new TriadOfFates(this); } } - -class DrawCardControllerTargetEffect extends OneShotEffect { - - DrawCardControllerTargetEffect() { - super(Outcome.Benefit); - this.staticText = "Its controller draws two cards"; - } - - private DrawCardControllerTargetEffect(final DrawCardControllerTargetEffect effect) { - super(effect); - } - - @Override - public DrawCardControllerTargetEffect copy() { - return new DrawCardControllerTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent creature = (Permanent) game.getLastKnownInformation(this.getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD); - if (creature != null) { - Player controllerOfTarget = game.getPlayer(creature.getControllerId()); - if (controllerOfTarget != null) { - controllerOfTarget.drawCards(2, source, game); - } - } - return false; - } -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/CryptGhastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/CryptGhastTest.java index 2d2f2634d9b..568571acff2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/CryptGhastTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/CryptGhastTest.java @@ -64,11 +64,14 @@ public class CryptGhastTest extends CardTestPlayerBase { // Without Crypt Ghast, the land won't give extra mana checkPlayableAbility("Not enough mana", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Erebos's", false); + setChoice(playerA, true); + setStrictChooseMode(true); setStopAt(3, PhaseStep.PRECOMBAT_MAIN); execute(); assertTapped("Nin, the Pain Artist", true); assertExileCount("Crypt Ghast", 1); + assertHandCount(playerA, 1 + 2 + 1); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/eve/DreamFractureTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/eve/DreamFractureTest.java new file mode 100644 index 00000000000..9d02013bc63 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/eve/DreamFractureTest.java @@ -0,0 +1,67 @@ +package org.mage.test.cards.single.eve; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author xenohedron + */ +public class DreamFractureTest extends CardTestPlayerBase { + + private static final String dreamFracture = "Dream Fracture"; + private static final String shock = "Shock"; + private static final String lorescale = "Lorescale Coatl"; // When you draw a card, +1/+1 counter + + @Test + public void testSpellOpponent() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + addCard(Zone.HAND, playerA, dreamFracture); + addCard(Zone.HAND, playerB, shock); + addCard(Zone.BATTLEFIELD, playerA, lorescale); + addCard(Zone.BATTLEFIELD, playerB, lorescale); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, shock, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dreamFracture, shock, shock); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, dreamFracture, 1); + assertGraveyardCount(playerB, shock, 1); + assertCounterCount(playerA, lorescale, CounterType.P1P1, 1); + assertCounterCount(playerB, lorescale, CounterType.P1P1, 1); + assertLife(playerA, 20); + assertLife(playerB, 20); + } + + @Test + public void testSpellOwn() { + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 4); + addCard(Zone.HAND, playerA, dreamFracture); + addCard(Zone.HAND, playerA, shock); + addCard(Zone.BATTLEFIELD, playerA, lorescale); + addCard(Zone.BATTLEFIELD, playerB, lorescale); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shock, playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dreamFracture, shock, shock); + + setChoice(playerA, "Whenever you draw"); // order triggers + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, dreamFracture, 1); + assertGraveyardCount(playerA, shock, 1); + assertCounterCount(playerA, lorescale, CounterType.P1P1, 2); + assertCounterCount(playerB, lorescale, CounterType.P1P1, 0); + assertLife(playerA, 20); + assertLife(playerB, 20); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/war/ObNixilisTheHateTwistedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/war/ObNixilisTheHateTwistedTest.java new file mode 100644 index 00000000000..54ee16dd518 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/war/ObNixilisTheHateTwistedTest.java @@ -0,0 +1,65 @@ +package org.mage.test.cards.single.war; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author xenohedron + */ +public class ObNixilisTheHateTwistedTest extends CardTestPlayerBase { + + /* Whenever an opponent draws a card, Ob Nixilis, the Hate-Twisted deals 1 damage to that player. + −2: Destroy target creature. Its controller draws two cards. + */ + private static final String nixilis = "Ob Nixilis, the Hate-Twisted"; + private static final String ghoul = "Warpath Ghoul"; + + @Test + public void testNixilis() { + addCard(Zone.BATTLEFIELD, playerA, nixilis); + addCard(Zone.BATTLEFIELD, playerB, ghoul); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2: Destroy", ghoul); + setChoice(playerA, "Whenever"); // order triggers + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, ghoul, 1); + assertHandCount(playerA, 0); + assertHandCount(playerB, 2); + assertCounterCount(playerA, nixilis, CounterType.LOYALTY, 3); + assertLife(playerA, 20); + assertLife(playerB, 18); + } + + @Test + public void testNixilisControlChanged() { + String threaten = "Act of Treason"; + + addCard(Zone.BATTLEFIELD, playerA, nixilis); + addCard(Zone.BATTLEFIELD, playerB, ghoul); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, threaten); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, threaten, ghoul); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "-2: Destroy", ghoul); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, ghoul, 1); + assertHandCount(playerA, 2); + assertHandCount(playerB, 0); + assertCounterCount(playerA, nixilis, CounterType.LOYALTY, 3); + assertLife(playerA, 20); + assertLife(playerB, 20); + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawCardTargetControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawCardTargetControllerEffect.java new file mode 100644 index 00000000000..a6bdb9eee64 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/DrawCardTargetControllerEffect.java @@ -0,0 +1,61 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * @author xenohedron + */ +public class DrawCardTargetControllerEffect extends OneShotEffect { + + protected DynamicValue amount; + + public DrawCardTargetControllerEffect(int amount) { + this(StaticValue.get(amount)); + this.staticText = "its controller draws " + CardUtil.numberToText(amount, "a") + + (amount == 1 ? " card" : " cards"); + } + + public DrawCardTargetControllerEffect(DynamicValue amount) { + super(Outcome.DrawCard); + this.amount = amount; + } + + protected DrawCardTargetControllerEffect(final DrawCardTargetControllerEffect effect) { + super(effect); + amount = effect.amount.copy(); + } + + @Override + public DrawCardTargetControllerEffect copy() { + return new DrawCardTargetControllerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player targetController = null; + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (permanent != null) { + targetController = game.getPlayer(permanent.getControllerId()); + } else { + Spell spell = game.getSpellOrLKIStack(getTargetPointer().getFirst(game, source)); + if (spell != null) { + targetController = game.getPlayer(spell.getControllerId()); + } + } + if (targetController != null) { + targetController.drawCards(amount.calculate(game, source, this), source, game); + return true; + } + return false; + } + +}