diff --git a/Mage.Sets/src/mage/cards/b/BrimazBlightOfOreskos.java b/Mage.Sets/src/mage/cards/b/BrimazBlightOfOreskos.java index 2992c623c1b..cd92b87e962 100644 --- a/Mage.Sets/src/mage/cards/b/BrimazBlightOfOreskos.java +++ b/Mage.Sets/src/mage/cards/b/BrimazBlightOfOreskos.java @@ -55,7 +55,7 @@ public final class BrimazBlightOfOreskos extends CardImpl { // At the beginning of each end step, if a Phyrexian died under your control this turn, proliferate. this.addAbility(new BeginningOfEndStepTriggeredAbility( - new ProliferateEffect(), TargetController.ANY, + new ProliferateEffect(false), TargetController.ANY, BrimazBlightOfOreskosCondition.instance, false ), new BrimazBlightOfOreskosWatcher()); } diff --git a/Mage.Sets/src/mage/cards/e/EsixFractalBloom.java b/Mage.Sets/src/mage/cards/e/EsixFractalBloom.java index 0a9bcf4e171..6bd8ba06770 100644 --- a/Mage.Sets/src/mage/cards/e/EsixFractalBloom.java +++ b/Mage.Sets/src/mage/cards/e/EsixFractalBloom.java @@ -18,12 +18,11 @@ import mage.game.Game; import mage.game.events.CreateTokenEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.TargetPermanent; -import mage.util.CardUtil; import mage.util.functions.CopyApplier; +import mage.util.functions.CopyTokenFunction; import mage.util.functions.EmptyCopyApplier; import mage.watchers.Watcher; @@ -145,8 +144,7 @@ class EsixFractalBloomEffect extends ReplacementEffectImpl { } // create token and modify all attributes permanently (without game usage) - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(copyFromPermanent, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) + Token token = CopyTokenFunction.createTokenCopy(copyFromPermanent, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) applier.apply(game, token, source, permanent.getId()); return token; } diff --git a/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java b/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java index 486c80aa1fe..97331b80c88 100644 --- a/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java +++ b/Mage.Sets/src/mage/cards/g/GodPharaohsGift.java @@ -14,11 +14,11 @@ import mage.constants.*; import mage.filter.StaticFilters; 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.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTargets; -import mage.util.CardUtil; +import mage.util.functions.CopyTokenFunction; import java.util.List; import java.util.Objects; @@ -83,12 +83,11 @@ class GodPharaohsGiftEffect extends OneShotEffect { return false; } // create token and modify all attributes permanently (without game usage) - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(cardChosen, game); + Token token = CopyTokenFunction.createTokenCopy(cardChosen, game); token.removePTCDA(); token.setPower(4); token.setToughness(4); - token.getColor().setColor(ObjectColor.BLACK); + token.setColor(ObjectColor.BLACK); token.removeAllCreatureTypes(); token.addSubType(SubType.ZOMBIE); token.putOntoBattlefield(1, game, source, source.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/h/HourOfEternity.java b/Mage.Sets/src/mage/cards/h/HourOfEternity.java index 5e26a57b157..c731126cd2c 100644 --- a/Mage.Sets/src/mage/cards/h/HourOfEternity.java +++ b/Mage.Sets/src/mage/cards/h/HourOfEternity.java @@ -12,12 +12,12 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.game.Game; -import mage.game.permanent.token.EmptyToken; +import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetadjustment.TargetAdjuster; -import mage.util.CardUtil; +import mage.util.functions.CopyTokenFunction; import java.util.HashSet; import java.util.Set; @@ -91,12 +91,11 @@ class HourOfEternityEffect extends OneShotEffect { for (Card card : cardsToExile) { if (game.getState().getZone(card.getId()) == Zone.EXILED) { // create token and modify all attributes permanently (without game usage) - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card, game); + Token token = CopyTokenFunction.createTokenCopy(card, game); token.removePTCDA(); token.setPower(4); token.setToughness(4); - token.getColor().setColor(ObjectColor.BLACK); + token.setColor(ObjectColor.BLACK); token.removeAllCreatureTypes(); token.addSubType(SubType.ZOMBIE); token.putOntoBattlefield(1, game, source, source.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/m/MechanizedProduction.java b/Mage.Sets/src/mage/cards/m/MechanizedProduction.java index 11e530a8710..f442cbbe469 100644 --- a/Mage.Sets/src/mage/cards/m/MechanizedProduction.java +++ b/Mage.Sets/src/mage/cards/m/MechanizedProduction.java @@ -15,11 +15,11 @@ import mage.filter.common.FilterArtifactPermanent; import mage.filter.common.FilterControlledArtifactPermanent; 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.target.TargetPermanent; import mage.target.common.TargetControlledPermanent; -import mage.util.CardUtil; +import mage.util.functions.CopyTokenFunction; import java.util.HashMap; import java.util.Map; @@ -79,8 +79,7 @@ class MechanizedProductionEffect extends OneShotEffect { if (sourceObject != null && sourceObject.getAttachedTo() != null) { Permanent enchantedArtifact = game.getPermanentOrLKIBattlefield(sourceObject.getAttachedTo()); if (enchantedArtifact != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(enchantedArtifact, game); + Token token = CopyTokenFunction.createTokenCopy(enchantedArtifact, game); token.putOntoBattlefield(1, game, source, source.getControllerId()); } Map countNames = new HashMap<>(); diff --git a/Mage.Sets/src/mage/cards/m/MyrkulLordOfBones.java b/Mage.Sets/src/mage/cards/m/MyrkulLordOfBones.java index 0e9a1a48fbd..47d54992ed7 100644 --- a/Mage.Sets/src/mage/cards/m/MyrkulLordOfBones.java +++ b/Mage.Sets/src/mage/cards/m/MyrkulLordOfBones.java @@ -104,7 +104,7 @@ class MyrkulLordOfBonesEffect extends OneShotEffect { return new CreateTokenCopyTargetEffect().setSavedPermanent( new PermanentCard(CardUtil.getDefaultCardSideForBattlefield(game, card), source.getControllerId(), game) ).setPermanentModifier((token, g) -> { - token.getCardType().clear(); + token.removeAllCardTypes(); token.addCardType(CardType.ENCHANTMENT); token.retainAllEnchantmentSubTypes(g); }).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/p/PhantomSteed.java b/Mage.Sets/src/mage/cards/p/PhantomSteed.java index 3d97e590a22..d7c36640aac 100644 --- a/Mage.Sets/src/mage/cards/p/PhantomSteed.java +++ b/Mage.Sets/src/mage/cards/p/PhantomSteed.java @@ -23,10 +23,11 @@ import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; +import mage.game.permanent.token.Token; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; +import mage.util.functions.CopyTokenFunction; import java.util.List; import java.util.Objects; @@ -99,8 +100,7 @@ class PhantomSteedEffect extends OneShotEffect { return false; } for (Card card : exileZone.getCards(game)) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card, game); + Token token = CopyTokenFunction.createTokenCopy(card, game); token.addSubType(SubType.ILLUSION); token.putOntoBattlefield(1, game, source, source.getControllerId(), true, true); List permanents = token diff --git a/Mage.Sets/src/mage/cards/p/PrototypePortal.java b/Mage.Sets/src/mage/cards/p/PrototypePortal.java index 90e04f8f3dd..81728e72dcd 100644 --- a/Mage.Sets/src/mage/cards/p/PrototypePortal.java +++ b/Mage.Sets/src/mage/cards/p/PrototypePortal.java @@ -21,10 +21,10 @@ import mage.constants.Zone; import mage.filter.StaticFilters; 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.target.TargetCard; -import mage.util.CardUtil; +import mage.util.functions.CopyTokenFunction; import java.util.UUID; @@ -149,8 +149,7 @@ class PrototypePortalCreateTokenEffect extends OneShotEffect { if (!permanent.getImprinted().isEmpty()) { Card card = game.getCard(permanent.getImprinted().get(0)); if (card != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card, game); + Token token = CopyTokenFunction.createTokenCopy(card, game); token.putOntoBattlefield(1, game, source, source.getControllerId()); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SpittingImage.java b/Mage.Sets/src/mage/cards/s/SpittingImage.java index 87f1844fa17..acf1b776a9b 100644 --- a/Mage.Sets/src/mage/cards/s/SpittingImage.java +++ b/Mage.Sets/src/mage/cards/s/SpittingImage.java @@ -11,9 +11,9 @@ 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.game.permanent.token.Token; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; +import mage.util.functions.CopyTokenFunction; import java.util.UUID; @@ -62,8 +62,7 @@ class SpittingImageEffect extends OneShotEffect { permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); } if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent, game); + Token token = CopyTokenFunction.createTokenCopy(permanent, game); token.putOntoBattlefield(1, game, source, source.getControllerId()); return true; } diff --git a/Mage.Sets/src/mage/sets/MarchOfTheMachine.java b/Mage.Sets/src/mage/sets/MarchOfTheMachine.java index 1758124ab18..d182d53a7a1 100644 --- a/Mage.Sets/src/mage/sets/MarchOfTheMachine.java +++ b/Mage.Sets/src/mage/sets/MarchOfTheMachine.java @@ -4,16 +4,11 @@ import mage.cards.ExpansionSet; import mage.constants.Rarity; import mage.constants.SetType; -import java.util.Arrays; -import java.util.List; - /** * @author TheElk801 */ public final class MarchOfTheMachine extends ExpansionSet { - private static final List unfinished = Arrays.asList("Assimilate Essence", "Blighted Burgeoning", "Bloated Processor", "Chrome Host Seedshark", "Compleated Huntmaster", "Converter Beast", "Corruption of Towashi", "Elesh Norn", "The Argent Etchings", "Elvish Vatkeeper", "Essence of Orthodoxy", "Eyes of Gitaxias", "Furnace Gremlin", "Gift of Compleation", "Glissa, Herald of Predation", "Glistening Dawn", "Ichor Drinker", "Infected Defector", "Injector Crocodile", "Marauding Dreadship", "Merciless Repurposing", "Norn's Inquisitor", "Phyrexian Awakening", "Progenitor Exarch", "Sculpted Perfection", "Searing Barb", "Sunder the Gateway", "Sunfall", "Tangled Skyline", "Tiller of Flesh", "Traumatic Revelation"); - private static final MarchOfTheMachine instance = new MarchOfTheMachine(); public static MarchOfTheMachine getInstance() { @@ -488,8 +483,6 @@ public final class MarchOfTheMachine extends ExpansionSet { cards.add(new SetCardInfo("Zimone and Dina", 318, Rarity.MYTHIC, mage.cards.z.ZimoneAndDina.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Zurgo and Ojutai", 258, Rarity.MYTHIC, mage.cards.z.ZurgoAndOjutai.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Zurgo and Ojutai", 319, Rarity.MYTHIC, mage.cards.z.ZurgoAndOjutai.class, NON_FULL_USE_VARIOUS)); - - cards.removeIf(setCardInfo -> unfinished.contains(setCardInfo.getName())); // remove when mechanic is implemented } // @Override diff --git a/Mage.Sets/src/mage/sets/MarchOfTheMachineCommander.java b/Mage.Sets/src/mage/sets/MarchOfTheMachineCommander.java index 8e8ce841579..4f00d577128 100644 --- a/Mage.Sets/src/mage/sets/MarchOfTheMachineCommander.java +++ b/Mage.Sets/src/mage/sets/MarchOfTheMachineCommander.java @@ -4,16 +4,11 @@ import mage.cards.ExpansionSet; import mage.constants.Rarity; import mage.constants.SetType; -import java.util.Arrays; -import java.util.List; - /** * @author TheElk801 */ public final class MarchOfTheMachineCommander extends ExpansionSet { - private static final List unfinished = Arrays.asList("Blight Titan", "Brimaz, Blight of Oreskos", "Excise the Imperfect"); - private static final MarchOfTheMachineCommander instance = new MarchOfTheMachineCommander(); public static MarchOfTheMachineCommander getInstance() { @@ -367,7 +362,5 @@ public final class MarchOfTheMachineCommander extends ExpansionSet { cards.add(new SetCardInfo("Workshop Elders", 245, Rarity.RARE, mage.cards.w.WorkshopElders.class)); cards.add(new SetCardInfo("Worthy Knight", 217, Rarity.RARE, mage.cards.w.WorthyKnight.class)); cards.add(new SetCardInfo("Yawgmoth's Vile Offering", 271, Rarity.RARE, mage.cards.y.YawgmothsVileOffering.class)); - - cards.removeIf(setCardInfo -> unfinished.contains(setCardInfo.getName())); // remove when mechanic is implemented } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/TokenCopyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/TokenCopyTest.java new file mode 100644 index 00000000000..43f7d59e50c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/TokenCopyTest.java @@ -0,0 +1,133 @@ +package org.mage.test.cards.copy; + +import mage.ObjectColor; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class TokenCopyTest extends CardTestPlayerBase { + + private static final String rite = "Rite of Replication"; + private static final String prowler = "Kessig Prowler"; + private static final String predator = "Sinuous Predator"; + private static final String brink = "Back from the Brink"; + + private void checkProwlers(int prowlerCount, int predatorCount) { + assertPermanentCount(playerA, prowler, prowlerCount); + assertPermanentCount(playerA, predator, predatorCount); + for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents()) { + switch (permanent.getName()) { + case prowler: + Assert.assertEquals("Power of " + prowler + " should be 2", 2, permanent.getPower().getValue()); + Assert.assertEquals("Toughness of " + prowler + " should be 1", 1, permanent.getToughness().getValue()); + Assert.assertEquals(prowler + " should be green", ObjectColor.GREEN, permanent.getColor(currentGame)); + Assert.assertTrue(prowler + " should be a Werewolf", permanent.hasSubtype(SubType.WEREWOLF, currentGame)); + Assert.assertTrue(prowler + " should be a Horror", permanent.hasSubtype(SubType.HORROR, currentGame)); + Assert.assertFalse(prowler + " should not be an Eldrazi", permanent.hasSubtype(SubType.ELDRAZI, currentGame)); + Assert.assertEquals(prowler + " should have mana value 1", 1, permanent.getManaValue()); + Assert.assertFalse(prowler + " should not be transformed", permanent.isTransformed()); + break; + case predator: + Assert.assertEquals("Power of " + predator + " should be 4", 4, permanent.getPower().getValue()); + Assert.assertEquals("Toughness of " + predator + " should be 4", 4, permanent.getToughness().getValue()); + Assert.assertTrue(predator + " should be colorless", permanent.getColor(currentGame).isColorless()); + Assert.assertTrue(predator + " should be an Eldrazi", permanent.hasSubtype(SubType.ELDRAZI, currentGame)); + Assert.assertTrue(predator + " should be a Werewolf", permanent.hasSubtype(SubType.WEREWOLF, currentGame)); + Assert.assertFalse(predator + " should not be a Horror", permanent.hasSubtype(SubType.HORROR, currentGame)); + Assert.assertEquals(predator + " should have mana value 1", 1, permanent.getManaValue()); + Assert.assertTrue(prowler + " should be transformed", permanent.isTransformed()); + break; + } + } + } + + @Test + public void testCopyDFC() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.BATTLEFIELD, playerA, prowler); + addCard(Zone.HAND, playerA, rite); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rite, prowler); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, rite, 1); + checkProwlers(1 + 1, 0); + } + + @Test + public void testCopyDFCAndTransform() { + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 5 + 5 + 4); + addCard(Zone.BATTLEFIELD, playerA, prowler); + addCard(Zone.HAND, playerA, rite); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rite, prowler); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{G}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{G}"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, rite, 1); + checkProwlers(0, 1 + 1); + } + + @Test + public void testCopyTransformedDFC() { + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 5 + 4); + addCard(Zone.BATTLEFIELD, playerA, prowler); + addCard(Zone.HAND, playerA, rite); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{G}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rite, predator); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, rite, 1); + checkProwlers(0, 1 + 1); + } + + @Test + public void testBackFromTheBrink() { + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 6 + 1); + addCard(Zone.BATTLEFIELD, playerA, brink); + addCard(Zone.GRAVEYARD, playerA, prowler); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exile"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertExileCount(playerA, prowler, 1); + checkProwlers(1, 0); + } + + @Test + public void testBackFromTheBrinkTransform() { + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 6 + 1 + 5); + addCard(Zone.BATTLEFIELD, playerA, brink); + addCard(Zone.GRAVEYARD, playerA, prowler); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exile"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{4}{G}"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertExileCount(playerA, prowler, 1); + checkProwlers(0, 1); + } +} diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java index 0262b52f03a..8a5de9fe31d 100644 --- a/Mage/src/main/java/mage/MageObject.java +++ b/Mage/src/main/java/mage/MageObject.java @@ -246,6 +246,10 @@ public interface MageObject extends MageItem, Serializable, Copyable getSuperType().add(superType); } + default void removeSuperType(SuperType superType) { + getSuperType().remove(superType); + } + default boolean isBasic() { return getSuperType().contains(SuperType.BASIC); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index f258d5712d9..aabf29e231c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -17,12 +17,12 @@ import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token; import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; import mage.util.functions.CopyApplier; +import mage.util.functions.CopyTokenFunction; import mage.util.functions.EmptyCopyApplier; import java.util.*; @@ -200,14 +200,13 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { } // create token and modify all attributes permanently (without game usage) - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(copyFrom, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) + Token token = CopyTokenFunction.createTokenCopy(copyFrom, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) applier.apply(game, token, source, targetId); if (becomesArtifact) { token.addCardType(CardType.ARTIFACT); } if (isntLegendary) { - token.getSuperType().remove(SuperType.LEGENDARY); + token.removeSuperType(SuperType.LEGENDARY); } if (startingLoyalty != -1) { @@ -238,7 +237,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { token.addSubType(additionalSubType); } if (color != null) { - token.getColor().setColor(color); + token.setColor(color); } additionalAbilities.stream().forEach(token::addAbility); if (permanentModifier != null) { diff --git a/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java b/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java index 05e20336ec0..38c6ec43709 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java @@ -13,9 +13,9 @@ import mage.constants.TimingRule; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.token.EmptyToken; +import mage.game.permanent.token.Token; import mage.players.Player; -import mage.util.CardUtil; +import mage.util.functions.CopyTokenFunction; import java.util.stream.Collectors; @@ -87,11 +87,10 @@ class EmbalmEffect extends OneShotEffect { } // create token and modify all attributes permanently (without game usage) - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) - token.getColor().setColor(ObjectColor.WHITE); + Token token = CopyTokenFunction.createTokenCopy(card, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) + token.setColor(ObjectColor.WHITE); token.addSubType(SubType.ZOMBIE); - token.getManaCost().clear(); + token.clearManaCost(); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.EMBALMED_CREATURE, token.getId(), source, controller.getId())); token.putOntoBattlefield(1, game, source, controller.getId(), false, false, null); // Probably it makes sense to remove also the Embalm ability (it's not shown on the token cards). diff --git a/Mage/src/main/java/mage/abilities/keyword/EncoreAbility.java b/Mage/src/main/java/mage/abilities/keyword/EncoreAbility.java index 237e7acbf8c..c52d4ff836e 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EncoreAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EncoreAbility.java @@ -17,9 +17,9 @@ import mage.constants.TimingRule; 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.target.targetpointer.FixedTarget; -import mage.util.CardUtil; +import mage.util.functions.CopyTokenFunction; import java.util.HashSet; import java.util.Iterator; @@ -85,8 +85,7 @@ class EncoreEffect extends OneShotEffect { if (card == null) { return false; } - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card, game); + Token token = CopyTokenFunction.createTokenCopy(card, game); Set addedTokens = new HashSet<>(); int opponentCount = OpponentsCount.instance.calculate(game, source, this); if (opponentCount < 1) { diff --git a/Mage/src/main/java/mage/abilities/keyword/EternalizeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EternalizeAbility.java index df95e97a8c9..1648f608a5d 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EternalizeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EternalizeAbility.java @@ -13,9 +13,9 @@ import mage.constants.TimingRule; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.token.EmptyToken; +import mage.game.permanent.token.Token; import mage.players.Player; -import mage.util.CardUtil; +import mage.util.functions.CopyTokenFunction; import java.util.stream.Collectors; @@ -91,11 +91,10 @@ class EternalizeEffect extends OneShotEffect { } // create token and modify all attributes permanently (without game usage) - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) - token.getColor().setColor(ObjectColor.BLACK); + Token token = CopyTokenFunction.createTokenCopy(card, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) + token.setColor(ObjectColor.BLACK); token.addSubType(SubType.ZOMBIE); - token.getManaCost().clear(); + token.clearManaCost(); token.removePTCDA(); token.setPower(4); token.setToughness(4); diff --git a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java index 8f395b8672d..49e9a83a297 100644 --- a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java @@ -1,5 +1,6 @@ package mage.abilities.keyword; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.SimpleStaticAbility; @@ -9,6 +10,7 @@ import mage.constants.*; import mage.game.Game; import mage.game.MageObjectAttribute; import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; import mage.game.stack.Spell; import mage.util.CardUtil; @@ -38,7 +40,7 @@ public class TransformAbility extends SimpleStaticAbility { return ""; } - public static void transformPermanent(Permanent permanent, Card sourceCard, Game game, Ability source) { + public static void transformPermanent(Permanent permanent, MageObject sourceCard, Game game, Ability source) { if (sourceCard == null) { return; } @@ -58,9 +60,10 @@ public class TransformAbility extends SimpleStaticAbility { for (SuperType type : sourceCard.getSuperType()) { permanent.addSuperType(type); } - + if (sourceCard instanceof Card) { + permanent.setExpansionSetCode(((Card) sourceCard).getExpansionSetCode()); + } CardUtil.copySetAndCardNumber(permanent, sourceCard); - permanent.getAbilities().clear(); for (Ability ability : sourceCard.getAbilities()) { // source == null -- call from init card (e.g. own abilities) @@ -144,7 +147,12 @@ class TransformEffect extends ContinuousEffectImpl { return true; } - Card card = permanent.getSecondCardFace(); + MageObject card; + if (permanent instanceof PermanentToken) { + card = ((PermanentToken) permanent).getToken().getBackFace(); + } else { + card = permanent.getSecondCardFace(); + } if (card == null) { return false; diff --git a/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java b/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java index c6417013ee0..a0344a261e3 100644 --- a/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java @@ -18,11 +18,10 @@ import mage.constants.TimingRule; import mage.constants.Zone; import mage.game.Game; import mage.game.command.Emblem; -import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token; import mage.game.permanent.token.custom.CreatureToken; -import mage.util.CardUtil; import mage.util.RandomUtil; +import mage.util.functions.CopyTokenFunction; import java.util.List; @@ -79,7 +78,7 @@ class MomirEffect extends OneShotEffect { } // search for a random non custom set creature - EmptyToken token = null; + Token token = null; while (!options.isEmpty()) { int index = RandomUtil.nextInt(options.size()); ExpansionSet expansionSet = Sets.findSet(options.get(index).getSetCode()); @@ -88,8 +87,7 @@ class MomirEffect extends OneShotEffect { } else { Card card = options.get(index).getCard(); if (card != null) { - token = new EmptyToken(); - CardUtil.copyTo(token).from(card, game); + token = CopyTokenFunction.createTokenCopy(card, game); break; } else { options.remove(index); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index c135add92f1..d397a7cd9f3 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -609,7 +609,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return true; } - private Card getOtherFace() { + protected MageObject getOtherFace() { return transformed ? this.getMainCard() : this.getMainCard().getSecondCardFace(); } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentToken.java b/Mage/src/main/java/mage/game/permanent/PermanentToken.java index 01df044f60c..a26064a16df 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentToken.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentToken.java @@ -5,6 +5,7 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCost; import mage.abilities.keyword.ChangelingAbility; +import mage.abilities.keyword.TransformAbility; import mage.cards.Card; import mage.constants.EmptyNames; import mage.game.Game; @@ -28,6 +29,9 @@ public class PermanentToken extends PermanentImpl { this.power = new MageInt(token.getPower().getModifiedBaseValue()); this.toughness = new MageInt(token.getToughness().getModifiedBaseValue()); this.copyFromToken(this.token, game, false); // needed to have at this time (e.g. for subtypes for entersTheBattlefield replacement effects) + if (this.token.isEntersTransformed()) { + TransformAbility.transformPermanent(this, this.token.getBackFace(), game, null); + } // token's ZCC must be synced with original token to keep abilities settings // Example: kicker ability and kicked status @@ -50,6 +54,14 @@ public class PermanentToken extends PermanentImpl { this.toughness.resetToBaseValue(); } + @Override + public int getManaValue() { + if (this.isTransformed()) { + return token.getManaValue(); + } + return super.getManaValue(); + } + @Override public String getName() { if (name.isEmpty()) { @@ -121,6 +133,16 @@ public class PermanentToken extends PermanentImpl { return this; } + @Override + public boolean isTransformable() { + return token.getBackFace() != null; + } + + @Override + protected MageObject getOtherFace() { + return this.transformed ? token : this.token.getBackFace(); + } + @Override public String getCardNumber() { return token.getOriginalCardNumber(); diff --git a/Mage/src/main/java/mage/game/permanent/token/EmptyToken.java b/Mage/src/main/java/mage/game/permanent/token/EmptyToken.java index 6a7152665ad..9b9ff133fe4 100644 --- a/Mage/src/main/java/mage/game/permanent/token/EmptyToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/EmptyToken.java @@ -1,4 +1,3 @@ - package mage.game.permanent.token; /** @@ -7,7 +6,14 @@ package mage.game.permanent.token; public final class EmptyToken extends TokenImpl { public EmptyToken() { + this(false); + } + + public EmptyToken(boolean withBackFace) { super(" Token", ""); + if (withBackFace) { + this.backFace = new EmptyToken(); + } } public EmptyToken(final EmptyToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/IncubatorToken.java b/Mage/src/main/java/mage/game/permanent/token/IncubatorToken.java index dcec1fd1428..e025147d308 100644 --- a/Mage/src/main/java/mage/game/permanent/token/IncubatorToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/IncubatorToken.java @@ -1,5 +1,9 @@ package mage.game.permanent.token; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; import mage.constants.CardType; import mage.constants.SubType; @@ -14,9 +18,12 @@ public final class IncubatorToken extends TokenImpl { super("Incubator Token", "Incubator artifact token with \"{2}: Transform this artifact.\""); cardType.add(CardType.ARTIFACT); subtype.add(SubType.INCUBATOR); + this.backFace = new Phyrexian00Token(); - // TODO: Implement this correctly - + this.addAbility(new TransformAbility()); + this.addAbility(new SimpleActivatedAbility( + new TransformSourceEffect().setText("transform this artifact"), new GenericManaCost(2) + )); availableImageSetCodes = Arrays.asList("MOM"); } diff --git a/Mage/src/main/java/mage/game/permanent/token/Phyrexian00Token.java b/Mage/src/main/java/mage/game/permanent/token/Phyrexian00Token.java new file mode 100644 index 00000000000..774311215f5 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/Phyrexian00Token.java @@ -0,0 +1,33 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public class Phyrexian00Token extends TokenImpl { + + Phyrexian00Token() { + super("Phyrexian Token", "0/0 Phyrexian artifact creature token"); + cardType.add(CardType.ARTIFACT); + cardType.add(CardType.CREATURE); + subtype.add(SubType.PHYREXIAN); + power = new MageInt(0); + toughness = new MageInt(0); + + availableImageSetCodes = Arrays.asList("MOM"); + } + + public Phyrexian00Token(final Phyrexian00Token token) { + super(token); + } + + public Phyrexian00Token copy() { + return new Phyrexian00Token(this); + } +} + diff --git a/Mage/src/main/java/mage/game/permanent/token/Token.java b/Mage/src/main/java/mage/game/permanent/token/Token.java index 6bc9ce7a4ad..d3be91f6ba7 100644 --- a/Mage/src/main/java/mage/game/permanent/token/Token.java +++ b/Mage/src/main/java/mage/game/permanent/token/Token.java @@ -2,6 +2,7 @@ package mage.game.permanent.token; import mage.MageObject; +import mage.ObjectColor; import mage.abilities.Ability; import mage.cards.Card; import mage.game.Game; @@ -58,4 +59,14 @@ public interface Token extends MageObject { void setCopySourceCard(Card copySourceCard); void setExpansionSetCodeForImage(String code); + + Token getBackFace(); + + void setColor(ObjectColor color); + + void clearManaCost(); + + void setEntersTransformed(boolean entersTransformed); + + boolean isEntersTransformed(); } diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java index 415aa4b5c40..925953efd05 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java +++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java @@ -3,6 +3,7 @@ package mage.game.permanent.token; import mage.MageInt; import mage.MageObject; import mage.MageObjectImpl; +import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.effects.Effect; @@ -12,9 +13,7 @@ import mage.cards.Card; import mage.cards.repository.TokenInfo; import mage.cards.repository.TokenRepository; import mage.cards.repository.TokenType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.command.CommandObject; import mage.game.events.CreateTokenEvent; @@ -45,21 +44,8 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { // list of set codes token images are available for protected List availableImageSetCodes = new ArrayList<>(); // TODO: delete - public enum Type { - - FIRST(1), - SECOND(2); - - int code; - - Type(int code) { - this.code = code; - } - - int getCode() { - return this.code; - } - } + protected Token backFace = null; + private boolean entersTransformed = false; public TokenImpl() { } @@ -69,7 +55,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { this.description = description; } - public TokenImpl(final TokenImpl token) { + protected TokenImpl(final TokenImpl token) { super(token); this.description = token.description; this.tokenType = token.tokenType; @@ -78,6 +64,8 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { this.originalExpansionSetCode = token.originalExpansionSetCode; this.copySourceCard = token.copySourceCard; // will never be changed this.availableImageSetCodes = token.availableImageSetCodes; + this.backFace = token.backFace != null ? token.backFace.copy() : null; + this.entersTransformed = token.entersTransformed; } @Override @@ -98,11 +86,17 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { ability.setSourceId(this.getId()); abilities.add(ability); abilities.addAll(ability.getSubAbilities()); + if (backFace != null) { + backFace.addAbility(ability); + } } // Directly from PermanentImpl @Override public void removeAbility(Ability abilityToRemove) { + if (backFace != null) { + backFace.removeAbility(abilityToRemove); + } if (abilityToRemove == null) { return; } @@ -127,6 +121,9 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { } abilitiesToRemove.forEach(a -> removeAbility(a)); + if (backFace != null) { + backFace.removeAbilities(abilitiesToRemove); + } } @Override @@ -389,11 +386,17 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { @Override public void setPower(int power) { + if (this.backFace != null) { + this.backFace.setPower(power); + } this.power = new MageInt(power); } @Override public void setToughness(int toughness) { + if (this.backFace != null) { + this.backFace.setToughness(toughness); + } this.toughness = new MageInt(toughness); } @@ -426,6 +429,21 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { return originalExpansionSetCode; } + @Override + public void setStartingLoyalty(int startingLoyalty) { + if (backFace != null) { + backFace.setStartingLoyalty(startingLoyalty); + } + super.setStartingLoyalty(startingLoyalty); + } + + public void setStartingDefense(int intArg) { + if (backFace != null) { + backFace.setStartingDefense(intArg); + } + super.setStartingDefense(intArg); + } + @Override public void setOriginalExpansionSetCode(String originalExpansionSetCode) { // TODO: delete @@ -451,4 +469,167 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { // TODO: delete setOriginalExpansionSetCode(code); } + + @Override + public Token getBackFace() { + return backFace; + } + + public void retainAllArtifactSubTypes(Game game) { + if (backFace != null) { + backFace.retainAllArtifactSubTypes(game); + } + super.retainAllArtifactSubTypes(game); + } + + public void retainAllEnchantmentSubTypes(Game game) { + if (backFace != null) { + backFace.retainAllEnchantmentSubTypes(game); + } + super.retainAllEnchantmentSubTypes(game); + } + + public void addSuperType(SuperType superType) { + if (backFace != null) { + backFace.addSuperType(superType); + } + super.addSuperType(superType); + } + + public void removeSuperType(SuperType superType) { + if (backFace != null) { + backFace.removeSuperType(superType); + } + super.removeSuperType(superType); + } + + public void addCardType(CardType... cardTypes) { + if (backFace != null) { + backFace.addCardType(cardTypes); + } + super.addCardType(cardTypes); + } + + public void removeCardType(CardType... cardTypes) { + if (backFace != null) { + backFace.removeCardType(cardTypes); + } + super.removeCardType(cardTypes); + } + + public void removeAllCardTypes() { + if (backFace != null) { + backFace.removeAllCardTypes(); + } + super.removeAllCardTypes(); + } + + public void removeAllCardTypes(Game game) { + if (backFace != null) { + backFace.removeAllCardTypes(game); + } + super.removeAllCardTypes(game); + } + + public void addSubType(SubType... subTypes) { + if (backFace != null) { + backFace.addSubType(subTypes); + } + super.addSubType(subTypes); + } + + public void removeAllSubTypes(Game game, SubTypeSet subTypeSet) { + if (backFace != null) { + backFace.removeAllSubTypes(game, subTypeSet); + } + super.removeAllSubTypes(game, subTypeSet); + } + + public void removeAllSubTypes(Game game) { + if (backFace != null) { + backFace.removeAllSubTypes(game); + } + super.removeAllSubTypes(game); + } + + public void retainAllLandSubTypes(Game game) { + if (backFace != null) { + backFace.retainAllLandSubTypes(game); + } + super.retainAllLandSubTypes(game); + } + + public void removeAllCreatureTypes(Game game) { + if (backFace != null) { + backFace.removeAllCreatureTypes(game); + } + super.removeAllCreatureTypes(game); + } + + public void removeAllCreatureTypes() { + if (backFace != null) { + backFace.removeAllCreatureTypes(); + } + super.removeAllCreatureTypes(); + } + + public void removeSubType(Game game, SubType subType) { + if (backFace != null) { + backFace.removeSubType(game, subType); + } + super.removeSubType(game, subType); + } + + public void setIsAllCreatureTypes(boolean value) { + if (backFace != null) { + backFace.setIsAllCreatureTypes(value); + } + super.setIsAllCreatureTypes(value); + } + + public void removePTCDA() { + if (backFace != null) { + backFace.removePTCDA(); + } + super.removePTCDA(); + } + + public String getName() { + if (backFace != null) { + backFace.getName(); + } + return super.getName(); + } + + public void setName(String name) { + if (backFace != null) { + backFace.setName(name); + } + super.setName(name); + } + + public void setColor(ObjectColor color) { + if (backFace != null) { + backFace.setColor(color); + } + this.getColor().setColor(color); + } + + @Override + public void clearManaCost() { + if (backFace != null) { + backFace.clearManaCost(); + } + this.getManaCost().clear(); + } + + @Override + public void setEntersTransformed(boolean entersTransformed) { + this.entersTransformed = entersTransformed; + } + + @Override + public boolean isEntersTransformed() { + return this.entersTransformed && this.backFace != null; + } } diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 4aa1858d5e8..6682a3ef3de 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -25,12 +25,13 @@ import mage.game.events.CopiedStackObjectEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; -import mage.game.permanent.token.EmptyToken; +import mage.game.permanent.token.Token; import mage.players.Player; import mage.util.CardUtil; import mage.util.GameLog; import mage.util.ManaUtil; import mage.util.SubTypes; +import mage.util.functions.CopyTokenFunction; import mage.util.functions.StackObjectCopyApplier; import org.apache.log4j.Logger; @@ -275,8 +276,7 @@ public class Spell extends StackObjectImpl implements Card { UUID permId; boolean flag; if (isCopy()) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card, game, this); + Token token = CopyTokenFunction.createTokenCopy(card, game, this); // The token that a resolving copy of a spell becomes isn’t said to have been “created.” (2020-09-25) if (token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, false)) { permId = token.getLastAddedTokenIds().stream().findFirst().orElse(null); @@ -343,8 +343,7 @@ public class Spell extends StackObjectImpl implements Card { return false; } } else if (isCopy()) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card, game, this); + Token token = CopyTokenFunction.createTokenCopy(card, game, this); // The token that a resolving copy of a spell becomes isn’t said to have been “created.” (2020-09-25) token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, false); return true; diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 31c073b67cc..ec89547e4a6 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -42,7 +42,6 @@ import mage.players.Player; import mage.target.Target; import mage.target.TargetCard; import mage.target.targetpointer.FixedTarget; -import mage.util.functions.CopyTokenFunction; import org.apache.log4j.Logger; import java.io.UnsupportedEncodingException; @@ -474,17 +473,6 @@ public final class CardUtil { spellAbility.getManaCostsToPay().addAll(adjustedCost); } - /** - * Returns function that copies params\abilities from one card to - * {@link Token}. - * - * @param target - * @return - */ - public static CopyTokenFunction copyTo(Token target) { - return new CopyTokenFunction(target); - } - /** * Converts an integer number to string Numbers > 20 will be returned as * digits @@ -1821,6 +1809,9 @@ public final class CardUtil { needSetCode = ((Card) copyFromObject).getExpansionSetCode(); needCardNumber = ((Card) copyFromObject).getCardNumber(); needTokenType = 0; + } else if (copyFromObject instanceof Token) { + // TODO: make this work + return; } else { throw new IllegalStateException("Unsupported copyFromObject class: " + copyFromObject.getClass().getSimpleName()); } diff --git a/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java b/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java index f80c3af248c..ae7e41ed58e 100644 --- a/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java +++ b/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java @@ -7,64 +7,95 @@ import mage.cards.Card; import mage.constants.CardType; import mage.constants.SuperType; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentToken; +import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token; import mage.game.stack.Spell; /** * @author nantuko */ -public class CopyTokenFunction implements Function { +public class CopyTokenFunction { - protected Token target; + protected final Token target; - public CopyTokenFunction(Token target) { + private CopyTokenFunction(Token target) { if (target == null) { throw new IllegalArgumentException("Target can't be null"); } this.target = target; } - @Override - public Token apply(Card source, Game game) { + public static Token createTokenCopy(Card source, Game game) { + return createTokenCopy(source, game, null); + } + + public static Token createTokenCopy(Card source, Game game, Spell spell) { + return new CopyTokenFunction(new EmptyToken(source.isTransformable())).from(source, game, spell); + } + + public void apply(Card source, Game game) { if (target == null) { throw new IllegalArgumentException("Target can't be null"); } // 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; + target.setEntersTransformed(source instanceof Permanent && ((Permanent) source).isTransformed()); + MageObject sourceObj; if (source instanceof PermanentToken) { // create token from another token - sourceObj = ((PermanentToken) source).getToken(); + Token sourceToken = ((PermanentToken) source).getToken(); + sourceObj = sourceToken; + copyToToken(target, sourceObj, game); + if (sourceToken.getBackFace() != null) { + target.getBackFace().setOriginalExpansionSetCode(sourceToken.getOriginalExpansionSetCode()); + target.getBackFace().setOriginalCardNumber(sourceToken.getOriginalCardNumber()); + target.getBackFace().setCopySourceCard(sourceToken.getBackFace().getCopySourceCard()); + copyToToken(target.getBackFace(), sourceToken.getBackFace(), game); + } // to show the source image, the original values have to be used - target.setOriginalExpansionSetCode(((Token) sourceObj).getOriginalExpansionSetCode()); - target.setOriginalCardNumber(((Token) sourceObj).getOriginalCardNumber()); + target.setOriginalExpansionSetCode(sourceToken.getOriginalExpansionSetCode()); + target.setOriginalCardNumber(sourceToken.getOriginalCardNumber()); target.setCopySourceCard(((PermanentToken) source).getToken().getCopySourceCard()); - } else if (source instanceof PermanentCard) { + return; + } + if (source instanceof PermanentCard) { // create token from non-token permanent if (((PermanentCard) source).isMorphed() || ((PermanentCard) source).isManifested()) { MorphAbility.setPermanentToFaceDownCreature(target, game); - return target; - } else { - if (((PermanentCard) source).isTransformed() && source.getSecondCardFace() != null) { - sourceObj = ((PermanentCard) source).getSecondCardFace(); - } else { - sourceObj = ((PermanentCard) source).getCard(); - } - - target.setOriginalExpansionSetCode(source.getExpansionSetCode()); - target.setOriginalCardNumber(source.getCardNumber()); - target.setCopySourceCard((Card) sourceObj); + return; } - } else { - // create token from non-permanent object like card (example: Embalm ability) + sourceObj = ((PermanentCard) source).getMainCard(); + copyToToken(target, sourceObj, game); + if (((Card) sourceObj).isTransformable()) { + target.getBackFace().setOriginalExpansionSetCode(source.getExpansionSetCode()); + target.getBackFace().setOriginalCardNumber(source.getCardNumber()); + target.getBackFace().setCopySourceCard(((Card) sourceObj).getSecondCardFace()); + copyToToken(target.getBackFace(), ((Card) sourceObj).getSecondCardFace(), game); + } + target.setOriginalExpansionSetCode(source.getExpansionSetCode()); target.setOriginalCardNumber(source.getCardNumber()); - target.setCopySourceCard(source); + target.setCopySourceCard((Card) sourceObj); + return; } + sourceObj = source; + copyToToken(target, sourceObj, game); + if (source.isTransformable()) { + target.getBackFace().setOriginalExpansionSetCode(source.getExpansionSetCode()); + target.getBackFace().setOriginalCardNumber(source.getCardNumber()); + target.getBackFace().setCopySourceCard(source.getSecondCardFace()); + copyToToken(target.getBackFace(), source.getSecondCardFace(), game); + } + // create token from non-permanent object like card (example: Embalm ability) + target.setOriginalExpansionSetCode(source.getExpansionSetCode()); + target.setOriginalCardNumber(source.getCardNumber()); + target.setCopySourceCard(source); + } + private static Token copyToToken(Token target, MageObject sourceObj, Game game) { // modify all attributes permanently (without game usage) target.setName(sourceObj.getName()); target.getColor().setColor(sourceObj.getColor()); @@ -101,11 +132,7 @@ public class CopyTokenFunction implements Function { return target; } - public Token from(Card source, Game game) { - return from(source, game, null); - } - - public Token from(Card source, Game game, Spell spell) { + private Token from(Card source, Game game, Spell spell) { apply(source, game); // token's ZCC must be synced with original card to keep abilities settings diff --git a/Mage/src/main/java/mage/util/functions/Function.java b/Mage/src/main/java/mage/util/functions/Function.java deleted file mode 100644 index c8f4817fca6..00000000000 --- a/Mage/src/main/java/mage/util/functions/Function.java +++ /dev/null @@ -1,11 +0,0 @@ -package mage.util.functions; - -import mage.game.Game; - -/** - * @author nantuko - */ -@FunctionalInterface -public interface Function { - X apply(Y in, Game game); -}