[WOE] Implement Stroke of midnight (refactor together similar effects) (#10834)

* [WOE] Implement Stroke of Midnight

* refactor similar effects together.

* add tests (and fix a bug)

---------

Co-authored-by: Evan Kranzler <theelk801@gmail.com>
This commit is contained in:
Susucre 2023-08-19 00:27:36 +02:00 committed by GitHub
parent c6b49253c9
commit 43de68afe3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 324 additions and 582 deletions

View file

@ -1,18 +1,15 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.SpiritWhiteToken;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author Backfir3
@ -26,7 +23,7 @@ public final class Afterlife extends CardImpl {
// 1/1 white Spirit creature token with flying.
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
this.getSpellAbility().addEffect(new DestroyTargetEffect(true));
this.getSpellAbility().addEffect(new AfterlifeEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new SpiritWhiteToken()));
}
private Afterlife(final Afterlife card) {
@ -37,32 +34,4 @@ public final class Afterlife extends CardImpl {
public Afterlife copy() {
return new Afterlife(this);
}
}
class AfterlifeEffect extends OneShotEffect {
public AfterlifeEffect() {
super(Outcome.PutCreatureInPlay);
staticText = "Its controller creates a 1/1 white Spirit creature token with flying";
}
public AfterlifeEffect(final AfterlifeEffect effect) {
super(effect);
}
@Override
public AfterlifeEffect copy() {
return new AfterlifeEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source);
if (permanent != null) {
SpiritWhiteToken token = new SpiritWhiteToken();
token.putOntoBattlefield(1, game, source, permanent.getControllerId());
}
return true;
}
}
}

View file

@ -1,19 +1,15 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.AngelToken;
import mage.game.permanent.token.Token;
import mage.target.common.TargetCreatureOrPlaneswalker;
import java.util.UUID;
/**
* @author TheElk801
*/
@ -24,7 +20,7 @@ public final class AngelicAscension extends CardImpl {
// Exile target creature or planeswalker. Its controller creates a 4/4 white Angel creature token with flying.
this.getSpellAbility().addEffect(new ExileTargetEffect());
this.getSpellAbility().addEffect(new AngelicAscensionEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new AngelToken()));
this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker());
}
@ -36,32 +32,4 @@ public final class AngelicAscension extends CardImpl {
public AngelicAscension copy() {
return new AngelicAscension(this);
}
}
class AngelicAscensionEffect extends OneShotEffect {
private static final Token token = new AngelToken();
AngelicAscensionEffect() {
super(Outcome.PutCreatureInPlay);
this.staticText = "Its controller creates a 4/4 white Angel creature token with flying";
}
private AngelicAscensionEffect(final AngelicAscensionEffect effect) {
super(effect);
}
@Override
public AngelicAscensionEffect copy() {
return new AngelicAscensionEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source);
if (permanent == null) {
return false;
}
return token.putOntoBattlefield(1, game, source, permanent.getControllerId());
}
}
}

View file

@ -1,14 +1,10 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.BeastToken;
import mage.target.TargetPermanent;
@ -25,7 +21,7 @@ public final class BeastWithin extends CardImpl {
// Destroy target permanent. Its controller creates a 3/3 green Beast creature token.
this.getSpellAbility().addTarget(new TargetPermanent());
this.getSpellAbility().addEffect(new DestroyTargetEffect());
this.getSpellAbility().addEffect(new BeastWithinEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new BeastToken()));
}
private BeastWithin(final BeastWithin card) {
@ -36,34 +32,4 @@ public final class BeastWithin extends CardImpl {
public BeastWithin copy() {
return new BeastWithin(this);
}
}
class BeastWithinEffect extends OneShotEffect {
public BeastWithinEffect() {
super(Outcome.Detriment);
staticText = "Its controller creates a 3/3 green Beast creature token";
}
public BeastWithinEffect(final BeastWithinEffect effect) {
super(effect);
}
@Override
public BeastWithinEffect copy() {
return new BeastWithinEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
// If the permanent is an illegal target when Beast Within tries to resolve, the spell wont resolve and none
// of its effects will happen. The permanents controller wont get a Beast token.
// (2011-06-01)
Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); // must use LKI
if (permanent != null) {
new BeastToken().putOntoBattlefield(1, game, source, permanent.getControllerId());
}
return true;
}
}
}

View file

@ -1,14 +1,10 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.TreasureToken;
import mage.target.common.TargetNonlandPermanent;
@ -24,7 +20,7 @@ public final class BuyYourSilence extends CardImpl {
// Exile target nonland permanent. Its controller creates a Treasure token.
this.getSpellAbility().addEffect(new ExileTargetEffect());
this.getSpellAbility().addEffect(new BuyYourSilenceEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new TreasureToken()));
this.getSpellAbility().addTarget(new TargetNonlandPermanent());
}
@ -36,30 +32,4 @@ public final class BuyYourSilence extends CardImpl {
public BuyYourSilence copy() {
return new BuyYourSilence(this);
}
}
class BuyYourSilenceEffect extends OneShotEffect {
BuyYourSilenceEffect() {
super(Outcome.Benefit);
staticText = "Its controller creates a Treasure token";
}
private BuyYourSilenceEffect(final BuyYourSilenceEffect effect) {
super(effect);
}
@Override
public BuyYourSilenceEffect copy() {
return new BuyYourSilenceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source);
if (permanent == null) {
return false;
}
return new TreasureToken().putOntoBattlefield(1, game, source, permanent.getControllerId());
}
}
}

View file

@ -4,25 +4,19 @@ import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenTargetEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.FilterCard;
import mage.filter.common.FilterArtifactOrEnchantmentCard;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.GolemToken;
import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.common.TargetNonlandPermanent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
@ -46,7 +40,8 @@ public final class CavalierOfDawn extends CardImpl {
this.addAbility(VigilanceAbility.getInstance());
// When Cavalier of Dawn enters the battlefield, destroy up to one target nonland permanent. Its controller creates a 3/3 colorless Golem artifact creature token.
Ability ability = new EntersBattlefieldTriggeredAbility(new CavalierOfDawnEffect());
Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect());
ability.addEffect(new CreateTokenControllerTargetPermanentEffect(new GolemToken()));
ability.addTarget(new TargetNonlandPermanent(0, 1, false));
this.addAbility(ability);
@ -64,38 +59,4 @@ public final class CavalierOfDawn extends CardImpl {
public CavalierOfDawn copy() {
return new CavalierOfDawn(this);
}
}
class CavalierOfDawnEffect extends OneShotEffect {
CavalierOfDawnEffect() {
super(Outcome.Benefit);
staticText = "destroy up to one target nonland permanent. " +
"Its controller creates a 3/3 colorless Golem artifact creature token.";
}
private CavalierOfDawnEffect(final CavalierOfDawnEffect effect) {
super(effect);
}
@Override
public CavalierOfDawnEffect copy() {
return new CavalierOfDawnEffect(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, false);
if (player == null) {
return false;
}
Effect effect = new CreateTokenTargetEffect(new GolemToken());
effect.setTargetPointer(new FixedTarget(player.getId(), game));
return effect.apply(game, source);
}
}

View file

@ -1,25 +1,25 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.keyword.UnearthAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.PowerstoneToken;
import mage.target.common.TargetNonlandPermanent;
import java.util.UUID;
/**
*
* @author weirddan455
@ -38,6 +38,7 @@ public final class CityscapeLeveler extends CardImpl {
// When you cast this spell and whenever Cityscape Leveler attacks, destroy up to one target nonland permanent. Its controller creates a tapped Powerstone token.
Ability ability = new CityscapeLevelerAbility();
ability.addEffect(new CreateTokenControllerTargetPermanentEffect(new PowerstoneToken(), 1, true));
ability.addTarget(new TargetNonlandPermanent(0, 1));
this.addAbility(ability);
@ -58,7 +59,7 @@ public final class CityscapeLeveler extends CardImpl {
class CityscapeLevelerAbility extends TriggeredAbilityImpl {
public CityscapeLevelerAbility() {
super(Zone.ALL, new CityscapeLevelerEffect());
super(Zone.ALL, new DestroyTargetEffect());
setTriggerPhrase("When you cast this spell and whenever {this} attacks, ");
}
@ -96,33 +97,4 @@ class CityscapeLevelerAbility extends TriggeredAbilityImpl {
return false;
}
}
}
class CityscapeLevelerEffect extends OneShotEffect {
public CityscapeLevelerEffect() {
super(Outcome.DestroyPermanent);
this.staticText = "destroy up to one target nonland permanent. Its controller creates a tapped Powerstone token.";
}
private CityscapeLevelerEffect(final CityscapeLevelerEffect effect) {
super(effect);
}
@Override
public CityscapeLevelerEffect copy() {
return new CityscapeLevelerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getFirstTarget());
if (permanent == null) {
return false;
}
UUID controllerId = permanent.getControllerId();
permanent.destroy(source, game);
new PowerstoneToken().putOntoBattlefield(1, game, source, controllerId, true, false);
return true;
}
}
}

View file

@ -1,22 +1,18 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.keyword.ChangelingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.CribSwapShapeshifterWhiteToken;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author LevelX2
@ -31,8 +27,8 @@ public final class CribSwap extends CardImpl {
this.addAbility(new ChangelingAbility());
// Exile target creature. Its controller creates a 1/1 colorless Shapeshifter creature token with changeling.
this.getSpellAbility().addEffect(new ExileTargetEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new CribSwapShapeshifterWhiteToken()));
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
this.getSpellAbility().addEffect(new CribSwapEffect());
}
@ -44,34 +40,4 @@ public final class CribSwap extends CardImpl {
public CribSwap copy() {
return new CribSwap(this);
}
}
class CribSwapEffect extends OneShotEffect {
public CribSwapEffect() {
super(Outcome.Benefit);
this.staticText = "Its controller creates a 1/1 colorless Shapeshifter creature token with changeling";
}
public CribSwapEffect(final CribSwapEffect effect) {
super(effect);
}
@Override
public CribSwapEffect copy() {
return new CribSwapEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent targetCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source);
if (targetCreature != null) {
CribSwapShapeshifterWhiteToken token = new CribSwapShapeshifterWhiteToken();
return token.putOntoBattlefield(1, game, source, targetCreature.getControllerId());
}
}
return false;
}
}
}

View file

@ -1,29 +1,25 @@
package mage.cards.g;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.common.TapTargetCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenTargetEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
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.filter.common.FilterControlledPermanent;
import mage.filter.predicate.permanent.TappedPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.SpiritWhiteToken;
import mage.players.Player;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
/**
* @author noxx
@ -38,10 +34,11 @@ public final class GallowsAtWillowHill extends CardImpl {
}
public GallowsAtWillowHill(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// {3}, {tap}, Tap three untapped Humans you control: Destroy target creature. Its controller creates a 1/1 white Spirit creature token with flying.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GallowsAtWillowHillEffect(), new GenericManaCost(3));
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new GenericManaCost(3));
ability.addEffect(new CreateTokenControllerTargetPermanentEffect(new SpiritWhiteToken()));
ability.addCost(new TapSourceCost());
ability.addCost(new TapTargetCost(new TargetControlledPermanent(3, 3, humanFilter, false)));
ability.addTarget(new TargetCreaturePermanent());
@ -56,37 +53,4 @@ public final class GallowsAtWillowHill extends CardImpl {
public GallowsAtWillowHill copy() {
return new GallowsAtWillowHill(this);
}
}
class GallowsAtWillowHillEffect extends OneShotEffect {
public GallowsAtWillowHillEffect() {
super(Outcome.DestroyPermanent);
staticText = "Destroy target creature. Its controller creates a 1/1 white Spirit creature token with flying";
}
public GallowsAtWillowHillEffect(final GallowsAtWillowHillEffect effect) {
super(effect);
}
@Override
public GallowsAtWillowHillEffect copy() {
return new GallowsAtWillowHillEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source));
if (permanent != null) {
Player controller = game.getPlayer(permanent.getControllerId());
permanent.destroy(source, game, false);
if (controller != null) {
CreateTokenTargetEffect effect = new CreateTokenTargetEffect(new SpiritWhiteToken());
effect.setTargetPointer(new FixedTarget(controller.getId()));
effect.apply(game, source);
}
return true;
}
return false;
}
}
}

View file

@ -1,19 +1,12 @@
package mage.cards.g;
import mage.abilities.Ability;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenTargetEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.ElephantToken;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
@ -26,7 +19,8 @@ public final class GenerousGift extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}");
// Destroy target permanent. Its controller creates a 3/3 green Elephant creature token.
this.getSpellAbility().addEffect(new GenerousGiftEffect());
this.getSpellAbility().addEffect(new DestroyTargetEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new ElephantToken()));
this.getSpellAbility().addTarget(new TargetPermanent());
}
@ -38,37 +32,4 @@ public final class GenerousGift extends CardImpl {
public GenerousGift copy() {
return new GenerousGift(this);
}
}
class GenerousGiftEffect extends OneShotEffect {
GenerousGiftEffect() {
super(Outcome.Benefit);
staticText = "Destroy target permanent. Its controller creates a 3/3 green Elephant creature token.";
}
private GenerousGiftEffect(final GenerousGiftEffect effect) {
super(effect);
}
@Override
public GenerousGiftEffect copy() {
return new GenerousGiftEffect(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, false);
if (player == null) {
return false;
}
Effect effect = new CreateTokenTargetEffect(new ElephantToken());
effect.setTargetPointer(new FixedTarget(player.getId(), game));
return effect.apply(game, source);
}
}

View file

@ -1,19 +1,16 @@
package mage.cards.p;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.ApeToken;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author dustinconrad
@ -25,7 +22,7 @@ public final class Pongify extends CardImpl {
// Destroy target creature. It can't be regenerated. That creature's controller creates a 3/3 green Ape creature token.
this.getSpellAbility().addEffect(new DestroyTargetEffect(true));
this.getSpellAbility().addEffect(new PongifyEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new ApeToken()));
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
}
@ -37,37 +34,4 @@ public final class Pongify extends CardImpl {
public Pongify copy() {
return new Pongify(this);
}
}
class PongifyEffect extends OneShotEffect {
public PongifyEffect() {
super(Outcome.PutCreatureInPlay);
this.staticText = "Its controller creates a 3/3 green Ape creature token";
}
public PongifyEffect(final PongifyEffect effect) {
super(effect);
}
@Override
public PongifyEffect copy() {
return new PongifyEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
UUID targetId = getTargetPointer().getFirst(game, source);
if (targetId != null) {
Permanent permanent = game.getPermanentOrLKIBattlefield(targetId);
if (permanent != null) {
UUID controllerId = permanent.getControllerId();
if (controllerId != null) {
new ApeToken().putOntoBattlefield(1, game, source, controllerId);
return true;
}
}
}
return false;
}
}
}

View file

@ -1,18 +1,13 @@
package mage.cards.r;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.keyword.ForetellAbility;
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.game.permanent.Permanent;
import mage.game.permanent.token.BlueBirdToken;
import mage.players.Player;
import mage.target.TargetPermanent;
import java.util.UUID;
@ -26,7 +21,8 @@ public final class Ravenform extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}");
// Exile target artifact or creature. Its controller creates a 1/1 blue Bird creature token with flying.
this.getSpellAbility().addEffect(new RavenformEffect());
this.getSpellAbility().addEffect(new ExileTargetEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new BlueBirdToken()));
this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE));
// Foretell {U}
@ -41,34 +37,4 @@ public final class Ravenform extends CardImpl {
public Ravenform copy() {
return new Ravenform(this);
}
}
class RavenformEffect extends OneShotEffect {
RavenformEffect() {
super(Outcome.PutCreatureInPlay);
staticText = "Exile target artifact or creature. " +
"Its controller creates a 1/1 blue Bird creature token with flying.";
}
private RavenformEffect(final RavenformEffect effect) {
super(effect);
}
@Override
public RavenformEffect copy() {
return new RavenformEffect(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());
player.moveCards(permanent, Zone.EXILED, source, game);
new BlueBirdToken().putOntoBattlefield(1, game, source, player.getId());
return true;
}
}
}

View file

@ -1,17 +1,12 @@
package mage.cards.r;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
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.game.permanent.token.Spirit32Token;
import mage.players.Player;
import mage.target.common.TargetNonlandPermanent;
import java.util.UUID;
@ -27,7 +22,8 @@ public final class ReduceToMemory extends CardImpl {
this.subtype.add(SubType.LESSON);
// Exile target nonland permanent. Its controller creates a 3/2 red and white spirit creature token.
this.getSpellAbility().addEffect(new ReduceToMemoryEffect());
this.getSpellAbility().addEffect(new ExileTargetEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new Spirit32Token()));
this.getSpellAbility().addTarget(new TargetNonlandPermanent());
}
@ -39,33 +35,4 @@ public final class ReduceToMemory extends CardImpl {
public ReduceToMemory copy() {
return new ReduceToMemory(this);
}
}
class ReduceToMemoryEffect extends OneShotEffect {
ReduceToMemoryEffect() {
super(Outcome.Benefit);
staticText = "exile target nonland permanent. Its controller creates a 3/2 red and white Spirit creature token";
}
private ReduceToMemoryEffect(final ReduceToMemoryEffect effect) {
super(effect);
}
@Override
public ReduceToMemoryEffect copy() {
return new ReduceToMemoryEffect(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());
player.moveCards(permanent, Zone.EXILED, source, game);
new Spirit32Token().putOntoBattlefield(1, game, source, player.getId());
return true;
}
}
}

View file

@ -1,17 +1,12 @@
package mage.cards.r;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
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.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.Elemental44Token;
import mage.players.Player;
import mage.target.TargetPermanent;
import java.util.UUID;
@ -25,7 +20,8 @@ public final class Resculpt extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}");
// Exile target artifact or creature. Its controller creates a 4/4 blue and red Elemental creature token.
this.getSpellAbility().addEffect(new ResculptEffect());
this.getSpellAbility().addEffect(new ExileTargetEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new Elemental44Token()));
this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE));
}
@ -37,34 +33,4 @@ public final class Resculpt extends CardImpl {
public Resculpt copy() {
return new Resculpt(this);
}
}
class ResculptEffect extends OneShotEffect {
ResculptEffect() {
super(Outcome.PutCreatureInPlay);
staticText = "Exile target artifact or creature. " +
"Its controller creates a 4/4 blue and red Elemental creature token";
}
private ResculptEffect(final ResculptEffect effect) {
super(effect);
}
@Override
public ResculptEffect copy() {
return new ResculptEffect(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());
player.moveCards(permanent, Zone.EXILED, source, game);
new Elemental44Token().putOntoBattlefield(1, game, source, player.getId());
return true;
}
}
}

View file

@ -5,7 +5,9 @@ import mage.abilities.Ability;
import mage.abilities.common.MutatesSourceTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.CreateTokenTargetEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.keyword.MutateAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
@ -49,7 +51,8 @@ public final class SawtuskDemolisher extends CardImpl {
this.addAbility(TrampleAbility.getInstance());
// Whenever this creature mutates, destroy target noncreature permanent. Its controller creates a 3/3 green Beast creature token.
Ability ability = new MutatesSourceTriggeredAbility(new SawtuskDemolisherEffect());
Ability ability = new MutatesSourceTriggeredAbility(new DestroyTargetEffect());
ability.addEffect(new CreateTokenControllerTargetPermanentEffect(new BeastToken()));
ability.addTarget(new TargetPermanent(filter));
this.addAbility(ability);
}

View file

@ -1,16 +1,11 @@
package mage.cards.s;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.SoldierToken;
import mage.game.permanent.token.Token;
import mage.target.common.TargetNonlandPermanent;
import java.util.UUID;
@ -25,7 +20,7 @@ public final class SecureTheScene extends CardImpl {
// Exile target nonland permanent. Its controller creates a 1/1 white Soldier creature token.
this.getSpellAbility().addEffect(new ExileTargetEffect());
this.getSpellAbility().addEffect(new SecureTheSceneEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new SoldierToken()));
this.getSpellAbility().addTarget(new TargetNonlandPermanent());
}
@ -37,32 +32,4 @@ public final class SecureTheScene extends CardImpl {
public SecureTheScene copy() {
return new SecureTheScene(this);
}
}
class SecureTheSceneEffect extends OneShotEffect {
private static final Token token = new SoldierToken();
SecureTheSceneEffect() {
super(Outcome.PutCreatureInPlay);
this.staticText = "Its controller creates a 1/1 white Soldier creature token";
}
private SecureTheSceneEffect(final SecureTheSceneEffect effect) {
super(effect);
}
@Override
public SecureTheSceneEffect copy() {
return new SecureTheSceneEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source);
if (permanent == null) {
return false;
}
return token.putOntoBattlefield(1, game, source, permanent.getControllerId());
}
}
}

View file

@ -0,0 +1,36 @@
package mage.cards.s;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.game.permanent.token.HumanToken;
import mage.target.common.TargetNonlandPermanent;
import java.util.UUID;
/**
* @author Susucr
*/
public final class StrokeOfMidnight extends CardImpl {
public StrokeOfMidnight(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}");
// Destroy target nonland permanent. Its controller creates a 1/1 white Human creature token.
this.getSpellAbility().addEffect(new DestroyTargetEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new HumanToken()));
this.getSpellAbility().addTarget(new TargetNonlandPermanent());
}
private StrokeOfMidnight(final StrokeOfMidnight card) {
super(card);
}
@Override
public StrokeOfMidnight copy() {
return new StrokeOfMidnight(this);
}
}

View file

@ -1,28 +1,23 @@
package mage.cards.t;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.ActivateAsSorceryActivatedAbility;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenTargetEffect;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
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.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.OxToken;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
/**
*
@ -42,9 +37,10 @@ public final class TransmogrifyingWand extends CardImpl {
// {1}, {T}, Remove a charge counter from Transmogrifying Wand: Destroy target creature. Its controller creates a 2/4 white Ox creature token. Activate this ability only any time you could cast a sorcery.
Ability ability = new ActivateAsSorceryActivatedAbility(
Zone.BATTLEFIELD,
new TransmogrifyingWandEffect(),
new DestroyTargetEffect(),
new GenericManaCost(1)
);
ability.addEffect(new CreateTokenControllerTargetPermanentEffect(new OxToken()));
ability.addCost(new TapSourceCost());
ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance()));
ability.addTarget(new TargetCreaturePermanent());
@ -59,33 +55,4 @@ public final class TransmogrifyingWand extends CardImpl {
public TransmogrifyingWand copy() {
return new TransmogrifyingWand(this);
}
}
class TransmogrifyingWandEffect extends OneShotEffect {
public TransmogrifyingWandEffect() {
super(Outcome.Benefit);
this.staticText = "Destroy target creature. Its controller creates a 2/4 white Ox creature token.";
}
public TransmogrifyingWandEffect(final TransmogrifyingWandEffect effect) {
super(effect);
}
@Override
public TransmogrifyingWandEffect copy() {
return new TransmogrifyingWandEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent creature = game.getPermanent(source.getFirstTarget());
if (creature == null) {
return false;
}
Effect effect = new CreateTokenTargetEffect(new OxToken());
effect.setTargetPointer(new FixedTarget(creature.getControllerId(), game));
new DestroyTargetEffect().apply(game, source);
return effect.apply(game, source);
}
}
}

View file

@ -88,6 +88,7 @@ public final class WildsOfEldraine extends ExpansionSet {
cards.add(new SetCardInfo("Sharae of Numbing Depths", 213, Rarity.UNCOMMON, mage.cards.s.SharaeOfNumbingDepths.class));
cards.add(new SetCardInfo("Skybeast Tracker", 185, Rarity.COMMON, mage.cards.s.SkybeastTracker.class));
cards.add(new SetCardInfo("Sleight of Hand", 67, Rarity.COMMON, mage.cards.s.SleightOfHand.class));
cards.add(new SetCardInfo("Stroke of Midnight", 33, Rarity.UNCOMMON, mage.cards.s.StrokeOfMidnight.class));
cards.add(new SetCardInfo("Soul-Guide Lantern", 251, Rarity.UNCOMMON, mage.cards.s.SoulGuideLantern.class));
cards.add(new SetCardInfo("Spellbook Vendor", 31, Rarity.RARE, mage.cards.s.SpellbookVendor.class));
cards.add(new SetCardInfo("Spiteful Hexmage", 108, Rarity.RARE, mage.cards.s.SpitefulHexmage.class));

View file

@ -0,0 +1,77 @@
package org.mage.test.cards.single.woe;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class StrokeOfMidnightTest extends CardTestPlayerBase {
/**
* Stroke of Midnight
* {2}{W}
* Instant
* <p>
* Destroy target nonland permanent. Its controller creates a 1/1 white Human creature token.
*/
private static final String stroke = "Stroke of Midnight";
// Indestructible permanent. Should still give a token.
private static final String relic = "Darksteel Relic";
// 2/1 creature
private static final String piker = "Goblin Piker";
@Test
public void destroyOpponentPermanent() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerA, stroke);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
addCard(Zone.BATTLEFIELD, playerB, piker);
castSpell(1, PhaseStep.UPKEEP, playerA, stroke, piker);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertPermanentCount(playerB, piker, 0);
assertGraveyardCount(playerB, piker, 1);
assertTokenCount(playerB, "Human Token", 1);
}
@Test
public void destroyOwnPermanent() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerA, stroke);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
addCard(Zone.BATTLEFIELD, playerA, piker);
castSpell(1, PhaseStep.UPKEEP, playerA, stroke, piker);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertPermanentCount(playerA, piker, 0);
assertGraveyardCount(playerA, piker, 1);
assertTokenCount(playerA, "Human Token", 1);
}
@Test
public void indestructiblePermanent() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerA, stroke);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
addCard(Zone.BATTLEFIELD, playerB, relic);
castSpell(1, PhaseStep.UPKEEP, playerA, stroke, relic);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertPermanentCount(playerB, relic, 1);
assertTokenCount(playerB, "Human Token", 1);
}
}

View file

@ -0,0 +1,131 @@
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.permanent.token.Token;
import mage.players.Player;
import mage.util.CardUtil;
/**
* @Author Susucr
* <p>
* Have the Controller of target permanent (or LKI controller) create Tokens.
*/
public class CreateTokenControllerTargetPermanentEffect extends OneShotEffect {
private final Token token;
private final DynamicValue amount;
private final boolean tapped;
private final boolean attacking;
public CreateTokenControllerTargetPermanentEffect(Token token) {
this(token, StaticValue.get(1));
}
public CreateTokenControllerTargetPermanentEffect(Token token, int amount) {
this(token, StaticValue.get(amount));
}
public CreateTokenControllerTargetPermanentEffect(Token token, DynamicValue amount) {
this(token, amount, false, false);
}
public CreateTokenControllerTargetPermanentEffect(Token token, int amount, boolean tapped) {
this(token, amount, tapped, false);
}
public CreateTokenControllerTargetPermanentEffect(Token token, int amount, boolean tapped, boolean attacking) {
this(token, StaticValue.get(amount), tapped, attacking);
}
public CreateTokenControllerTargetPermanentEffect(Token token, DynamicValue amount, boolean tapped, boolean attacking) {
super(Outcome.Neutral);
this.token = token;
this.amount = amount.copy();
this.tapped = tapped;
this.attacking = attacking;
this.staticText = setText();
}
protected CreateTokenControllerTargetPermanentEffect(final CreateTokenControllerTargetPermanentEffect effect) {
super(effect);
this.token = effect.token.copy();
this.amount = effect.amount.copy();
this.tapped = effect.tapped;
this.attacking = effect.attacking;
}
@Override
public CreateTokenControllerTargetPermanentEffect copy() {
return new CreateTokenControllerTargetPermanentEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source);
if (creature != null) {
Player controllerOfTarget = game.getPlayer(creature.getControllerId());
if (controllerOfTarget != null) {
int value = amount.calculate(game, source, this);
return token.putOntoBattlefield(value, game, source, controllerOfTarget.getId(), tapped, attacking);
}
}
return false;
}
private String setText() {
String text = "Its controller creates ";
if (token.getDescription().contains(", a legendary")) {
text += token.getDescription();
return text;
}
if (amount.toString().equals("1")) {
if (tapped && !attacking) {
text += "a tapped " + token.getDescription();
} else {
text += CardUtil.addArticle(token.getDescription());
}
} else {
text += CardUtil.numberToText(amount.toString()) + " ";
if (tapped && !attacking) {
text += "tapped ";
}
text += token.getDescription().replace("token. It has", "tokens. They have");
if (token.getDescription().endsWith("token")) {
text += "s";
}
text.replace("token ", "tokens ");
}
if (attacking) {
if (amount.toString().equals("1")) {
text += " that's";
} else {
text += " that are";
}
if (tapped) {
text += " tapped and";
}
text += " attacking";
}
String message = amount.getMessage();
if (!message.isEmpty()) {
if (amount.toString().equals("X")) {
text += ", where X is ";
} else {
text += " for each ";
}
}
text += message;
return text;
}
}