1
0
Fork 0
mirror of https://github.com/correl/mage.git synced 2025-01-15 19:13:24 +00:00

Implement DFC tokens for Incubate ()

* remove incubate skip

* initial implementation of DFC tokens

* separate incubator back face to separate class

* small refactor to token copy function

* token copies now have back faces as well

* effects which modify token copies now correctly apply to both faces

* add skip for exception

* tokens now enter transformed correctly

* [MOC] remove skip for incubate

* fix verify failure
This commit is contained in:
Evan Kranzler 2023-04-27 11:03:25 -04:00 committed by GitHub
parent f8d23ff56b
commit 726e289646
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 540 additions and 156 deletions

View file

@ -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. // At the beginning of each end step, if a Phyrexian died under your control this turn, proliferate.
this.addAbility(new BeginningOfEndStepTriggeredAbility( this.addAbility(new BeginningOfEndStepTriggeredAbility(
new ProliferateEffect(), TargetController.ANY, new ProliferateEffect(false), TargetController.ANY,
BrimazBlightOfOreskosCondition.instance, false BrimazBlightOfOreskosCondition.instance, false
), new BrimazBlightOfOreskosWatcher()); ), new BrimazBlightOfOreskosWatcher());
} }

View file

@ -18,12 +18,11 @@ import mage.game.Game;
import mage.game.events.CreateTokenEvent; import mage.game.events.CreateTokenEvent;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken;
import mage.game.permanent.token.Token; import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import mage.util.CardUtil;
import mage.util.functions.CopyApplier; import mage.util.functions.CopyApplier;
import mage.util.functions.CopyTokenFunction;
import mage.util.functions.EmptyCopyApplier; import mage.util.functions.EmptyCopyApplier;
import mage.watchers.Watcher; import mage.watchers.Watcher;
@ -145,8 +144,7 @@ class EsixFractalBloomEffect extends ReplacementEffectImpl {
} }
// create token and modify all attributes permanently (without game usage) // create token and modify all attributes permanently (without game usage)
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(copyFromPermanent, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer)
CardUtil.copyTo(token).from(copyFromPermanent, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer)
applier.apply(game, token, source, permanent.getId()); applier.apply(game, token, source, permanent.getId());
return token; return token;
} }

View file

@ -14,11 +14,11 @@ import mage.constants.*;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCardInYourGraveyard;
import mage.target.targetpointer.FixedTargets; import mage.target.targetpointer.FixedTargets;
import mage.util.CardUtil; import mage.util.functions.CopyTokenFunction;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -83,12 +83,11 @@ class GodPharaohsGiftEffect extends OneShotEffect {
return false; return false;
} }
// create token and modify all attributes permanently (without game usage) // create token and modify all attributes permanently (without game usage)
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(cardChosen, game);
CardUtil.copyTo(token).from(cardChosen, game);
token.removePTCDA(); token.removePTCDA();
token.setPower(4); token.setPower(4);
token.setToughness(4); token.setToughness(4);
token.getColor().setColor(ObjectColor.BLACK); token.setColor(ObjectColor.BLACK);
token.removeAllCreatureTypes(); token.removeAllCreatureTypes();
token.addSubType(SubType.ZOMBIE); token.addSubType(SubType.ZOMBIE);
token.putOntoBattlefield(1, game, source, source.getControllerId()); token.putOntoBattlefield(1, game, source, source.getControllerId());

View file

@ -12,12 +12,12 @@ import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreatureCard;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
import mage.target.Target; import mage.target.Target;
import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCardInYourGraveyard;
import mage.target.targetadjustment.TargetAdjuster; import mage.target.targetadjustment.TargetAdjuster;
import mage.util.CardUtil; import mage.util.functions.CopyTokenFunction;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -91,12 +91,11 @@ class HourOfEternityEffect extends OneShotEffect {
for (Card card : cardsToExile) { for (Card card : cardsToExile) {
if (game.getState().getZone(card.getId()) == Zone.EXILED) { if (game.getState().getZone(card.getId()) == Zone.EXILED) {
// create token and modify all attributes permanently (without game usage) // create token and modify all attributes permanently (without game usage)
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(card, game);
CardUtil.copyTo(token).from(card, game);
token.removePTCDA(); token.removePTCDA();
token.setPower(4); token.setPower(4);
token.setToughness(4); token.setToughness(4);
token.getColor().setColor(ObjectColor.BLACK); token.setColor(ObjectColor.BLACK);
token.removeAllCreatureTypes(); token.removeAllCreatureTypes();
token.addSubType(SubType.ZOMBIE); token.addSubType(SubType.ZOMBIE);
token.putOntoBattlefield(1, game, source, source.getControllerId()); token.putOntoBattlefield(1, game, source, source.getControllerId());

View file

@ -15,11 +15,11 @@ import mage.filter.common.FilterArtifactPermanent;
import mage.filter.common.FilterControlledArtifactPermanent; import mage.filter.common.FilterControlledArtifactPermanent;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetControlledPermanent;
import mage.util.CardUtil; import mage.util.functions.CopyTokenFunction;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -79,8 +79,7 @@ class MechanizedProductionEffect extends OneShotEffect {
if (sourceObject != null && sourceObject.getAttachedTo() != null) { if (sourceObject != null && sourceObject.getAttachedTo() != null) {
Permanent enchantedArtifact = game.getPermanentOrLKIBattlefield(sourceObject.getAttachedTo()); Permanent enchantedArtifact = game.getPermanentOrLKIBattlefield(sourceObject.getAttachedTo());
if (enchantedArtifact != null) { if (enchantedArtifact != null) {
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(enchantedArtifact, game);
CardUtil.copyTo(token).from(enchantedArtifact, game);
token.putOntoBattlefield(1, game, source, source.getControllerId()); token.putOntoBattlefield(1, game, source, source.getControllerId());
} }
Map<String, Integer> countNames = new HashMap<>(); Map<String, Integer> countNames = new HashMap<>();

View file

@ -104,7 +104,7 @@ class MyrkulLordOfBonesEffect extends OneShotEffect {
return new CreateTokenCopyTargetEffect().setSavedPermanent( return new CreateTokenCopyTargetEffect().setSavedPermanent(
new PermanentCard(CardUtil.getDefaultCardSideForBattlefield(game, card), source.getControllerId(), game) new PermanentCard(CardUtil.getDefaultCardSideForBattlefield(game, card), source.getControllerId(), game)
).setPermanentModifier((token, g) -> { ).setPermanentModifier((token, g) -> {
token.getCardType().clear(); token.removeAllCardTypes();
token.addCardType(CardType.ENCHANTMENT); token.addCardType(CardType.ENCHANTMENT);
token.retainAllEnchantmentSubTypes(g); token.retainAllEnchantmentSubTypes(g);
}).apply(game, source); }).apply(game, source);

View file

@ -23,10 +23,11 @@ import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.game.ExileZone; import mage.game.ExileZone;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import mage.target.targetpointer.FixedTargets; import mage.target.targetpointer.FixedTargets;
import mage.util.CardUtil; import mage.util.CardUtil;
import mage.util.functions.CopyTokenFunction;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -99,8 +100,7 @@ class PhantomSteedEffect extends OneShotEffect {
return false; return false;
} }
for (Card card : exileZone.getCards(game)) { for (Card card : exileZone.getCards(game)) {
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(card, game);
CardUtil.copyTo(token).from(card, game);
token.addSubType(SubType.ILLUSION); token.addSubType(SubType.ILLUSION);
token.putOntoBattlefield(1, game, source, source.getControllerId(), true, true); token.putOntoBattlefield(1, game, source, source.getControllerId(), true, true);
List<Permanent> permanents = token List<Permanent> permanents = token

View file

@ -21,10 +21,10 @@ import mage.constants.Zone;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetCard; import mage.target.TargetCard;
import mage.util.CardUtil; import mage.util.functions.CopyTokenFunction;
import java.util.UUID; import java.util.UUID;
@ -149,8 +149,7 @@ class PrototypePortalCreateTokenEffect extends OneShotEffect {
if (!permanent.getImprinted().isEmpty()) { if (!permanent.getImprinted().isEmpty()) {
Card card = game.getCard(permanent.getImprinted().get(0)); Card card = game.getCard(permanent.getImprinted().get(0));
if (card != null) { if (card != null) {
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(card, game);
CardUtil.copyTo(token).from(card, game);
token.putOntoBattlefield(1, game, source, source.getControllerId()); token.putOntoBattlefield(1, game, source, source.getControllerId());
return true; return true;
} }

View file

@ -11,9 +11,9 @@ import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
import mage.util.CardUtil; import mage.util.functions.CopyTokenFunction;
import java.util.UUID; import java.util.UUID;
@ -62,8 +62,7 @@ class SpittingImageEffect extends OneShotEffect {
permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD);
} }
if (permanent != null) { if (permanent != null) {
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(permanent, game);
CardUtil.copyTo(token).from(permanent, game);
token.putOntoBattlefield(1, game, source, source.getControllerId()); token.putOntoBattlefield(1, game, source, source.getControllerId());
return true; return true;
} }

View file

@ -4,16 +4,11 @@ import mage.cards.ExpansionSet;
import mage.constants.Rarity; import mage.constants.Rarity;
import mage.constants.SetType; import mage.constants.SetType;
import java.util.Arrays;
import java.util.List;
/** /**
* @author TheElk801 * @author TheElk801
*/ */
public final class MarchOfTheMachine extends ExpansionSet { public final class MarchOfTheMachine extends ExpansionSet {
private static final List<String> 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(); private static final MarchOfTheMachine instance = new MarchOfTheMachine();
public static MarchOfTheMachine getInstance() { 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("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", 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.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 // @Override

View file

@ -4,16 +4,11 @@ import mage.cards.ExpansionSet;
import mage.constants.Rarity; import mage.constants.Rarity;
import mage.constants.SetType; import mage.constants.SetType;
import java.util.Arrays;
import java.util.List;
/** /**
* @author TheElk801 * @author TheElk801
*/ */
public final class MarchOfTheMachineCommander extends ExpansionSet { public final class MarchOfTheMachineCommander extends ExpansionSet {
private static final List<String> unfinished = Arrays.asList("Blight Titan", "Brimaz, Blight of Oreskos", "Excise the Imperfect");
private static final MarchOfTheMachineCommander instance = new MarchOfTheMachineCommander(); private static final MarchOfTheMachineCommander instance = new MarchOfTheMachineCommander();
public static MarchOfTheMachineCommander getInstance() { 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("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("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.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
} }
} }

View file

@ -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);
}
}

View file

@ -246,6 +246,10 @@ public interface MageObject extends MageItem, Serializable, Copyable<MageObject>
getSuperType().add(superType); getSuperType().add(superType);
} }
default void removeSuperType(SuperType superType) {
getSuperType().remove(superType);
}
default boolean isBasic() { default boolean isBasic() {
return getSuperType().contains(SuperType.BASIC); return getSuperType().contains(SuperType.BASIC);
} }

View file

@ -17,12 +17,12 @@ import mage.constants.*;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken;
import mage.game.permanent.token.Token; import mage.game.permanent.token.Token;
import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.FixedTargets; import mage.target.targetpointer.FixedTargets;
import mage.util.CardUtil; import mage.util.CardUtil;
import mage.util.functions.CopyApplier; import mage.util.functions.CopyApplier;
import mage.util.functions.CopyTokenFunction;
import mage.util.functions.EmptyCopyApplier; import mage.util.functions.EmptyCopyApplier;
import java.util.*; import java.util.*;
@ -200,14 +200,13 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
} }
// create token and modify all attributes permanently (without game usage) // create token and modify all attributes permanently (without game usage)
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(copyFrom, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer)
CardUtil.copyTo(token).from(copyFrom, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer)
applier.apply(game, token, source, targetId); applier.apply(game, token, source, targetId);
if (becomesArtifact) { if (becomesArtifact) {
token.addCardType(CardType.ARTIFACT); token.addCardType(CardType.ARTIFACT);
} }
if (isntLegendary) { if (isntLegendary) {
token.getSuperType().remove(SuperType.LEGENDARY); token.removeSuperType(SuperType.LEGENDARY);
} }
if (startingLoyalty != -1) { if (startingLoyalty != -1) {
@ -238,7 +237,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
token.addSubType(additionalSubType); token.addSubType(additionalSubType);
} }
if (color != null) { if (color != null) {
token.getColor().setColor(color); token.setColor(color);
} }
additionalAbilities.stream().forEach(token::addAbility); additionalAbilities.stream().forEach(token::addAbility);
if (permanentModifier != null) { if (permanentModifier != null) {

View file

@ -13,9 +13,9 @@ import mage.constants.TimingRule;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil; import mage.util.functions.CopyTokenFunction;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -87,11 +87,10 @@ class EmbalmEffect extends OneShotEffect {
} }
// create token and modify all attributes permanently (without game usage) // create token and modify all attributes permanently (without game usage)
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(card, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer)
CardUtil.copyTo(token).from(card, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) token.setColor(ObjectColor.WHITE);
token.getColor().setColor(ObjectColor.WHITE);
token.addSubType(SubType.ZOMBIE); token.addSubType(SubType.ZOMBIE);
token.getManaCost().clear(); token.clearManaCost();
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.EMBALMED_CREATURE, token.getId(), source, controller.getId())); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.EMBALMED_CREATURE, token.getId(), source, controller.getId()));
token.putOntoBattlefield(1, game, source, controller.getId(), false, false, null); 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). // Probably it makes sense to remove also the Embalm ability (it's not shown on the token cards).

View file

@ -17,9 +17,9 @@ import mage.constants.TimingRule;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; 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.FixedTarget;
import mage.util.CardUtil; import mage.util.functions.CopyTokenFunction;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -85,8 +85,7 @@ class EncoreEffect extends OneShotEffect {
if (card == null) { if (card == null) {
return false; return false;
} }
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(card, game);
CardUtil.copyTo(token).from(card, game);
Set<MageObjectReference> addedTokens = new HashSet<>(); Set<MageObjectReference> addedTokens = new HashSet<>();
int opponentCount = OpponentsCount.instance.calculate(game, source, this); int opponentCount = OpponentsCount.instance.calculate(game, source, this);
if (opponentCount < 1) { if (opponentCount < 1) {

View file

@ -13,9 +13,9 @@ import mage.constants.TimingRule;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil; import mage.util.functions.CopyTokenFunction;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -91,11 +91,10 @@ class EternalizeEffect extends OneShotEffect {
} }
// create token and modify all attributes permanently (without game usage) // create token and modify all attributes permanently (without game usage)
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(card, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer)
CardUtil.copyTo(token).from(card, game); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) token.setColor(ObjectColor.BLACK);
token.getColor().setColor(ObjectColor.BLACK);
token.addSubType(SubType.ZOMBIE); token.addSubType(SubType.ZOMBIE);
token.getManaCost().clear(); token.clearManaCost();
token.removePTCDA(); token.removePTCDA();
token.setPower(4); token.setPower(4);
token.setToughness(4); token.setToughness(4);

View file

@ -1,5 +1,6 @@
package mage.abilities.keyword; package mage.abilities.keyword;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.Mode; import mage.abilities.Mode;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
@ -9,6 +10,7 @@ import mage.constants.*;
import mage.game.Game; import mage.game.Game;
import mage.game.MageObjectAttribute; import mage.game.MageObjectAttribute;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.util.CardUtil; import mage.util.CardUtil;
@ -38,7 +40,7 @@ public class TransformAbility extends SimpleStaticAbility {
return ""; 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) { if (sourceCard == null) {
return; return;
} }
@ -58,9 +60,10 @@ public class TransformAbility extends SimpleStaticAbility {
for (SuperType type : sourceCard.getSuperType()) { for (SuperType type : sourceCard.getSuperType()) {
permanent.addSuperType(type); permanent.addSuperType(type);
} }
if (sourceCard instanceof Card) {
permanent.setExpansionSetCode(((Card) sourceCard).getExpansionSetCode());
}
CardUtil.copySetAndCardNumber(permanent, sourceCard); CardUtil.copySetAndCardNumber(permanent, sourceCard);
permanent.getAbilities().clear(); permanent.getAbilities().clear();
for (Ability ability : sourceCard.getAbilities()) { for (Ability ability : sourceCard.getAbilities()) {
// source == null -- call from init card (e.g. own abilities) // source == null -- call from init card (e.g. own abilities)
@ -144,7 +147,12 @@ class TransformEffect extends ContinuousEffectImpl {
return true; return true;
} }
Card card = permanent.getSecondCardFace(); MageObject card;
if (permanent instanceof PermanentToken) {
card = ((PermanentToken) permanent).getToken().getBackFace();
} else {
card = permanent.getSecondCardFace();
}
if (card == null) { if (card == null) {
return false; return false;

View file

@ -18,11 +18,10 @@ import mage.constants.TimingRule;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.command.Emblem; import mage.game.command.Emblem;
import mage.game.permanent.token.EmptyToken;
import mage.game.permanent.token.Token; import mage.game.permanent.token.Token;
import mage.game.permanent.token.custom.CreatureToken; import mage.game.permanent.token.custom.CreatureToken;
import mage.util.CardUtil;
import mage.util.RandomUtil; import mage.util.RandomUtil;
import mage.util.functions.CopyTokenFunction;
import java.util.List; import java.util.List;
@ -79,7 +78,7 @@ class MomirEffect extends OneShotEffect {
} }
// search for a random non custom set creature // search for a random non custom set creature
EmptyToken token = null; Token token = null;
while (!options.isEmpty()) { while (!options.isEmpty()) {
int index = RandomUtil.nextInt(options.size()); int index = RandomUtil.nextInt(options.size());
ExpansionSet expansionSet = Sets.findSet(options.get(index).getSetCode()); ExpansionSet expansionSet = Sets.findSet(options.get(index).getSetCode());
@ -88,8 +87,7 @@ class MomirEffect extends OneShotEffect {
} else { } else {
Card card = options.get(index).getCard(); Card card = options.get(index).getCard();
if (card != null) { if (card != null) {
token = new EmptyToken(); token = CopyTokenFunction.createTokenCopy(card, game);
CardUtil.copyTo(token).from(card, game);
break; break;
} else { } else {
options.remove(index); options.remove(index);

View file

@ -609,7 +609,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return true; return true;
} }
private Card getOtherFace() { protected MageObject getOtherFace() {
return transformed ? this.getMainCard() : this.getMainCard().getSecondCardFace(); return transformed ? this.getMainCard() : this.getMainCard().getSecondCardFace();
} }

View file

@ -5,6 +5,7 @@ import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCost;
import mage.abilities.keyword.ChangelingAbility; import mage.abilities.keyword.ChangelingAbility;
import mage.abilities.keyword.TransformAbility;
import mage.cards.Card; import mage.cards.Card;
import mage.constants.EmptyNames; import mage.constants.EmptyNames;
import mage.game.Game; import mage.game.Game;
@ -28,6 +29,9 @@ public class PermanentToken extends PermanentImpl {
this.power = new MageInt(token.getPower().getModifiedBaseValue()); this.power = new MageInt(token.getPower().getModifiedBaseValue());
this.toughness = new MageInt(token.getToughness().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) 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 // token's ZCC must be synced with original token to keep abilities settings
// Example: kicker ability and kicked status // Example: kicker ability and kicked status
@ -50,6 +54,14 @@ public class PermanentToken extends PermanentImpl {
this.toughness.resetToBaseValue(); this.toughness.resetToBaseValue();
} }
@Override
public int getManaValue() {
if (this.isTransformed()) {
return token.getManaValue();
}
return super.getManaValue();
}
@Override @Override
public String getName() { public String getName() {
if (name.isEmpty()) { if (name.isEmpty()) {
@ -121,6 +133,16 @@ public class PermanentToken extends PermanentImpl {
return this; return this;
} }
@Override
public boolean isTransformable() {
return token.getBackFace() != null;
}
@Override
protected MageObject getOtherFace() {
return this.transformed ? token : this.token.getBackFace();
}
@Override @Override
public String getCardNumber() { public String getCardNumber() {
return token.getOriginalCardNumber(); return token.getOriginalCardNumber();

View file

@ -1,4 +1,3 @@
package mage.game.permanent.token; package mage.game.permanent.token;
/** /**
@ -7,7 +6,14 @@ package mage.game.permanent.token;
public final class EmptyToken extends TokenImpl { public final class EmptyToken extends TokenImpl {
public EmptyToken() { public EmptyToken() {
this(false);
}
public EmptyToken(boolean withBackFace) {
super(" Token", ""); super(" Token", "");
if (withBackFace) {
this.backFace = new EmptyToken();
}
} }
public EmptyToken(final EmptyToken token) { public EmptyToken(final EmptyToken token) {

View file

@ -1,5 +1,9 @@
package mage.game.permanent.token; 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.CardType;
import mage.constants.SubType; 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.\""); super("Incubator Token", "Incubator artifact token with \"{2}: Transform this artifact.\"");
cardType.add(CardType.ARTIFACT); cardType.add(CardType.ARTIFACT);
subtype.add(SubType.INCUBATOR); 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"); availableImageSetCodes = Arrays.asList("MOM");
} }

View file

@ -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);
}
}

View file

@ -2,6 +2,7 @@
package mage.game.permanent.token; package mage.game.permanent.token;
import mage.MageObject; import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.cards.Card; import mage.cards.Card;
import mage.game.Game; import mage.game.Game;
@ -58,4 +59,14 @@ public interface Token extends MageObject {
void setCopySourceCard(Card copySourceCard); void setCopySourceCard(Card copySourceCard);
void setExpansionSetCodeForImage(String code); void setExpansionSetCodeForImage(String code);
Token getBackFace();
void setColor(ObjectColor color);
void clearManaCost();
void setEntersTransformed(boolean entersTransformed);
boolean isEntersTransformed();
} }

View file

@ -3,6 +3,7 @@ package mage.game.permanent.token;
import mage.MageInt; import mage.MageInt;
import mage.MageObject; import mage.MageObject;
import mage.MageObjectImpl; import mage.MageObjectImpl;
import mage.ObjectColor;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.SpellAbility; import mage.abilities.SpellAbility;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
@ -12,9 +13,7 @@ import mage.cards.Card;
import mage.cards.repository.TokenInfo; import mage.cards.repository.TokenInfo;
import mage.cards.repository.TokenRepository; import mage.cards.repository.TokenRepository;
import mage.cards.repository.TokenType; import mage.cards.repository.TokenType;
import mage.constants.Outcome; import mage.constants.*;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.command.CommandObject; import mage.game.command.CommandObject;
import mage.game.events.CreateTokenEvent; 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 // list of set codes token images are available for
protected List<String> availableImageSetCodes = new ArrayList<>(); // TODO: delete protected List<String> availableImageSetCodes = new ArrayList<>(); // TODO: delete
public enum Type { protected Token backFace = null;
private boolean entersTransformed = false;
FIRST(1),
SECOND(2);
int code;
Type(int code) {
this.code = code;
}
int getCode() {
return this.code;
}
}
public TokenImpl() { public TokenImpl() {
} }
@ -69,7 +55,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
this.description = description; this.description = description;
} }
public TokenImpl(final TokenImpl token) { protected TokenImpl(final TokenImpl token) {
super(token); super(token);
this.description = token.description; this.description = token.description;
this.tokenType = token.tokenType; this.tokenType = token.tokenType;
@ -78,6 +64,8 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
this.originalExpansionSetCode = token.originalExpansionSetCode; this.originalExpansionSetCode = token.originalExpansionSetCode;
this.copySourceCard = token.copySourceCard; // will never be changed this.copySourceCard = token.copySourceCard; // will never be changed
this.availableImageSetCodes = token.availableImageSetCodes; this.availableImageSetCodes = token.availableImageSetCodes;
this.backFace = token.backFace != null ? token.backFace.copy() : null;
this.entersTransformed = token.entersTransformed;
} }
@Override @Override
@ -98,11 +86,17 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
ability.setSourceId(this.getId()); ability.setSourceId(this.getId());
abilities.add(ability); abilities.add(ability);
abilities.addAll(ability.getSubAbilities()); abilities.addAll(ability.getSubAbilities());
if (backFace != null) {
backFace.addAbility(ability);
}
} }
// Directly from PermanentImpl // Directly from PermanentImpl
@Override @Override
public void removeAbility(Ability abilityToRemove) { public void removeAbility(Ability abilityToRemove) {
if (backFace != null) {
backFace.removeAbility(abilityToRemove);
}
if (abilityToRemove == null) { if (abilityToRemove == null) {
return; return;
} }
@ -127,6 +121,9 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
} }
abilitiesToRemove.forEach(a -> removeAbility(a)); abilitiesToRemove.forEach(a -> removeAbility(a));
if (backFace != null) {
backFace.removeAbilities(abilitiesToRemove);
}
} }
@Override @Override
@ -389,11 +386,17 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
@Override @Override
public void setPower(int power) { public void setPower(int power) {
if (this.backFace != null) {
this.backFace.setPower(power);
}
this.power = new MageInt(power); this.power = new MageInt(power);
} }
@Override @Override
public void setToughness(int toughness) { public void setToughness(int toughness) {
if (this.backFace != null) {
this.backFace.setToughness(toughness);
}
this.toughness = new MageInt(toughness); this.toughness = new MageInt(toughness);
} }
@ -426,6 +429,21 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
return originalExpansionSetCode; 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 @Override
public void setOriginalExpansionSetCode(String originalExpansionSetCode) { public void setOriginalExpansionSetCode(String originalExpansionSetCode) {
// TODO: delete // TODO: delete
@ -451,4 +469,167 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
// TODO: delete // TODO: delete
setOriginalExpansionSetCode(code); 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;
}
} }

View file

@ -25,12 +25,13 @@ import mage.game.events.CopiedStackObjectEvent;
import mage.game.events.ZoneChangeEvent; import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentCard;
import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil; import mage.util.CardUtil;
import mage.util.GameLog; import mage.util.GameLog;
import mage.util.ManaUtil; import mage.util.ManaUtil;
import mage.util.SubTypes; import mage.util.SubTypes;
import mage.util.functions.CopyTokenFunction;
import mage.util.functions.StackObjectCopyApplier; import mage.util.functions.StackObjectCopyApplier;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -275,8 +276,7 @@ public class Spell extends StackObjectImpl implements Card {
UUID permId; UUID permId;
boolean flag; boolean flag;
if (isCopy()) { if (isCopy()) {
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(card, game, this);
CardUtil.copyTo(token).from(card, game, this);
// The token that a resolving copy of a spell becomes isnt said to have been created. (2020-09-25) // The token that a resolving copy of a spell becomes isnt said to have been created. (2020-09-25)
if (token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, false)) { if (token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, false)) {
permId = token.getLastAddedTokenIds().stream().findFirst().orElse(null); permId = token.getLastAddedTokenIds().stream().findFirst().orElse(null);
@ -343,8 +343,7 @@ public class Spell extends StackObjectImpl implements Card {
return false; return false;
} }
} else if (isCopy()) { } else if (isCopy()) {
EmptyToken token = new EmptyToken(); Token token = CopyTokenFunction.createTokenCopy(card, game, this);
CardUtil.copyTo(token).from(card, game, this);
// The token that a resolving copy of a spell becomes isnt said to have been created. (2020-09-25) // The token that a resolving copy of a spell becomes isnt said to have been created. (2020-09-25)
token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, false); token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, false);
return true; return true;

View file

@ -42,7 +42,6 @@ import mage.players.Player;
import mage.target.Target; import mage.target.Target;
import mage.target.TargetCard; import mage.target.TargetCard;
import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTarget;
import mage.util.functions.CopyTokenFunction;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@ -474,17 +473,6 @@ public final class CardUtil {
spellAbility.getManaCostsToPay().addAll(adjustedCost); 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 * Converts an integer number to string Numbers > 20 will be returned as
* digits * digits
@ -1821,6 +1809,9 @@ public final class CardUtil {
needSetCode = ((Card) copyFromObject).getExpansionSetCode(); needSetCode = ((Card) copyFromObject).getExpansionSetCode();
needCardNumber = ((Card) copyFromObject).getCardNumber(); needCardNumber = ((Card) copyFromObject).getCardNumber();
needTokenType = 0; needTokenType = 0;
} else if (copyFromObject instanceof Token) {
// TODO: make this work
return;
} else { } else {
throw new IllegalStateException("Unsupported copyFromObject class: " + copyFromObject.getClass().getSimpleName()); throw new IllegalStateException("Unsupported copyFromObject class: " + copyFromObject.getClass().getSimpleName());
} }

View file

@ -7,64 +7,95 @@ import mage.cards.Card;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentCard;
import mage.game.permanent.PermanentToken; import mage.game.permanent.PermanentToken;
import mage.game.permanent.token.EmptyToken;
import mage.game.permanent.token.Token; import mage.game.permanent.token.Token;
import mage.game.stack.Spell; import mage.game.stack.Spell;
/** /**
* @author nantuko * @author nantuko
*/ */
public class CopyTokenFunction implements Function<Token, Card> { public class CopyTokenFunction {
protected Token target; protected final Token target;
public CopyTokenFunction(Token target) { private CopyTokenFunction(Token target) {
if (target == null) { if (target == null) {
throw new IllegalArgumentException("Target can't be null"); throw new IllegalArgumentException("Target can't be null");
} }
this.target = target; this.target = target;
} }
@Override public static Token createTokenCopy(Card source, Game game) {
public Token apply(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) { if (target == null) {
throw new IllegalArgumentException("Target can't be 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 // 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. // else gained abililies would be copied too.
target.setEntersTransformed(source instanceof Permanent && ((Permanent) source).isTransformed());
MageObject sourceObj = source; MageObject sourceObj;
if (source instanceof PermanentToken) { if (source instanceof PermanentToken) {
// create token from another token // 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 // to show the source image, the original values have to be used
target.setOriginalExpansionSetCode(((Token) sourceObj).getOriginalExpansionSetCode()); target.setOriginalExpansionSetCode(sourceToken.getOriginalExpansionSetCode());
target.setOriginalCardNumber(((Token) sourceObj).getOriginalCardNumber()); target.setOriginalCardNumber(sourceToken.getOriginalCardNumber());
target.setCopySourceCard(((PermanentToken) source).getToken().getCopySourceCard()); target.setCopySourceCard(((PermanentToken) source).getToken().getCopySourceCard());
} else if (source instanceof PermanentCard) { return;
}
if (source instanceof PermanentCard) {
// create token from non-token permanent // create token from non-token permanent
if (((PermanentCard) source).isMorphed() || ((PermanentCard) source).isManifested()) { if (((PermanentCard) source).isMorphed() || ((PermanentCard) source).isManifested()) {
MorphAbility.setPermanentToFaceDownCreature(target, game); MorphAbility.setPermanentToFaceDownCreature(target, game);
return target; return;
} 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);
} }
} else { sourceObj = ((PermanentCard) source).getMainCard();
// create token from non-permanent object like card (example: Embalm ability) 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.setOriginalExpansionSetCode(source.getExpansionSetCode());
target.setOriginalCardNumber(source.getCardNumber()); 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) // modify all attributes permanently (without game usage)
target.setName(sourceObj.getName()); target.setName(sourceObj.getName());
target.getColor().setColor(sourceObj.getColor()); target.getColor().setColor(sourceObj.getColor());
@ -101,11 +132,7 @@ public class CopyTokenFunction implements Function<Token, Card> {
return target; return target;
} }
public Token from(Card source, Game game) { private Token from(Card source, Game game, Spell spell) {
return from(source, game, null);
}
public Token from(Card source, Game game, Spell spell) {
apply(source, game); apply(source, game);
// token's ZCC must be synced with original card to keep abilities settings // token's ZCC must be synced with original card to keep abilities settings

View file

@ -1,11 +0,0 @@
package mage.util.functions;
import mage.game.Game;
/**
* @author nantuko
*/
@FunctionalInterface
public interface Function<X, Y> {
X apply(Y in, Game game);
}