From 1175a186613c7789cd45ea7116655181657cdc12 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Sep 2015 14:52:14 +0200 Subject: [PATCH] * Fixed put token into play thta's of copy of target effects to handle targets correctly that already copy something (e.g. Kiki-Jiki, Mirror Breaker copying a Body Double creature). --- .../KikiJikiMirrorBreaker.java | 34 ++--- .../sets/commander/RikuOfTwoReflections.java | 47 ++----- .../src/mage/sets/commander/Spawnwrithe.java | 4 +- .../commander2014/FeldonOfTheThirdPath.java | 21 +-- .../sets/conflux/MirrorSigilSergeant.java | 55 +------- .../src/mage/sets/darkascension/Seance.java | 36 ++--- .../sets/dissension/SproutingPhytohydra.java | 7 +- .../sets/dragonsmaze/ProgenitorMimic.java | 66 +-------- .../sets/dragonsoftarkir/CloneLegion.java | 10 +- .../sets/dragonsoftarkir/MirrorMockery.java | 18 +-- .../src/mage/sets/eventide/SpittingImage.java | 9 +- .../sets/fatereforged/FlamerushRider.java | 20 ++- .../mage/sets/fatereforged/SupplantForm.java | 42 +----- .../mage/sets/gatecrash/GiantAdephage.java | 54 +------ .../mage/sets/gatecrash/StolenIdentity.java | 59 +------- .../mage/sets/innistrad/BackFromTheBrink.java | 66 +++------ .../mage/sets/journeyintonyx/Twinflame.java | 46 +++--- .../src/mage/sets/mirrodin/SoulFoundry.java | 9 +- .../sets/mirrodinbesieged/Mirrorworks.java | 133 ++++-------------- .../src/mage/sets/planarchaos/Chronozoa.java | 22 +-- .../mage/sets/returntoravnica/PackRat.java | 33 +++-- .../sets/riseoftheeldrazi/SplinterTwin.java | 40 +++--- .../mage/sets/scarsofmirrodin/MimicVat.java | 32 ++--- .../sets/scarsofmirrodin/MyrPropagator.java | 55 +------- .../mage/sets/shadowmoor/RhysTheRedeemed.java | 14 +- .../sets/shardsofalara/MinionReflector.java | 47 +++---- .../src/mage/sets/thedark/DanceOfMany.java | 56 ++++---- .../src/mage/sets/worldwake/NemesisTrap.java | 39 +++-- .../mage/sets/zendikar/RiteOfReplication.java | 48 +------ .../cards/copy/KikiJikiMirrorBreakerTest.java | 55 ++++++++ .../abilities/effects/CopyCardEffect.java | 51 ------- ...tTokenOntoBattlefieldCopySourceEffect.java | 47 +++++++ .../PutTokenOntoBattlefieldCopySource.java | 94 ------------- ...tTokenOntoBattlefieldCopyTargetEffect.java | 77 +++++++--- Mage/src/mage/game/permanent/Permanent.java | 1 + .../util/functions/CopyTokenFunction.java | 1 + 36 files changed, 477 insertions(+), 971 deletions(-) delete mode 100644 Mage/src/mage/abilities/effects/CopyCardEffect.java create mode 100644 Mage/src/mage/abilities/effects/PutTokenOntoBattlefieldCopySourceEffect.java delete mode 100644 Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java index 2cf5cc1ea2b..8eef2b60f3a 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java @@ -35,6 +35,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.SacrificeTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; @@ -47,10 +48,8 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.SupertypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -111,25 +110,20 @@ class KikiJikiMirrorBreakerEffect extends OneShotEffect { @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); - } - + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - - token.addAbility(HasteAbility.getInstance()); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId()); - sacrificeEffect.setTargetPointer(new FixedTarget(token.getLastAddedToken())); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId()); + sacrificeEffect.setTargetPointer(new FixedTarget(addedToken.getId())); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } return true; } diff --git a/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java b/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java index e975b2db45b..f1d4582bf77 100644 --- a/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java +++ b/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java @@ -30,11 +30,13 @@ package mage.sets.commander; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -48,11 +50,8 @@ import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.permanent.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.game.stack.Spell; import mage.players.Player; -import mage.util.CardUtil; /** * @@ -62,6 +61,7 @@ public class RikuOfTwoReflections extends CardImpl { private static final FilterSpell filter = new FilterSpell("an instant or sorcery spell"); private static final FilterControlledCreaturePermanent filterPermanent = new FilterControlledCreaturePermanent("another nontoken creature"); + static { filter.add(Predicates.or( new CardTypePredicate(CardType.INSTANT), @@ -85,12 +85,10 @@ public class RikuOfTwoReflections extends CardImpl { this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(new RikuOfTwoReflectionsCopyEffect(), new ManaCostsImpl("{U}{R}")), filter, false, true)); // Whenever another nontoken creature enters the battlefield under your control, you may pay {G}{U}. If you do, put a token that's a copy of that creature onto the battlefield. - Ability ability = new EntersBattlefieldAllTriggeredAbility( - Zone.BATTLEFIELD, new RikuOfTwoReflectionsCopyTokenEffect(),filterPermanent, false, SetTargetPointer.PERMANENT, - "Whenever another nontoken creature enters the battlefield under your control, you may pay {G}{U}. If you do, put a token that's a copy of that creature onto the battlefield.", - true); - ability.addCost(new ManaCostsImpl("{G}{U}")); - this.addAbility(ability); + Effect effect = new DoIfCostPaid(new PutTokenOntoBattlefieldCopyTargetEffect(), + new ManaCostsImpl("{G}{U}"), "Put a token that's a copy of that creature onto the battlefield?"); + effect.setText("you may pay {G}{U}. If you do, put a token that's a copy of that creature onto the battlefield"); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, effect, filterPermanent, false, SetTargetPointer.PERMANENT, null)); } public RikuOfTwoReflections(final RikuOfTwoReflections card) { @@ -139,32 +137,3 @@ class RikuOfTwoReflectionsCopyEffect extends OneShotEffect { return new RikuOfTwoReflectionsCopyEffect(this); } } - -class RikuOfTwoReflectionsCopyTokenEffect extends OneShotEffect { - - public RikuOfTwoReflectionsCopyTokenEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "put a token that's a copy of that creature onto the battlefield"; - } - - public RikuOfTwoReflectionsCopyTokenEffect(final RikuOfTwoReflectionsCopyTokenEffect effect) { - super(effect); - } - - @Override - public RikuOfTwoReflectionsCopyTokenEffect copy() { - return new RikuOfTwoReflectionsCopyTokenEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/commander/Spawnwrithe.java b/Mage.Sets/src/mage/sets/commander/Spawnwrithe.java index fed6939173d..9fc589a8482 100644 --- a/Mage.Sets/src/mage/sets/commander/Spawnwrithe.java +++ b/Mage.Sets/src/mage/sets/commander/Spawnwrithe.java @@ -30,7 +30,7 @@ package mage.sets.commander; import java.util.UUID; import mage.MageInt; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.common.PutTokenOntoBattlefieldCopySource; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.constants.CardType; @@ -53,7 +53,7 @@ public class Spawnwrithe extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); // Whenever Spawnwrithe deals combat damage to a player, put a token that's a copy of Spawnwrithe onto the battlefield. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new PutTokenOntoBattlefieldCopySource(), false)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new PutTokenOntoBattlefieldCopySourceEffect(), false)); } diff --git a/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java b/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java index a3c53ae6d78..77840101add 100644 --- a/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java +++ b/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java @@ -36,8 +36,8 @@ import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbil import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.SacrificeTargetEffect; -import mage.abilities.keyword.HasteAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -47,10 +47,8 @@ import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -105,19 +103,12 @@ class FeldonOfTheThirdPathEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { - EmptyToken token = new EmptyToken(); - // TODO: This fails if a card will be copied, that uses adjustTargets() method. - CardUtil.copyTo(token).from(card); - - if (!token.getCardType().contains(CardType.ARTIFACT)) { - token.getCardType().add(CardType.ARTIFACT); - } - token.addAbility(HasteAbility.getInstance()); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - Permanent permanent = game.getPermanent(token.getLastAddedToken()); - if (permanent != null) { + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), CardType.ARTIFACT, true); + effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId()))); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId()); - sacrificeEffect.setTargetPointer(new FixedTarget(permanent, game)); + sacrificeEffect.setTargetPointer(new FixedTarget(addedToken, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); diff --git a/Mage.Sets/src/mage/sets/conflux/MirrorSigilSergeant.java b/Mage.Sets/src/mage/sets/conflux/MirrorSigilSergeant.java index 7361afc4052..76dca6a09b9 100644 --- a/Mage.Sets/src/mage/sets/conflux/MirrorSigilSergeant.java +++ b/Mage.Sets/src/mage/sets/conflux/MirrorSigilSergeant.java @@ -29,28 +29,21 @@ package mage.sets.conflux; import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.game.permanent.token.EmptyToken; -import mage.util.CardUtil; /** * @@ -63,7 +56,7 @@ public class MirrorSigilSergeant extends CardImpl { static { filter.add(new ColorPredicate(ObjectColor.BLUE)); } - + private static final String rule = "At the beginning of your upkeep, if you control a blue permanent, you may put a token that's a copy of Mirror-Sigil Sergeant onto the battlefield."; public MirrorSigilSergeant(UUID ownerId) { @@ -79,7 +72,9 @@ public class MirrorSigilSergeant extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // At the beginning of your upkeep, if you control a blue permanent, you may put a token that's a copy of Mirror-Sigil Sergeant onto the battlefield. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new MirrorSigilSergeantEffect(), TargetController.YOU, true); + Effect effect = new PutTokenOntoBattlefieldCopySourceEffect(); + effect.setText("you may put a token that's a copy of {this} onto the battlefield"); + TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.YOU, true); this.addAbility(new ConditionalTriggeredAbility(ability, new PermanentsOnTheBattlefieldCondition(filter), rule)); } @@ -93,41 +88,3 @@ public class MirrorSigilSergeant extends CardImpl { return new MirrorSigilSergeant(this); } } - -class MirrorSigilSergeantEffect extends OneShotEffect { - - public MirrorSigilSergeantEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "put a token that's a copy of {this} onto the battlefield"; - } - - public MirrorSigilSergeantEffect(final MirrorSigilSergeantEffect effect) { - super(effect); - } - - @Override - public MirrorSigilSergeantEffect copy() { - return new MirrorSigilSergeantEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - UUID targetId = source.getSourceId(); - if (targetId != null && player != null) { - MageObject target = game.getPermanent(targetId); - if (target == null) { - target = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); - } - if (target != null) { - if (target instanceof Permanent) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from((Permanent) target); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/darkascension/Seance.java b/Mage.Sets/src/mage/sets/darkascension/Seance.java index 61a81d26e38..5f848ebe8ab 100644 --- a/Mage.Sets/src/mage/sets/darkascension/Seance.java +++ b/Mage.Sets/src/mage/sets/darkascension/Seance.java @@ -34,6 +34,7 @@ import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -44,11 +45,9 @@ import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -97,28 +96,21 @@ class SeanceEffect extends OneShotEffect { Card card = game.getCard(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && card != null) { - if (controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true)) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card); - - if (!token.hasSubtype("Spirit")) { - token.getSubtype().add("Spirit"); - } - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); - } + if (controller.moveCards(card, null, Zone.EXILED, source, game)) { + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setAdditionalSubType("Spirit"); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); } } - return true; } diff --git a/Mage.Sets/src/mage/sets/dissension/SproutingPhytohydra.java b/Mage.Sets/src/mage/sets/dissension/SproutingPhytohydra.java index 1f941488cf9..29125cb5e58 100644 --- a/Mage.Sets/src/mage/sets/dissension/SproutingPhytohydra.java +++ b/Mage.Sets/src/mage/sets/dissension/SproutingPhytohydra.java @@ -30,7 +30,8 @@ package mage.sets.dissension; import java.util.UUID; import mage.MageInt; import mage.abilities.common.DealtDamageToSourceTriggeredAbility; -import mage.abilities.effects.common.PutTokenOntoBattlefieldCopySource; +import mage.abilities.effects.Effect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.constants.CardType; @@ -54,7 +55,9 @@ public class SproutingPhytohydra extends CardImpl { // Defender this.addAbility(DefenderAbility.getInstance()); // Whenever Sprouting Phytohydra is dealt damage, you may put a token that's a copy of Sprouting Phytohydra onto the battlefield. - this.addAbility(new DealtDamageToSourceTriggeredAbility(Zone.BATTLEFIELD, new PutTokenOntoBattlefieldCopySource(), false)); + Effect effect = new PutTokenOntoBattlefieldCopySourceEffect(); + effect.setText("you may put a token that's a copy of {this} onto the battlefield"); + this.addAbility(new DealtDamageToSourceTriggeredAbility(Zone.BATTLEFIELD, effect, true)); } public SproutingPhytohydra(final SproutingPhytohydra card) { diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java b/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java index 34aacc41a2a..0553d75993b 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java @@ -29,30 +29,22 @@ package mage.sets.dragonsmaze; import java.util.UUID; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.SourceMatchesFilterCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.EntersBattlefieldEffect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CopyEffect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.effects.common.CopyPermanentEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; -import mage.util.CardUtil; import mage.util.functions.AbilityApplier; /** @@ -78,9 +70,11 @@ public class ProgenitorMimic extends CardImpl { // You may have Progenitor Mimic enter the battlefield as a copy of any creature on the battlefield // except it gains "At the beginning of your upkeep, if this creature isn't a token, // put a token onto the battlefield that's a copy of this creature." + Effect effect = new PutTokenOntoBattlefieldCopySourceEffect(); + effect.setText("put a token onto the battlefield that's a copy of this creature"); AbilityApplier applier = new AbilityApplier( new ConditionalTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new ProgenitorMimicCopyEffect(), TargetController.YOU, false), + new BeginningOfUpkeepTriggeredAbility(effect, TargetController.YOU, false), new SourceMatchesFilterCondition(filter), "At the beginning of your upkeep, if this creature isn't a token, put a token onto the battlefield that's a copy of this creature.") ); @@ -100,55 +94,3 @@ public class ProgenitorMimic extends CardImpl { return new ProgenitorMimic(this); } } - -class ProgenitorMimicCopyEffect extends OneShotEffect { - - public ProgenitorMimicCopyEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "put a token onto the battlefield that's a copy of this creature"; - } - - public ProgenitorMimicCopyEffect(final ProgenitorMimicCopyEffect effect) { - super(effect); - } - - @Override - public ProgenitorMimicCopyEffect copy() { - return new ProgenitorMimicCopyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent copyFromPermanent = null; - // handle copies of copies - for (Effect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) { - if (effect instanceof CopyEffect) { - CopyEffect copyEffect = (CopyEffect) effect; - // there is another copy effect that our targetPermanent copies stats from - if (copyEffect.getSourceId().equals(source.getSourceId())) { - MageObject oldBluePrint = ((CopyEffect) effect).getTarget(); - if (oldBluePrint instanceof Permanent) { - // copy it and apply the applier if any - copyFromPermanent = (Permanent) oldBluePrint; - } - } - } - } - if (copyFromPermanent == null) { - // if it was no copy of copy take the target itself - copyFromPermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } - - if (copyFromPermanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(copyFromPermanent); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - Permanent newPermanentToken = game.getPermanent(token.getLastAddedToken()); - if (newPermanentToken != null) { - game.copyPermanent(copyFromPermanent, newPermanentToken, source, null); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/CloneLegion.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/CloneLegion.java index 6225c113df0..bda88f79158 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/CloneLegion.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/CloneLegion.java @@ -30,6 +30,7 @@ package mage.sets.dragonsoftarkir; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -37,10 +38,9 @@ import mage.constants.Rarity; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.TargetPlayer; -import mage.util.CardUtil; +import mage.target.targetpointer.FixedTarget; /** * @@ -91,9 +91,9 @@ class CloneLegionEffect extends OneShotEffect { if (controller != null && targetPlayer != null) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), targetPlayer.getId(), game)) { if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId()); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); } } return true; diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java index 5eb71f49bdf..f43c40388d0 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java @@ -35,6 +35,7 @@ import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.constants.AttachmentType; @@ -43,11 +44,9 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -105,16 +104,13 @@ class MirrorMockeryEffect extends OneShotEffect { } Permanent enchanted = game.getPermanentOrLKIBattlefield(enchantment.getAttachedTo()); if (enchanted != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(enchanted); - - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(enchanted, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + if (addedToken != null) { ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); DelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); diff --git a/Mage.Sets/src/mage/sets/eventide/SpittingImage.java b/Mage.Sets/src/mage/sets/eventide/SpittingImage.java index a9eda3c7deb..08a233a9427 100644 --- a/Mage.Sets/src/mage/sets/eventide/SpittingImage.java +++ b/Mage.Sets/src/mage/sets/eventide/SpittingImage.java @@ -30,6 +30,7 @@ package mage.sets.eventide; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.keyword.RetraceAbility; import mage.cards.CardImpl; import mage.constants.CardType; @@ -45,7 +46,7 @@ import mage.util.CardUtil; /** * * @author jeffwadsworth - + * */ public class SpittingImage extends CardImpl { @@ -54,12 +55,12 @@ public class SpittingImage extends CardImpl { this.expansionSetCode = "EVE"; // Put a token that's a copy of target creature onto the battlefield. - this.getSpellAbility().addEffect(new SpittingImageEffect()); + this.getSpellAbility().addEffect(new PutTokenOntoBattlefieldCopyTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - + // Retrace (You may cast this card from your graveyard by discarding a land card in addition to paying its other costs.) this.addAbility(new RetraceAbility(this)); - + } public SpittingImage(final SpittingImage card) { diff --git a/Mage.Sets/src/mage/sets/fatereforged/FlamerushRider.java b/Mage.Sets/src/mage/sets/fatereforged/FlamerushRider.java index 721d26b3dd4..888ce088d0b 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/FlamerushRider.java +++ b/Mage.Sets/src/mage/sets/fatereforged/FlamerushRider.java @@ -36,6 +36,7 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.keyword.DashAbility; import mage.cards.CardImpl; import mage.constants.CardType; @@ -46,11 +47,9 @@ import mage.filter.predicate.permanent.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -113,16 +112,13 @@ class FlamerushRiderEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); if (controller != null && permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId(), true, true); - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - Effect effect = new ExileTargetEffect(); - effect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(effect), false).apply(game, source); - } + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + Effect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), false).apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/sets/fatereforged/SupplantForm.java b/Mage.Sets/src/mage/sets/fatereforged/SupplantForm.java index 13914d02aac..d71e5462ee2 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/SupplantForm.java +++ b/Mage.Sets/src/mage/sets/fatereforged/SupplantForm.java @@ -28,18 +28,13 @@ package mage.sets.fatereforged; import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; /** * @@ -54,7 +49,9 @@ public class SupplantForm extends CardImpl { // Return target creature to its owner's hand. You put a token onto the battlefield that's a copy of that creature. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new SupplantFormEffect()); + Effect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setText("You put a token onto the battlefield that's a copy of that creature"); + this.getSpellAbility().addEffect(effect); } public SupplantForm(final SupplantForm card) { @@ -66,32 +63,3 @@ public class SupplantForm extends CardImpl { return new SupplantForm(this); } } - -class SupplantFormEffect extends OneShotEffect { - - public SupplantFormEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "You put a token onto the battlefield that's a copy of that creature"; - } - - public SupplantFormEffect(final SupplantFormEffect effect) { - super(effect); - } - - @Override - public SupplantFormEffect copy() { - return new SupplantFormEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent targetPermanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); - if (targetPermanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(targetPermanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/gatecrash/GiantAdephage.java b/Mage.Sets/src/mage/sets/gatecrash/GiantAdephage.java index 67b1fd6ab7c..c0d4b180daf 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/GiantAdephage.java +++ b/Mage.Sets/src/mage/sets/gatecrash/GiantAdephage.java @@ -28,22 +28,13 @@ package mage.sets.gatecrash; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.Rarity; /** * @@ -63,7 +54,7 @@ public class GiantAdephage extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever Giant Adephage deals combat damage to a player, put a token onto the battlefield that is a copy of Giant Adephage. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new GiantAdephageCopyEffect(), false)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new PutTokenOntoBattlefieldCopySourceEffect(), false)); } @@ -76,40 +67,3 @@ public class GiantAdephage extends CardImpl { return new GiantAdephage(this); } } - -class GiantAdephageCopyEffect extends OneShotEffect { - - public GiantAdephageCopyEffect() { - super(Outcome.Copy); - this.staticText = "put a token onto the battlefield that is a copy of Giant Adephage"; - } - - public GiantAdephageCopyEffect(final GiantAdephageCopyEffect effect) { - super(effect); - } - - @Override - public GiantAdephageCopyEffect copy() { - return new GiantAdephageCopyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - MageObject thisCard = game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (thisCard != null && thisCard instanceof Permanent) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from((Permanent)thisCard); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } else { // maybe it's token - Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/gatecrash/StolenIdentity.java b/Mage.Sets/src/mage/sets/gatecrash/StolenIdentity.java index af874fafb0d..6ea81331c7e 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/StolenIdentity.java +++ b/Mage.Sets/src/mage/sets/gatecrash/StolenIdentity.java @@ -28,23 +28,15 @@ package mage.sets.gatecrash; import java.util.UUID; - +import mage.abilities.effects.common.CipherEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; +import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.Zone; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CipherEffect; -import mage.cards.CardImpl; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.TargetPermanent; -import mage.util.CardUtil; /** * @@ -53,17 +45,17 @@ import mage.util.CardUtil; public class StolenIdentity extends CardImpl { private static final FilterPermanent filter = new FilterPermanent("artifact or creature"); + static { filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.CREATURE))); } - + public StolenIdentity(UUID ownerId) { super(ownerId, 53, "Stolen Identity", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{4}{U}{U}"); this.expansionSetCode = "GTC"; - // Put a token onto the battlefield that's a copy of target artifact or creature. - this.getSpellAbility().addEffect(new StolenIdentityEffect()); + this.getSpellAbility().addEffect(new PutTokenOntoBattlefieldCopyTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent(filter)); // Cipher this.getSpellAbility().addEffect(new CipherEffect()); @@ -78,42 +70,3 @@ public class StolenIdentity extends CardImpl { return new StolenIdentity(this); } } - - -class StolenIdentityEffect extends OneShotEffect { - - - - public StolenIdentityEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "Put a token onto the battlefield that's a copy of target artifact or creature"; - } - - public StolenIdentityEffect(final StolenIdentityEffect effect) { - super(effect); - } - - @Override - public StolenIdentityEffect copy() { - return new StolenIdentityEffect(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) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - return true; - } - - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/innistrad/BackFromTheBrink.java b/Mage.Sets/src/mage/sets/innistrad/BackFromTheBrink.java index d088e33c83c..4565580087f 100644 --- a/Mage.Sets/src/mage/sets/innistrad/BackFromTheBrink.java +++ b/Mage.Sets/src/mage/sets/innistrad/BackFromTheBrink.java @@ -28,24 +28,23 @@ package mage.sets.innistrad; import java.util.UUID; - +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.CostImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.abilities.Ability; -import mage.abilities.common.ActivateAsSorceryActivatedAbility; -import mage.abilities.costs.CostImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.players.Player; -import mage.game.permanent.token.EmptyToken; +import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -57,9 +56,10 @@ public class BackFromTheBrink extends CardImpl { super(ownerId, 44, "Back from the Brink", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}{U}"); this.expansionSetCode = "ISD"; - // Exile a creature card from your graveyard and pay its mana cost: Put a token onto the battlefield that's a copy of that card. Activate this ability only any time you could cast a sorcery. - this.addAbility(new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new BackFromTheBrinkEffect(), new BackFromTheBrinkCost())); + Effect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setText("Put a token onto the battlefield that's a copy of that card"); + this.addAbility(new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, effect, new BackFromTheBrinkCost())); } @@ -73,40 +73,12 @@ public class BackFromTheBrink extends CardImpl { } } -class BackFromTheBrinkEffect extends OneShotEffect { - - public BackFromTheBrinkEffect () { - super(Outcome.PutCreatureInPlay); - staticText = "Put a token onto the battlefield that's a copy of that card. Activate this ability only any time you could cast a sorcery"; - } - - public BackFromTheBrinkEffect(final BackFromTheBrinkEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(this.targetPointer.getFirst(game, source)); - if (card != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - return false; - } - - @Override - public BackFromTheBrinkEffect copy() { - return new BackFromTheBrinkEffect(this); - } - -} - class BackFromTheBrinkCost extends CostImpl { public BackFromTheBrinkCost() { - this.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card from your graveyard"))); + Target target = new TargetCardInYourGraveyard(new FilterCreatureCard("creature card from your graveyard")); + target.setNotTarget(true); + this.addTarget(target); this.text = "Exile a creature card from your graveyard and pay its mana cost"; } @@ -127,10 +99,10 @@ class BackFromTheBrinkCost extends CostImpl { @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { if (targets.choose(Outcome.Exile, controllerId, sourceId, game)) { - Player player = game.getPlayer(controllerId); - if (player != null) { - Card card = player.getGraveyard().get(targets.getFirstTarget(), game); - if (card != null && card.moveToZone(Zone.EXILED, sourceId, game, false)) { + Player controller = game.getPlayer(controllerId); + if (controller != null) { + Card card = controller.getGraveyard().get(targets.getFirstTarget(), game); + if (card != null && controller.moveCards(card, null, Zone.EXILED, ability, game)) { ability.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId())); paid = card.getManaCost().pay(ability, game, sourceId, controllerId, noMana); } @@ -139,4 +111,4 @@ class BackFromTheBrinkCost extends CostImpl { return paid; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java b/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java index ae132b9f1a8..da065db84d5 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java @@ -34,7 +34,7 @@ import mage.abilities.abilityword.StriveAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.keyword.HasteAbility; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -42,11 +42,9 @@ import mage.constants.Rarity; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -58,13 +56,12 @@ public class Twinflame extends CardImpl { super(ownerId, 115, "Twinflame", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{R}"); this.expansionSetCode = "JOU"; - // Strive - Twinflame costs 2R more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{R}")); // Choose any number of target creatures you control. For each of them, put a token that's a copy of that creature onto the battlefield. Those tokens have haste. Exile them at the beginning of the next end step. this.getSpellAbility().addEffect(new TwinflameCopyEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, new FilterControlledCreaturePermanent(), false)); - + } public Twinflame(final Twinflame card) { @@ -78,44 +75,43 @@ public class Twinflame extends CardImpl { } class TwinflameCopyEffect extends OneShotEffect { - + public TwinflameCopyEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "Choose any number of target creatures you control. For each of them, put a token that's a copy of that creature onto the battlefield. Those tokens have haste. Exile them at the beginning of the next end step"; } - + public TwinflameCopyEffect(final TwinflameCopyEffect effect) { super(effect); } - + @Override public TwinflameCopyEffect copy() { return new TwinflameCopyEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - for(UUID creatureId: this.getTargetPointer().getTargets(game, source)) { + for (UUID creatureId : this.getTargetPointer().getTargets(game, source)) { Permanent creature = game.getPermanentOrLKIBattlefield(creatureId); if (creature != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(creature); - - token.addAbility(HasteAbility.getInstance()); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(token.getLastAddedToken())); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); - } + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(creature, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } + } } - return true; + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java b/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java index 0ae3d362a29..801eaf76dca 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java +++ b/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java @@ -37,6 +37,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -47,9 +48,9 @@ import mage.filter.FilterCard; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.TargetCard; +import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; /** @@ -175,9 +176,9 @@ class SoulFoundryEffect extends OneShotEffect { Card imprinted = game.getCard(soulFoundry.getImprinted().get(0)); if (imprinted != null && game.getState().getZone(imprinted.getId()).equals(Zone.EXILED)) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(imprinted); - return token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId()); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(imprinted.getId(), imprinted.getZoneChangeCounter(game))); + return effect.apply(game, source); } } } diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/Mirrorworks.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/Mirrorworks.java index 6bc55444c9a..93d3b051d17 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/Mirrorworks.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/Mirrorworks.java @@ -1,16 +1,16 @@ /* * Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. @@ -28,26 +28,20 @@ package mage.sets.mirrodinbesieged; import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.costs.Cost; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SetTargetPointer; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentToken; -import mage.game.permanent.token.EmptyToken; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.TokenPredicate; /** * @@ -55,13 +49,23 @@ import mage.util.CardUtil; */ public class Mirrorworks extends CardImpl { + private final static FilterArtifactPermanent filter = new FilterArtifactPermanent("another nontoken artifact"); + + static { + filter.add(new AnotherPredicate()); + filter.add(Predicates.not(new TokenPredicate())); + } + public Mirrorworks(UUID ownerId) { super(ownerId, 114, "Mirrorworks", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{5}"); this.expansionSetCode = "MBS"; // Whenever another nontoken artifact enters the battlefield under your control, you may pay {2}. // If you do, put a token that's a copy of that artifact onto the battlefield. - this.addAbility(new MirrorworksAbility()); + Effect effect = new DoIfCostPaid(new PutTokenOntoBattlefieldCopyTargetEffect(), + new ManaCostsImpl("{2}"), "Put a token that's a copy of that artifact onto the battlefield?"); + effect.setText("you may pay {2}. If you do, put a token that's a copy of that artifact onto the battlefield"); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, effect, filter, false, SetTargetPointer.PERMANENT, null)); } public Mirrorworks(final Mirrorworks card) { @@ -74,90 +78,3 @@ public class Mirrorworks extends CardImpl { } } - -class MirrorworksAbility extends TriggeredAbilityImpl { - - public MirrorworksAbility() { - super(Zone.BATTLEFIELD, new MirrorworksEffect()); - } - - public MirrorworksAbility(final MirrorworksAbility ability) { - super(ability); - } - - @Override - public MirrorworksAbility copy() { - return new MirrorworksAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null - && permanent.getControllerId().equals(getControllerId()) - && !(permanent instanceof PermanentToken) - && permanent.getCardType().contains(CardType.ARTIFACT)) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever another nontoken artifact enters the battlefield under your control, you may pay {2}. If you do, put a token that's a copy of that artifact onto the battlefield"; - } - -} - -class MirrorworksEffect extends OneShotEffect { - - public MirrorworksEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "put a token that's a copy of that artifact onto the battlefield"; - } - - public MirrorworksEffect(final MirrorworksEffect effect) { - super(effect); - } - - @Override - public MirrorworksEffect copy() { - return new MirrorworksEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - UUID targetId = targetPointer.getFirst(game, source); - if (targetId != null && player != null) { - MageObject target = game.getPermanent(targetId); - if (target == null) { - target = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); - } - if (target != null) { - Cost cost = new ManaCostsImpl("{2}"); - if (player.chooseUse(outcome, new StringBuilder("Pay ").append(cost.getText()).append(" and put a token copy of ").append(target.getName()).append(" onto the battlefield").toString(), source, game)) { - cost.clearPaid(); - if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { - if (target instanceof Permanent) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from((Permanent)target); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - } - } - } - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/sets/planarchaos/Chronozoa.java b/Mage.Sets/src/mage/sets/planarchaos/Chronozoa.java index c3e250f828c..660b1a980ae 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/Chronozoa.java +++ b/Mage.Sets/src/mage/sets/planarchaos/Chronozoa.java @@ -34,7 +34,8 @@ import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.LastTimeCounterRemovedCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; -import mage.abilities.effects.CopyCardEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VanishingSacrificeAbility; @@ -47,14 +48,11 @@ import mage.counters.CounterType; /** * * @author Gal Lerman - + * */ public class Chronozoa extends CardImpl { - private static final int timeCounters = 3; - private static final int numCopies = 2; - - public Chronozoa(UUID ownerId) { + public Chronozoa(UUID ownerId) { super(ownerId, 37, "Chronozoa", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{U}"); this.expansionSetCode = "PLC"; this.subtype.add("Illusion"); @@ -64,15 +62,17 @@ public class Chronozoa extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Vanishing 3 - Ability ability = new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.TIME.createInstance(timeCounters))); + Ability ability = new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.TIME.createInstance(3))); ability.setRuleVisible(false); this.addAbility(ability); - this.addAbility(new VanishingUpkeepAbility(timeCounters)); + this.addAbility(new VanishingUpkeepAbility(3)); this.addAbility(new VanishingSacrificeAbility()); // When Chronozoa is put into a graveyard from play, if it had no time counters on it, put two tokens into play that are copies of it. - this.addAbility(new ConditionalTriggeredAbility(new DiesCreatureTriggeredAbility(new CopyCardEffect(this, numCopies), false), - new LastTimeCounterRemovedCondition(), - "When {this} dies, if it had no time counters on it, put two tokens that are copies of it onto the battlefield.")); + Effect effect = new PutTokenOntoBattlefieldCopySourceEffect(2); + effect.setText("put two tokens into play that are copies of it"); + this.addAbility(new ConditionalTriggeredAbility(new DiesCreatureTriggeredAbility(effect, false), + new LastTimeCounterRemovedCondition(), + "When {this} dies, if it had no time counters on it, put two tokens that are copies of it onto the battlefield.")); } public Chronozoa(final Chronozoa card) { diff --git a/Mage.Sets/src/mage/sets/returntoravnica/PackRat.java b/Mage.Sets/src/mage/sets/returntoravnica/PackRat.java index 266521195b0..2d32fcf5aaf 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/PackRat.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/PackRat.java @@ -28,11 +28,6 @@ package mage.sets.returntoravnica; import java.util.UUID; - -import mage.abilities.effects.CopyCardEffect; -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -40,32 +35,37 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; /** * * The token will copy Pack Rat's two abilities. Its power and toughness will be - * equal to the number of Rats you control (not the number of Rats you controlled - * when the token entered the battlefield). It will also be able to create copies - * of itself. + * equal to the number of Rats you control (not the number of Rats you + * controlled when the token entered the battlefield). It will also be able to + * create copies of itself. * - * The token won't copy counters on Pack Rat, nor will it copy other effects that - * have changed Pack Rat's power, toughness, types, color, or so on. Normally, - * this means the token will simply be a Pack Rat. But if any copy effects have - * affected that Pack Rat, they're taken into account. + * The token won't copy counters on Pack Rat, nor will it copy other effects + * that have changed Pack Rat's power, toughness, types, color, or so on. + * Normally, this means the token will simply be a Pack Rat. But if any copy + * effects have affected that Pack Rat, they're taken into account. * - * If Pack Rat leaves the battlefield before its activated ability resolves, - * the token will still enter the battlefield as a copy of Pack Rat, using - * Pack Rat's copiable values from when it was last on the battlefield. + * If Pack Rat leaves the battlefield before its activated ability resolves, the + * token will still enter the battlefield as a copy of Pack Rat, using Pack + * Rat's copiable values from when it was last on the battlefield. * * * @author LevelX2 */ public class PackRat extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Rats you control"); static { @@ -83,7 +83,7 @@ public class PackRat extends CardImpl { // Pack Rat's power and toughness are each equal to the number of Rats you control. this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new PermanentsOnBattlefieldCount(filter), Duration.EndOfGame))); // {2}{B}, Discard a card: Put a token onto the battlefield that's a copy of Pack Rat. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CopyCardEffect(this, 1), new ManaCostsImpl("{2}{B}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutTokenOntoBattlefieldCopySourceEffect(), new ManaCostsImpl("{2}{B}")); ability.addCost(new DiscardCardCost()); this.addAbility(ability); } @@ -97,4 +97,3 @@ public class PackRat extends CardImpl { return new PackRat(this); } } - diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java index f3025d07eb1..ee1aa00718b 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java @@ -28,11 +28,6 @@ package mage.sets.riseoftheeldrazi; import java.util.UUID; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -42,17 +37,20 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; -import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -65,7 +63,6 @@ public class SplinterTwin extends CardImpl { this.expansionSetCode = "ROE"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -107,19 +104,18 @@ class SplinterTwinEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - - token.addAbility(HasteAbility.getInstance()); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(token.getLastAddedToken())); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } return true; } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java index 33664325db6..324ecd84756 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java @@ -37,7 +37,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.keyword.HasteAbility; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -50,10 +50,8 @@ import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @author nantuko @@ -204,23 +202,17 @@ class MimicVatCreateTokenEffect extends OneShotEffect { if (permanent.getImprinted().size() > 0) { Card card = game.getCard(permanent.getImprinted().get(0)); if (card != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card); - - token.addAbility(HasteAbility.getInstance()); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); - } + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); } return true; diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrPropagator.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrPropagator.java index 83037aeec91..ed639b1d68f 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrPropagator.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrPropagator.java @@ -27,24 +27,17 @@ */ package mage.sets.scarsofmirrodin; -import mage.constants.CardType; -import mage.constants.Rarity; +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.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.cards.CardImpl; -import mage.constants.Outcome; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; -import mage.util.CardUtil; - -import java.util.UUID; -import mage.MageObject; /** * @@ -61,7 +54,7 @@ public class MyrPropagator extends CardImpl { this.toughness = new MageInt(1); // {3}, {tap}: Put a token that's a copy of Myr Propagator onto the battlefield. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MyrPropagatorCreateTokenEffect(), new GenericManaCost(3)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutTokenOntoBattlefieldCopySourceEffect(), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } @@ -75,41 +68,3 @@ public class MyrPropagator extends CardImpl { return new MyrPropagator(this); } } - -class MyrPropagatorCreateTokenEffect extends OneShotEffect { - - public MyrPropagatorCreateTokenEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "Put a token that's a copy of {this} onto the battlefield"; - } - - public MyrPropagatorCreateTokenEffect(final MyrPropagatorCreateTokenEffect effect) { - super(effect); - } - - @Override - public MyrPropagatorCreateTokenEffect copy() { - return new MyrPropagatorCreateTokenEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - MageObject thisCard = game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (thisCard != null && thisCard instanceof Permanent) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from((Permanent)thisCard); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } else { // maybe it's token - Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/RhysTheRedeemed.java b/Mage.Sets/src/mage/sets/shadowmoor/RhysTheRedeemed.java index c66ba3594b3..14d5e8ab7c1 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/RhysTheRedeemed.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/RhysTheRedeemed.java @@ -35,6 +35,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -45,10 +46,9 @@ import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token; import mage.players.Player; -import mage.util.CardUtil; +import mage.target.targetpointer.FixedTarget; /** * @@ -113,13 +113,13 @@ class RhysTheRedeemedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { if (permanent.getControllerId().equals(source.getControllerId())) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); } } return true; diff --git a/Mage.Sets/src/mage/sets/shardsofalara/MinionReflector.java b/Mage.Sets/src/mage/sets/shardsofalara/MinionReflector.java index d7980605090..47f5c126b5d 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/MinionReflector.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/MinionReflector.java @@ -28,17 +28,22 @@ package mage.sets.shardsofalara; import java.util.UUID; - -import mage.constants.*; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.SacrificeSourceEffect; -import mage.abilities.keyword.HasteAbility; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; @@ -46,9 +51,7 @@ import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -57,10 +60,11 @@ import mage.util.CardUtil; public class MinionReflector extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature"); - static{ + + static { filter.add(Predicates.not(new TokenPredicate())); } - + public MinionReflector(UUID ownerId) { super(ownerId, 211, "Minion Reflector", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{5}"); this.expansionSetCode = "ALA"; @@ -83,7 +87,6 @@ public class MinionReflector extends CardImpl { class MinionReflectorTriggeredAbility extends EntersBattlefieldAllTriggeredAbility { - public MinionReflectorTriggeredAbility() { super(new MinionReflectorEffect(), new FilterControlledCreaturePermanent(), "Whenever a nontoken creature enters the battlefield under your control, you may pay {2}. If you do, put a token that's a copy of that creature onto the battlefield. That token has haste and \"At the beginning of the end step, sacrifice this permanent"); filter.add(Predicates.not(new TokenPredicate())); @@ -99,7 +102,7 @@ class MinionReflectorTriggeredAbility extends EntersBattlefieldAllTriggeredAbili UUID targetId = event.getTargetId(); Permanent permanent = game.getPermanent(targetId); if (filter.match(permanent, getSourceId(), getControllerId(), game)) { - for(Effect effect : this.getEffects()){ + for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(targetId)); } return true; @@ -108,16 +111,12 @@ class MinionReflectorTriggeredAbility extends EntersBattlefieldAllTriggeredAbili return false; } - - @Override public MinionReflectorTriggeredAbility copy() { return new MinionReflectorTriggeredAbility(this); } - - -} +} class MinionReflectorEffect extends OneShotEffect { @@ -137,18 +136,16 @@ class MinionReflectorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); - } - + Permanent permanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - - token.addAbility(HasteAbility.getInstance()); - token.addAbility(new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect(), TargetController.ANY, true)); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + ContinuousEffect continuousEffect = new GainAbilityTargetEffect(new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect(), TargetController.ANY, true), Duration.Custom); + continuousEffect.setTargetPointer(new FixedTarget(addedToken.getId())); + game.addEffect(continuousEffect, source); + } return true; } diff --git a/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java b/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java index 758f9baf86d..de05affa1de 100644 --- a/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java +++ b/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java @@ -27,6 +27,7 @@ */ package mage.sets.thedark; +import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -36,12 +37,14 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.effects.common.SacrificeTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; @@ -53,10 +56,9 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -113,22 +115,22 @@ class DanceOfManyCreateTokenCopyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); - } - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - Permanent permanentToken = game.getPermanent(token.getLastAddedToken()); - game.getState().setValue(source.getSourceId() + "_token", permanentToken); - Effect sacrificeEffect = new SacrificeTargetEffect("sacrifice Dance of Many"); - sacrificeEffect.setTargetPointer(new FixedTarget(game.getPermanent(source.getSourceId()).getId())); - LeavesBattlefieldTriggeredAbility triggerAbility = new LeavesBattlefieldTriggeredAbility(sacrificeEffect, false); - ContinuousEffect effect = new GainAbilityTargetEffect(triggerAbility, Duration.WhileOnBattlefield); - effect.setTargetPointer(new FixedTarget(token.getLastAddedToken())); - game.addEffect(effect, source); + Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent sourceObject = game.getPermanent(source.getSourceId()); + if (permanent != null && sourceObject != null) { + + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + game.getState().setValue(source.getSourceId() + "_token", effect.getAddedPermanent()); + for (Permanent addedToken : effect.getAddedPermanent()) { + Effect sacrificeEffect = new SacrificeTargetEffect("sacrifice Dance of Many"); + sacrificeEffect.setTargetPointer(new FixedTarget(sourceObject, game)); + LeavesBattlefieldTriggeredAbility triggerAbility = new LeavesBattlefieldTriggeredAbility(sacrificeEffect, false); + ContinuousEffect continuousEffect = new GainAbilityTargetEffect(triggerAbility, Duration.WhileOnBattlefield); + continuousEffect.setTargetPointer(new FixedTarget(addedToken, game)); + game.addEffect(continuousEffect, source); + } return true; } return false; @@ -153,11 +155,17 @@ class DanceOfManyExileTokenEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent tokenPermanent = (Permanent) game.getState().getValue(source.getSourceId() + "_token"); - if (tokenPermanent != null) { - Effect effect = new ExileTargetEffect(); - effect.setTargetPointer(new FixedTarget(tokenPermanent.getId())); - return effect.apply(game, source); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + List tokenPermanents = (List) game.getState().getValue(source.getSourceId() + "_token"); + if (tokenPermanents != null) { + Cards cards = new CardsImpl(); + for (Permanent permanent : tokenPermanents) { + cards.add(permanent); + } + controller.moveCards(cards, null, Zone.EXILED, source, game); + return true; + } } return false; } diff --git a/Mage.Sets/src/mage/sets/worldwake/NemesisTrap.java b/Mage.Sets/src/mage/sets/worldwake/NemesisTrap.java index 5665494edbe..95b7dead30f 100644 --- a/Mage.Sets/src/mage/sets/worldwake/NemesisTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/NemesisTrap.java @@ -29,28 +29,29 @@ package mage.sets.worldwake; import java.util.UUID; import mage.ObjectColor; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.common.TargetAttackingCreature; -import mage.util.CardUtil; +import mage.target.targetpointer.FixedTarget; /** * @@ -59,19 +60,17 @@ import mage.util.CardUtil; public class NemesisTrap extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("If a white creature is attacking"); - + static { filter.add(new ColorPredicate(ObjectColor.WHITE)); filter.add(new AttackingPredicate()); } - - + public NemesisTrap(UUID ownerId) { super(ownerId, 61, "Nemesis Trap", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{4}{B}{B}"); this.expansionSetCode = "WWK"; this.subtype.add("Trap"); - // If a white creature is attacking, you may pay {B}{B} rather than pay Nemesis Trap's mana cost. this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{B}{B}"), new PermanentsOnTheBattlefieldCondition(filter, PermanentsOnTheBattlefieldCondition.CountType.MORE_THAN, 0, false))); @@ -111,11 +110,21 @@ class NemesisTrapEffect extends OneShotEffect { Permanent targetedCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && targetedCreature != null) { - controller.moveCardToExileWithInfo(targetedCreature, null, null, source.getSourceId(), game, Zone.BATTLEFIELD, true); - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(targetedCreature); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - token.addAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ExileSourceEffect())); + // exile target + controller.moveCards(targetedCreature, null, Zone.EXILED, source, game); + // create token + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(targetedCreature, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + Effect exileEffect = new ExileTargetEffect("Exile " + addedToken.getName() + " at the beginning of the next end step"); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } return true; } return false; diff --git a/Mage.Sets/src/mage/sets/zendikar/RiteOfReplication.java b/Mage.Sets/src/mage/sets/zendikar/RiteOfReplication.java index fd51ca2ee7a..85152ecc7c4 100644 --- a/Mage.Sets/src/mage/sets/zendikar/RiteOfReplication.java +++ b/Mage.Sets/src/mage/sets/zendikar/RiteOfReplication.java @@ -28,21 +28,14 @@ package mage.sets.zendikar; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.condition.common.KickedCondition; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; /** * @@ -59,8 +52,8 @@ public class RiteOfReplication extends CardImpl { // Put a token that's a copy of target creature onto the battlefield. If Rite of Replication was kicked, put five of those tokens onto the battlefield instead. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new RiteOfReplicationEffect(5), - new RiteOfReplicationEffect(1), KickedCondition.getInstance(), + this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new PutTokenOntoBattlefieldCopyTargetEffect(null, null, false, 5), + new PutTokenOntoBattlefieldCopyTargetEffect(), KickedCondition.getInstance(), "Put a token that's a copy of target creature onto the battlefield. If {this} was kicked, put five of those tokens onto the battlefield instead")); } @@ -73,38 +66,3 @@ public class RiteOfReplication extends CardImpl { return new RiteOfReplication(this); } } - -class RiteOfReplicationEffect extends OneShotEffect { - - private final int amount; - - public RiteOfReplicationEffect(int amount) { - super(Outcome.PutCreatureInPlay); - this.amount = amount; - } - - public RiteOfReplicationEffect(final RiteOfReplicationEffect effect) { - super(effect); - this.amount = effect.amount; - } - - @Override - public RiteOfReplicationEffect copy() { - return new RiteOfReplicationEffect(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) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(amount, game, source.getSourceId(), source.getControllerId()); - return true; - } - return false; - } -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/KikiJikiMirrorBreakerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/KikiJikiMirrorBreakerTest.java index d252e8a6a79..ff78a4dcf31 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/KikiJikiMirrorBreakerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/KikiJikiMirrorBreakerTest.java @@ -27,8 +27,13 @@ */ package org.mage.test.cards.copy; +import mage.abilities.keyword.HasteAbility; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -134,4 +139,54 @@ public class KikiJikiMirrorBreakerTest extends CardTestPlayerBase { } + /** + * + * Kiki-Jiki, Mirror Breaker tokens are not entering with haste... + * + * I just tried to reproduce this but I was not able-- + * + * In the game in question I used a card (Body Double) to target a card in a + * graveyard (don't remember the name) and copy it + * + * I then used Kiki to copy Body Double that copied a graveyard card of my + * opponent---that copy did not have haste... + */ + @Test + public void testCopyBodyDouble() { + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + addCard(Zone.BATTLEFIELD, playerB, "Kiki-Jiki, Mirror Breaker", 1); + // {T}: Draw two cards. Target opponent gains control of Humble Defector. Activate this ability only during your turn. + addCard(Zone.HAND, playerB, "Body Double", 1); // {4}{U} + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Body Double"); + setChoice(playerB, "Silvercoat Lion"); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Put a token that's a copy of target nonlegendary creature you control onto the battlefield. That token has haste. Sacrifice it at the beginning of the next end step."); + + attack(2, playerB, "Silvercoat Lion"); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerB, "Silvercoat Lion", 2); // one from Body Double and one from Kiki + + Permanent kikiCopy = null; + for (Permanent permanent : currentGame.getState().getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), currentGame)) { + if (permanent.getName().equals("Silvercoat Lion") && (permanent instanceof PermanentToken)) { + kikiCopy = permanent; + break; + } + } + if (kikiCopy != null) { + Assert.assertEquals("Has to have haste", kikiCopy.getAbilities(currentGame).containsClass(HasteAbility.class), true); + } else { + Assert.assertEquals("Silvercoat Lion copied by Kiki is missing", kikiCopy != null, true); + } + + assertLife(playerA, 18); + assertLife(playerB, 20); + + } } diff --git a/Mage/src/mage/abilities/effects/CopyCardEffect.java b/Mage/src/mage/abilities/effects/CopyCardEffect.java deleted file mode 100644 index 3435e707673..00000000000 --- a/Mage/src/mage/abilities/effects/CopyCardEffect.java +++ /dev/null @@ -1,51 +0,0 @@ -package mage.abilities.effects; - -import mage.abilities.Ability; -import mage.cards.Card; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; -import mage.util.CardUtil; - -/** - * Created by glerman on 20/6/15. - */ -public class CopyCardEffect extends OneShotEffect { - - private final Card card; - private final int copies; - - public CopyCardEffect(Card card, int copies) { - super(Outcome.PutCreatureInPlay); - this.card = card; - this.copies = copies; - staticText = "Put a token onto the battlefield that's a copy of {this}"; - } - - public CopyCardEffect(final CopyCardEffect effect) { - super(effect); - this.card = effect.card; - this.copies = effect.copies; - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - } - if (permanent != null) { - EmptyToken newToken = new EmptyToken(); - CardUtil.copyTo(newToken).from(permanent); - return newToken.putOntoBattlefield(copies, game, source.getSourceId(), source.getControllerId()); - } - return false; - } - - @Override - public CopyCardEffect copy() { - return new CopyCardEffect(this); - } -} diff --git a/Mage/src/mage/abilities/effects/PutTokenOntoBattlefieldCopySourceEffect.java b/Mage/src/mage/abilities/effects/PutTokenOntoBattlefieldCopySourceEffect.java new file mode 100644 index 00000000000..ac09d741514 --- /dev/null +++ b/Mage/src/mage/abilities/effects/PutTokenOntoBattlefieldCopySourceEffect.java @@ -0,0 +1,47 @@ +package mage.abilities.effects; + +import mage.abilities.Ability; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * Created by glerman on 20/6/15. + */ +public class PutTokenOntoBattlefieldCopySourceEffect extends OneShotEffect { + + private final int number; + + public PutTokenOntoBattlefieldCopySourceEffect() { + this(1); + } + + public PutTokenOntoBattlefieldCopySourceEffect(int copies) { + super(Outcome.PutCreatureInPlay); + this.number = copies; + staticText = "put a token onto the battlefield that's a copy of {this}"; + } + + public PutTokenOntoBattlefieldCopySourceEffect(final PutTokenOntoBattlefieldCopySourceEffect effect) { + super(effect); + this.number = effect.number; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (permanent != null) { + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, false, number); + effect.setTargetPointer(new FixedTarget(source.getSourceId())); + return effect.apply(game, source); + } + return false; + } + + @Override + public PutTokenOntoBattlefieldCopySourceEffect copy() { + return new PutTokenOntoBattlefieldCopySourceEffect(this); + } +} diff --git a/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java b/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java deleted file mode 100644 index 78ebf7f019c..00000000000 --- a/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ - -package mage.abilities.effects.common; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; -import mage.players.Player; -import mage.util.CardUtil; - -/** - * - * @author LevelX2 - */ - -public class PutTokenOntoBattlefieldCopySource extends OneShotEffect { - - public PutTokenOntoBattlefieldCopySource() { - super(Outcome.PutCreatureInPlay); - this.staticText = "put a token that's a copy of {this} onto the battlefield"; - } - - public PutTokenOntoBattlefieldCopySource(final PutTokenOntoBattlefieldCopySource effect) { - super(effect); - } - - @Override - public PutTokenOntoBattlefieldCopySource copy() { - return new PutTokenOntoBattlefieldCopySource(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - MageObject thisCard = game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (thisCard != null && thisCard instanceof Permanent) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from((Permanent)thisCard); - if (token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId())) { - if (!game.isSimulation()) - game.informPlayers(new StringBuilder(controller.getLogName()) - .append(" puts a ").append(token.getName()).append(" token ").append("onto the Battlefield").toString()); - return true; - } - } else { // maybe it's token - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - if (token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId())) { - if (!game.isSimulation()) - game.informPlayers(new StringBuilder(controller.getLogName()) - .append(" puts a ").append(token.getName()).append(" token ").append("onto the Battlefield").toString()); - return true; - } - } - } - } - return false; - } - -} \ No newline at end of file diff --git a/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java b/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java index ba01a9eef9d..2cc2381a78d 100644 --- a/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java @@ -36,6 +36,7 @@ import mage.abilities.Mode; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; @@ -54,32 +55,42 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { private final UUID playerId; private final CardType additionalCardType; private boolean gainsHaste; + private final int number; private List addedTokenPermanents; + private String additionalSubType; public PutTokenOntoBattlefieldCopyTargetEffect() { super(Outcome.PutCreatureInPlay); this.playerId = null; this.additionalCardType = null; this.addedTokenPermanents = new ArrayList<>(); + this.number = 1; + this.additionalSubType = null; } public PutTokenOntoBattlefieldCopyTargetEffect(UUID playerId) { this(playerId, null, false); } + public PutTokenOntoBattlefieldCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste) { + this(playerId, additionalCardType, gainsHaste, 1); + } + /** * * @param playerId null the token is controlled/owned by the controller of * the source ability * @param additionalCardType the token gains tis card types in addition * @param gainsHaste the token gains haste + * @param number number of tokens to put into play */ - public PutTokenOntoBattlefieldCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste) { + public PutTokenOntoBattlefieldCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste, int number) { super(Outcome.PutCreatureInPlay); this.playerId = playerId; this.additionalCardType = additionalCardType; this.gainsHaste = gainsHaste; this.addedTokenPermanents = new ArrayList<>(); + this.number = number; } public PutTokenOntoBattlefieldCopyTargetEffect(final PutTokenOntoBattlefieldCopyTargetEffect effect) { @@ -88,15 +99,18 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { this.additionalCardType = effect.additionalCardType; this.gainsHaste = effect.gainsHaste; this.addedTokenPermanents = new ArrayList<>(effect.addedTokenPermanents); + this.number = effect.number; + this.additionalSubType = effect.additionalSubType; } @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Card copyFrom; + ApplyToPermanent applier = new EmptyApplyToPermanent(); if (permanent != null) { // handle copies of copies Permanent copyFromPermanent = permanent; - ApplyToPermanent applier = new EmptyApplyToPermanent(); for (Effect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) { if (effect instanceof CopyEffect) { CopyEffect copyEffect = (CopyEffect) effect; @@ -112,27 +126,37 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { } } } - - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(copyFromPermanent); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) - applier.apply(game, token); - if (additionalCardType != null && !token.getCardType().contains(additionalCardType)) { - token.getCardType().add(additionalCardType); - } - if (gainsHaste) { - token.addAbility(HasteAbility.getInstance()); - } - - token.putOntoBattlefield(1, game, source.getSourceId(), playerId == null ? source.getControllerId() : playerId); - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - addedTokenPermanents.add(tokenPermanent); - } - } - return true; + copyFrom = copyFromPermanent; + } else { + copyFrom = game.getCard(getTargetPointer().getFirst(game, source)); } - return false; + + if (permanent == null && copyFrom == null) { + return false; + } + + EmptyToken token = new EmptyToken(); + CardUtil.copyTo(token).from(copyFrom); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) + applier.apply(game, token); + if (additionalCardType != null && !token.getCardType().contains(additionalCardType)) { + token.getCardType().add(additionalCardType); + } + if (gainsHaste) { + token.addAbility(HasteAbility.getInstance()); + } + if (additionalSubType != null) { + if (token.getSubtype().contains(additionalSubType)) { + token.getSubtype().add(additionalSubType); + } + } + token.putOntoBattlefield(number, game, source.getSourceId(), playerId == null ? source.getControllerId() : playerId); + for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield + Permanent tokenPermanent = game.getPermanent(tokenId); + if (tokenPermanent != null) { + addedTokenPermanents.add(tokenPermanent); + } + } + return true; } @Override @@ -142,8 +166,11 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { @Override public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } StringBuilder sb = new StringBuilder(); - sb.append("Put a token onto the battlefield that's a copy of "); + sb.append("Put a token onto the battlefield that's a copy of target "); if (mode.getTargets() != null) { sb.append(mode.getTargets().get(0).getTargetName()); } @@ -154,4 +181,8 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { public List getAddedPermanent() { return addedTokenPermanents; } + + public void setAdditionalSubType(String additionalSubType) { + this.additionalSubType = additionalSubType; + } } diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index 9a53acdc2d1..84ae5f3986f 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -347,4 +347,5 @@ public interface Permanent extends Card, Controllable { int getCreateOrder(); void setCreateOrder(int createOrder); + } diff --git a/Mage/src/mage/util/functions/CopyTokenFunction.java b/Mage/src/mage/util/functions/CopyTokenFunction.java index d7b5496320e..9344d7d0bfc 100644 --- a/Mage/src/mage/util/functions/CopyTokenFunction.java +++ b/Mage/src/mage/util/functions/CopyTokenFunction.java @@ -57,6 +57,7 @@ public class CopyTokenFunction implements Function { } // A copy contains only the attributes of the basic card or basic Token that's the base of the permanent // else gained abililies would be copied too. + MageObject sourceObj = source; if (source instanceof PermanentToken) { sourceObj = ((PermanentToken) source).getToken();