From 7dd873b1e60acf3a2ddcf6c3aa157cc4d67720db Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 21 Oct 2014 16:01:00 +0200 Subject: [PATCH] * Fixes to put token onto the battlefield that's a copy of creature on the battlefield effects that copy creatures that already copy other creatures. --- .../sets/bornofthegods/FatedInfatuation.java | 43 +---- .../bornofthegods/FelhideSpiritbinder.java | 35 ++--- .../championsofkamigawa/KitsuneMystic.java | 4 - .../src/mage/sets/commander/Spawnwrithe.java | 4 +- .../commander2013/TemptWithReflections.java | 28 ++-- .../sets/dragonsmaze/ProgenitorMimic.java | 1 - .../sets/innistrad/CacklingCounterpart.java | 43 +---- .../test/cards/copy/ArtisanOfFormsTest.java | 123 +++++++++++++++ .../abilities/effects/common/CopyEffect.java | 15 +- .../effects/common/PopulateEffect.java | 14 +- ...=> PutTokenOntoBattlefieldCopySource.java} | 10 +- ...tTokenOntoBattlefieldCopyTargetEffect.java | 147 ++++++++++++++++++ Mage/src/mage/game/GameImpl.java | 3 +- 13 files changed, 329 insertions(+), 141 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/copy/ArtisanOfFormsTest.java rename Mage/src/mage/abilities/effects/common/{PutCopySourceTokenOntoBattlefield.java => PutTokenOntoBattlefieldCopySource.java} (90%) create mode 100644 Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java diff --git a/Mage.Sets/src/mage/sets/bornofthegods/FatedInfatuation.java b/Mage.Sets/src/mage/sets/bornofthegods/FatedInfatuation.java index 222168a50ad..db7ab9dbef6 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/FatedInfatuation.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/FatedInfatuation.java @@ -28,21 +28,14 @@ package mage.sets.bornofthegods; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.ScryEffect; 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.TargetControlledCreaturePermanent; -import mage.util.CardUtil; /** * @@ -57,7 +50,7 @@ public class FatedInfatuation extends CardImpl { this.color.setBlue(true); // Put a token onto the battlefield that's a copy of target creature you control. If it's your turn, scry 2. - this.getSpellAbility().addEffect(new FatedInfatuationCopyEffect()); + this.getSpellAbility().addEffect(new PutTokenOntoBattlefieldCopyTargetEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2), MyTurnCondition.getInstance(), "If it's your turn, scry 2")); } @@ -71,35 +64,3 @@ public class FatedInfatuation extends CardImpl { return new FatedInfatuation(this); } } - -class FatedInfatuationCopyEffect extends OneShotEffect { - - public FatedInfatuationCopyEffect() { - super(Outcome.PutCreatureInPlay); - staticText = "Put a token onto the battlefield that's a copy of target creature you control"; - } - - public FatedInfatuationCopyEffect(final FatedInfatuationCopyEffect effect) { - super(effect); - } - - @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()); - return true; - } - return false; - } - - @Override - public FatedInfatuationCopyEffect copy() { - return new FatedInfatuationCopyEffect(this); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/bornofthegods/FelhideSpiritbinder.java b/Mage.Sets/src/mage/sets/bornofthegods/FelhideSpiritbinder.java index e11b41b0d20..40688b384b9 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/FelhideSpiritbinder.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/FelhideSpiritbinder.java @@ -36,21 +36,18 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.keyword.HasteAbility; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.keyword.InspiredAbility; 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.permanent.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -108,28 +105,20 @@ class FelhideSpiritbinderEffect 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); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(null, CardType.ENCHANTMENT, true); + effect.setTargetPointer(getTargetPointer()); + if (effect.apply(game, source) && effect.getAddedPermanent() != null) { + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanent().getId())); + DelayedTriggeredAbility delayedAbility = new AtEndOfTurnDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + game.addDelayedTriggeredAbility(delayedAbility); - if (!token.getCardType().contains(CardType.ENCHANTMENT)) { - token.getCardType().add(CardType.ENCHANTMENT); + return true; } - 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 AtEndOfTurnDelayedTriggeredAbility(exileEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - game.addDelayedTriggeredAbility(delayedAbility); - - return true; } return false; diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KitsuneMystic.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KitsuneMystic.java index 3d3793a1940..df307081a21 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KitsuneMystic.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KitsuneMystic.java @@ -36,14 +36,10 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.EnchantedCondition; -import mage.abilities.condition.common.FlippedCondition; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.decorator.ConditionalContinousEffect; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CopyTokenEffect; import mage.abilities.effects.common.FlipSourceEffect; import mage.cards.CardImpl; import mage.filter.common.FilterEnchantment; diff --git a/Mage.Sets/src/mage/sets/commander/Spawnwrithe.java b/Mage.Sets/src/mage/sets/commander/Spawnwrithe.java index 6ecc02b2311..9f37369b0f3 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.PutCopySourceTokenOntoBattlefield; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopySource; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.constants.CardType; @@ -54,7 +54,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 PutCopySourceTokenOntoBattlefield(), false)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new PutTokenOntoBattlefieldCopySource(), false)); } diff --git a/Mage.Sets/src/mage/sets/commander2013/TemptWithReflections.java b/Mage.Sets/src/mage/sets/commander2013/TemptWithReflections.java index bf678737764..7e9754483d1 100644 --- a/Mage.Sets/src/mage/sets/commander2013/TemptWithReflections.java +++ b/Mage.Sets/src/mage/sets/commander2013/TemptWithReflections.java @@ -31,15 +31,17 @@ import java.util.HashSet; import java.util.Set; import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +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.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.players.PlayerList; import mage.target.common.TargetControlledCreaturePermanent; @@ -90,15 +92,11 @@ class TemptWithReflectionsEffect 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.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + Effect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(getTargetPointer()); + effect.apply(game, source); Set playersSaidYes = new HashSet<>(); PlayerList playerList = game.getPlayerList().copy(); @@ -119,15 +117,15 @@ class TemptWithReflectionsEffect extends OneShotEffect { } while (!player.getId().equals(game.getActivePlayerId())); for (UUID playerId: playersSaidYes) { - token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), playerId); + effect = new PutTokenOntoBattlefieldCopyTargetEffect(playerId); + effect.setTargetPointer(getTargetPointer()); + effect.apply(game, source); } if (playersSaidYes.size() > 0) { - token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(playersSaidYes.size(), game, source.getSourceId(), source.getControllerId()); + effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(getTargetPointer()); + effect.apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java b/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java index 59116bf977b..0a5b592bd9e 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java @@ -52,7 +52,6 @@ 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.util.CardUtil; import mage.util.functions.ApplyToPermanent; diff --git a/Mage.Sets/src/mage/sets/innistrad/CacklingCounterpart.java b/Mage.Sets/src/mage/sets/innistrad/CacklingCounterpart.java index 7cc5e54c652..385a43f5de1 100644 --- a/Mage.Sets/src/mage/sets/innistrad/CacklingCounterpart.java +++ b/Mage.Sets/src/mage/sets/innistrad/CacklingCounterpart.java @@ -29,20 +29,13 @@ package mage.sets.innistrad; import java.util.UUID; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.TimingRule; -import mage.constants.Zone; -import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.common.TargetControlledCreaturePermanent; -import mage.util.CardUtil; /** * @author nantuko @@ -56,7 +49,7 @@ public class CacklingCounterpart extends CardImpl { this.color.setBlue(true); // Put a token onto the battlefield that's a copy of target creature you control. - this.getSpellAbility().addEffect(new CacklingCounterpartEffect()); + this.getSpellAbility().addEffect(new PutTokenOntoBattlefieldCopyTargetEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); // Flashback {5}{U}{U} @@ -73,36 +66,4 @@ public class CacklingCounterpart extends CardImpl { } } -class CacklingCounterpartEffect extends OneShotEffect { - - public CacklingCounterpartEffect() { - super(Outcome.PutCreatureInPlay); - staticText = "Put a token onto the battlefield that's a copy of target creature you control"; - } - - public CacklingCounterpartEffect(final CacklingCounterpartEffect effect) { - super(effect); - } - - @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()); - return true; - } - return false; - } - - @Override - public CacklingCounterpartEffect copy() { - return new CacklingCounterpartEffect(this); - } -} - diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/ArtisanOfFormsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/ArtisanOfFormsTest.java new file mode 100644 index 00000000000..661c54cef0a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/ArtisanOfFormsTest.java @@ -0,0 +1,123 @@ +/* + * 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 org.mage.test.cards.copy; + +import junit.framework.Assert; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class ArtisanOfFormsTest extends CardTestPlayerBase { + + /** + * Targeting a Artisan of Forms triggers it Heroic ability. So it can copy a traget creature. + * If Cackling Counterpart later resolves, it should copy the creature that Artisan of Forms copies, not + * the Artisan itself. + */ + @Test + public void testCopyTrggeredByCracklingCounterpart() { + // Heroic - Whenever you cast a spell that targets Artisan of Forms, you may have Artisan of Forms become a copy of target creature and gain this ability. + addCard(Zone.BATTLEFIELD, playerA, "Artisan of Forms"); + // {1}{U}{U} Put a token onto the battlefield that's a copy of target creature you control. + addCard(Zone.HAND, playerA, "Cackling Counterpart"); + addCard(Zone.BATTLEFIELD, playerA, "Island",3); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cackling Counterpart", "Artisan of Forms"); + addTarget(playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Cackling Counterpart", 1); + assertPermanentCount(playerA, "Artisan of Forms", 0); + assertPermanentCount(playerB, "Silvercoat Lion", 1); + // 1 + 2 Silvercoat Lion at the end + assertPermanentCount(playerA, "Silvercoat Lion", 2); + + for (Permanent permanent :currentGame.getBattlefield().getAllActivePermanents(playerA.getId())) { + if (permanent.getName().equals("Silvercoat Lion")) { + Assert.assertEquals("Creature has to have Cast + Heroic ability", 2, permanent.getAbilities().size()); + } + } + } + +/** + * Targeting a Artisan of Forms triggers it Heroic ability. So it can copy a target creature. + * If populate spell later resolves, it should copy the creature that Artisan of Forms copies, not + * the Artisan itself. + */ + @Test + public void testCopyTrggeredByPopulate() { + // Heroic - Whenever you cast a spell that targets Artisan of Forms, you may have Artisan of Forms become a copy of target creature and gain this ability. + addCard(Zone.BATTLEFIELD, playerA, "Artisan of Forms"); + // {1}{U}{U} Put a token onto the battlefield that's a copy of target creature you control. + addCard(Zone.HAND, playerA, "Cackling Counterpart"); + addCard(Zone.BATTLEFIELD, playerA, "Island",3); + addCard(Zone.HAND, playerA, "Eyes in the Skies"); + addCard(Zone.BATTLEFIELD, playerA, "Plains",4); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cackling Counterpart", "Artisan of Forms"); + setChoice(playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Eyes in the Skies"); + setChoice(playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Cackling Counterpart", 1); + assertPermanentCount(playerA, "Artisan of Forms", 0); + assertPermanentCount(playerB, "Silvercoat Lion", 1); + assertPermanentCount(playerA, "Bird", 1); + // 3 Silvercoat Lion at the end + assertPermanentCount(playerA, "Silvercoat Lion", 3); + + for (Permanent permanent :currentGame.getBattlefield().getAllActivePermanents(playerA.getId())) { + if (permanent.getName().equals("Silvercoat Lion")) { + Assert.assertEquals("Creature has to have Cast + Heroic ability", 2, permanent.getAbilities().size()); + } + } + } + +} diff --git a/Mage/src/mage/abilities/effects/common/CopyEffect.java b/Mage/src/mage/abilities/effects/common/CopyEffect.java index f940ae906c6..0fa67543bd5 100644 --- a/Mage/src/mage/abilities/effects/common/CopyEffect.java +++ b/Mage/src/mage/abilities/effects/common/CopyEffect.java @@ -42,6 +42,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentToken; +import mage.util.functions.ApplyToPermanent; /** * @@ -55,7 +56,8 @@ public class CopyEffect extends ContinuousEffectImpl { private MageObject target; private UUID sourceId; private int zoneChangeCounter; - + private ApplyToPermanent applier; + public CopyEffect(MageObject target, UUID sourceId) { this(Duration.Custom, target, sourceId); } @@ -71,6 +73,7 @@ public class CopyEffect extends ContinuousEffectImpl { this.target = effect.target.copy(); this.sourceId = effect.sourceId; this.zoneChangeCounter = effect.zoneChangeCounter; + this.applier = effect.applier; } @Override @@ -155,4 +158,14 @@ public class CopyEffect extends ContinuousEffectImpl { public UUID getSourceId() { return sourceId; } + + public ApplyToPermanent getApplier() { + return applier; + } + + public void setApplier(ApplyToPermanent applier) { + this.applier = applier; + } + + } diff --git a/Mage/src/mage/abilities/effects/common/PopulateEffect.java b/Mage/src/mage/abilities/effects/common/PopulateEffect.java index 98f0b671112..5efb8e3373a 100644 --- a/Mage/src/mage/abilities/effects/common/PopulateEffect.java +++ b/Mage/src/mage/abilities/effects/common/PopulateEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import mage.constants.Outcome; import mage.constants.TargetController; import mage.abilities.Ability; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.filter.FilterPermanent; import mage.filter.predicate.permanent.ControllerPredicate; @@ -41,6 +42,7 @@ import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; @@ -91,13 +93,11 @@ public class PopulateEffect extends OneShotEffect { if (target.canChoose(source.getControllerId(), game)) { player.choose(Outcome.Copy, target, source.getSourceId(), game); Permanent tokenToCopy = game.getPermanent(target.getFirstTarget()); - if (tokenToCopy != null && tokenToCopy instanceof PermanentToken) { - Token newToken = new Token("",""); - CardUtil.copyTo(newToken).from(tokenToCopy); - if (newToken != null ) { - game.informPlayers("Token selected for populate: " + newToken.getName()); - return newToken.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - } + if (tokenToCopy != null) { + game.informPlayers("Token selected for populate: " + tokenToCopy.getLogName()); + Effect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + return effect.apply(game, source); } } } diff --git a/Mage/src/mage/abilities/effects/common/PutCopySourceTokenOntoBattlefield.java b/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java similarity index 90% rename from Mage/src/mage/abilities/effects/common/PutCopySourceTokenOntoBattlefield.java rename to Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java index 280cf71b014..0c4a01e8ef5 100644 --- a/Mage/src/mage/abilities/effects/common/PutCopySourceTokenOntoBattlefield.java +++ b/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java @@ -44,20 +44,20 @@ import mage.util.CardUtil; * @author LevelX2 */ -public class PutCopySourceTokenOntoBattlefield extends OneShotEffect { +public class PutTokenOntoBattlefieldCopySource extends OneShotEffect { - public PutCopySourceTokenOntoBattlefield() { + public PutTokenOntoBattlefieldCopySource() { super(Outcome.PutCreatureInPlay); this.staticText = "put a token that's a copy of {this} onto the battlefield"; } - public PutCopySourceTokenOntoBattlefield(final PutCopySourceTokenOntoBattlefield effect) { + public PutTokenOntoBattlefieldCopySource(final PutTokenOntoBattlefieldCopySource effect) { super(effect); } @Override - public PutCopySourceTokenOntoBattlefield copy() { - return new PutCopySourceTokenOntoBattlefield(this); + public PutTokenOntoBattlefieldCopySource copy() { + return new PutTokenOntoBattlefieldCopySource(this); } @Override diff --git a/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java b/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java new file mode 100644 index 00000000000..7ad7b28d653 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java @@ -0,0 +1,147 @@ +/* + * 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 java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.HasteAbility; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.EmptyToken; +import mage.util.CardUtil; +import mage.util.functions.ApplyToPermanent; +import mage.util.functions.EmptyApplyToPermanent; + +/** + * + * @author LevelX2 + */ + +public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { + + private final UUID playerId; + private final CardType additionalCardType; + private boolean gainsHaste; + private Permanent addedTokenPermanent; + + public PutTokenOntoBattlefieldCopyTargetEffect() { + super(Outcome.PutCreatureInPlay); + this.playerId = null; + this.additionalCardType = null; + this.addedTokenPermanent = null; + } + + public PutTokenOntoBattlefieldCopyTargetEffect(UUID playerId) { + this(playerId, null, false); + } + + public PutTokenOntoBattlefieldCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste) { + super(Outcome.PutCreatureInPlay); + this.playerId = playerId; + this.additionalCardType = additionalCardType; + this.gainsHaste = gainsHaste; + this.addedTokenPermanent = null; + } + + public PutTokenOntoBattlefieldCopyTargetEffect(final PutTokenOntoBattlefieldCopyTargetEffect effect) { + super(effect); + this.playerId = effect.playerId; + this.additionalCardType = effect.additionalCardType; + this.gainsHaste = effect.gainsHaste; + this.addedTokenPermanent = effect.addedTokenPermanent; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + 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; + // there is another copy effect that our targetPermanent copies stats from + if (copyEffect.getSourceId().equals(permanent.getId())) { + MageObject object = ((CopyEffect) effect).getTarget(); + if (object instanceof Permanent) { + copyFromPermanent = (Permanent)object; + if (copyEffect.getApplier() != null) { + applier = copyEffect.getApplier(); + } + } + } + } + } + + EmptyToken token = new EmptyToken(); + CardUtil.copyTo(token).from(copyFromPermanent); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) + 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); + addedTokenPermanent = game.getPermanent(token.getLastAddedToken()); + if (addedTokenPermanent != null) { + game.copyPermanent(copyFromPermanent, addedTokenPermanent, source, applier); + return true; + } + } + return false; + } + + @Override + public PutTokenOntoBattlefieldCopyTargetEffect copy() { + return new PutTokenOntoBattlefieldCopyTargetEffect(this); + } + + @Override + public String getText(Mode mode) { + StringBuilder sb = new StringBuilder(); + sb.append("Put a token onto the battlefield that's a copy of "); + if (mode.getTargets() != null) { + sb.append(mode.getTargets().get(0).getTargetName()); + } + return sb.toString(); + + } + + public Permanent getAddedPermanent() { + return addedTokenPermanent; + } +} + + diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 495b341de80..c5f53b1af29 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -1248,8 +1248,9 @@ public abstract class GameImpl implements Game, Serializable { CopyEffect newEffect = new CopyEffect(duration, permanent, copyToPermanent.getId()); newEffect.newId(); newEffect.setTimestamp(); + newEffect.setApplier(applier); newEffect.init(newAbility, this); - + // handle copies of copies for (Effect effect : getState().getContinuousEffects().getLayeredEffects(this)) { if (effect instanceof CopyEffect) {