Copy Permanent Spell turns into token

This commit is contained in:
Hans Mackowiak
2020-10-05 08:12:50 +02:00
parent 9b87a33300
commit 1ce1d43e27
7 changed files with 63 additions and 49 deletions

View File

@@ -103,7 +103,7 @@ public class GameAction {
boolean wasFacedown = c.isFaceDown(); boolean wasFacedown = c.isFaceDown();
//Rule 110.5g: A token that has left the battlefield can't move to another zone //Rule 110.5g: A token that has left the battlefield can't move to another zone
if (c.isToken() && zoneFrom != null && !fromBattlefield) { if (c.isToken() && zoneFrom != null && !fromBattlefield && !zoneFrom.is(ZoneType.Stack)) {
return c; return c;
} }

View File

@@ -89,7 +89,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
String prompt = Localizer.getInstance().getMessage("lblSelectMultiSpellCopyToStack", Lang.getOrdinal(multi + 1)); String prompt = Localizer.getInstance().getMessage("lblSelectMultiSpellCopyToStack", Lang.getOrdinal(multi + 1));
SpellAbility chosen = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa, prompt, SpellAbility chosen = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa, prompt,
ImmutableMap.of()); ImmutableMap.of());
SpellAbility copiedSpell = CardFactory.copySpellAbilityAndPossiblyHost(card, chosen.getHostCard(), chosen, true); SpellAbility copiedSpell = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosen);
copiedSpell.getHostCard().setController(card.getController(), card.getGame().getNextTimestamp()); copiedSpell.getHostCard().setController(card.getController(), card.getGame().getNextTimestamp());
copiedSpell.setActivatingPlayer(controller); copiedSpell.setActivatingPlayer(controller);
copies.add(copiedSpell); copies.add(copiedSpell);
@@ -120,7 +120,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
mayChooseNewTargets = false; mayChooseNewTargets = false;
for (GameEntity o : candidates) { for (GameEntity o : candidates) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true); SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
resetFirstTargetOnCopy(copy, o, targetedSA); resetFirstTargetOnCopy(copy, o, targetedSA);
copies.add(copy); copies.add(copy);
} }
@@ -147,18 +147,18 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
mayChooseNewTargets = false; mayChooseNewTargets = false;
if (sa.hasParam("ChooseOnlyOne")) { if (sa.hasParam("ChooseOnlyOne")) {
Card choice = controller.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseOne"), null); Card choice = controller.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseOne"), null);
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true); if (choice != null) {
resetFirstTargetOnCopy(copy, choice, targetedSA); valid = new CardCollection(choice);
copies.add(copy); }
} else { }
for (final Card c : valid) { for (final Card c : valid) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true); SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
resetFirstTargetOnCopy(copy, c, targetedSA); resetFirstTargetOnCopy(copy, c, targetedSA);
copies.add(copy); copies.add(copy);
} }
}
for (final Player p : players) { for (final Player p : players) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true); SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
resetFirstTargetOnCopy(copy, p, targetedSA); resetFirstTargetOnCopy(copy, p, targetedSA);
copies.add(copy); copies.add(copy);
} }
@@ -169,8 +169,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
Localizer.getInstance().getMessage("lblSelectASpellCopy"), ImmutableMap.of()); Localizer.getInstance().getMessage("lblSelectASpellCopy"), ImmutableMap.of());
chosenSA.setActivatingPlayer(controller); chosenSA.setActivatingPlayer(controller);
for (int i = 0; i < amount; i++) { for (int i = 0; i < amount; i++) {
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost( SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
card, chosenSA.getHostCard(), chosenSA, true);
// extra case for Epic to remove the keyword and the last part of the SpellAbility // extra case for Epic to remove the keyword and the last part of the SpellAbility
if (sa.hasParam("Epic")) { if (sa.hasParam("Epic")) {

View File

@@ -22,6 +22,16 @@ public class PermanentEffect extends SpellAbilityEffect {
sa.getHostCard().setController(p, 0); sa.getHostCard().setController(p, 0);
final Card host = sa.getHostCard(); final Card host = sa.getHostCard();
// 111.11. A copy of a permanent spell becomes a token as it resolves.
// The token has the characteristics of the spell that became that token.
// The token is not “created” for the purposes of any replacement effects or triggered abilities that refer to creating a token.
if (host.isCopiedSpell()) {
host.setCopiedSpell(false);
host.setToken(true);
// for replacement Effects, need to add the previous copied spell to the Stack Zone
host.getGame().getStackZone().add(host);
}
final Card c = p.getGame().getAction().moveToPlay(host, p, sa); final Card c = p.getGame().getAction().moveToPlay(host, p, sa);
sa.setHostCard(c); sa.setHostCard(c);

View File

@@ -125,12 +125,13 @@ public class CardFactory {
* which wouldn't ordinarily get set during a simple Card.copy() call. * which wouldn't ordinarily get set during a simple Card.copy() call.
* </p> * </p>
* */ * */
private final static Card copySpellHost(final Card source, final Card original, final SpellAbility sa, final boolean bCopyDetails){ private final static Card copySpellHost(final SpellAbility sourceSA, final SpellAbility targetSA){
Player controller = sa.getActivatingPlayer(); final Card source = sourceSA.getHostCard();
final Card original = targetSA.getHostCard();
Player controller = sourceSA.getActivatingPlayer();
final Card c = copyCard(original, true); final Card c = copyCard(original, true);
// change the color of the copy (eg: Fork) // change the color of the copy (eg: Fork)
final SpellAbility sourceSA = source.getFirstSpellAbility();
if (null != sourceSA && sourceSA.hasParam("CopyIsColor")) { if (null != sourceSA && sourceSA.hasParam("CopyIsColor")) {
String tmp = ""; String tmp = "";
final String newColor = sourceSA.getParam("CopyIsColor"); final String newColor = sourceSA.getParam("CopyIsColor");
@@ -148,14 +149,12 @@ public class CardFactory {
c.setOwner(controller); c.setOwner(controller);
c.setCopiedSpell(true); c.setCopiedSpell(true);
if (bCopyDetails) {
c.setXManaCostPaidByColor(original.getXManaCostPaidByColor()); c.setXManaCostPaidByColor(original.getXManaCostPaidByColor());
c.setKickerMagnitude(original.getKickerMagnitude()); c.setKickerMagnitude(original.getKickerMagnitude());
for (OptionalCost cost : original.getOptionalCostsPaid()) { for (OptionalCost cost : original.getOptionalCostsPaid()) {
c.addOptionalCostPaid(cost); c.addOptionalCostPaid(cost);
} }
}
return c; return c;
} }
/** /**
@@ -174,44 +173,27 @@ public class CardFactory {
* @param bCopyDetails * @param bCopyDetails
* a boolean. * a boolean.
*/ */
public final static SpellAbility copySpellAbilityAndPossiblyHost(final Card source, final Card original, final SpellAbility sa, final boolean bCopyDetails) { public final static SpellAbility copySpellAbilityAndPossiblyHost(final SpellAbility sourceSA, final SpellAbility targetSA) {
Player controller = sa.getActivatingPlayer(); Player controller = sourceSA.getActivatingPlayer();
//it is only necessary to copy the host card if the SpellAbility is a spell, not an ability //it is only necessary to copy the host card if the SpellAbility is a spell, not an ability
final Card c; final Card c = targetSA.isSpell() ? copySpellHost(sourceSA, targetSA) : targetSA.getHostCard();
if (sa.isSpell()){
c = copySpellHost(source, original, sa, bCopyDetails);
}
else {
c = original;
}
final SpellAbility copySA; final SpellAbility copySA;
if (sa.isTrigger() && sa.isWrapper()) { if (targetSA.isTrigger() && targetSA.isWrapper()) {
copySA = getCopiedTriggeredAbility((WrappedAbility)sa, c); copySA = getCopiedTriggeredAbility((WrappedAbility)targetSA, c);
} else { } else {
copySA = sa.copy(c, false); copySA = targetSA.copy(c, false);
}
if (sa.isSpell()){
//only update c's abilities if c is a copy.
//(it would be nice to move this into `copySpellHost`,
// so all the c-mutating code is together in one place.
// but copySA doesn't exist until after `copySpellHost` finishes executing,
// so it's hard to resolve that dependency.)
c.getCurrentState().setNonManaAbilities(copySA);
} }
copySA.setCopied(true); copySA.setCopied(true);
//remove all costs //remove all costs
if (!copySA.isTrigger()) { if (!copySA.isTrigger()) {
copySA.setPayCosts(new Cost("", sa.isAbility())); copySA.setPayCosts(new Cost("", targetSA.isAbility()));
} }
copySA.setActivatingPlayer(controller); copySA.setActivatingPlayer(controller);
if (bCopyDetails) { copySA.setPaidHash(targetSA.getPaidHash());
copySA.setPaidHash(sa.getPaidHash());
}
return copySA; return copySA;
} }

View File

@@ -115,7 +115,7 @@ public class Zone implements java.io.Serializable, Iterable<Card> {
// Do not add Tokens to other zones than the battlefield. // Do not add Tokens to other zones than the battlefield.
// But Effects/Emblems count as Tokens too, so allow Command too. // But Effects/Emblems count as Tokens too, so allow Command too.
if (zoneType == ZoneType.Battlefield || !c.isToken()) { if (zoneType == ZoneType.Battlefield || zoneType == ZoneType.Stack || !c.isToken()) {
c.setZone(this); c.setZone(this);
if (index == null) { if (index == null) {

View File

@@ -0,0 +1,8 @@
Name:Lithoform Engine
ManaCost:4
Types:Legendary Artifact
A:AB$ CopySpellAbility | Cost$ 2 T | TgtPrompt$ Select target activated or triggered ability you control | TargetType$ Activated.YouCtrl,Triggered.YouCtrl | ValidTgts$ Card | StackDescription$ SpellDescription | SpellDescription$ Copy target activated or triggered ability you control. You may choose new targets for the copy.
A:AB$ CopySpellAbility | Cost$ 3 T | TgtPrompt$ Select target instant or sorcery spell you control | ValidTgts$ Instant.YouCtrl,Sorcery.YouCtrl | TargetType$ Spell | SpellDescription$ Copy target instant or sorcery spell you control. You may choose new targets for the copy.
A:AB$ CopySpellAbility | Cost$ 4 T | TgtPrompt$ Select target permanent spell you control | ValidTgts$ Permanent.YouCtrl | TargetType$ Spell | SpellDescription$ Copy target permanent spell you control. (The copy becomes a token.)
Oracle:{2}, {T}: Copy target activated or triggered ability you control. You may choose new targets for the copy.\n{3}, {T}: Copy target instant or sorcery spell you control. You may choose new targets for the copy.\n{4}, {T}: Copy target permanent spell you control. (The copy becomes a token.)

View File

@@ -0,0 +1,15 @@
Name:Verazol, the Split Current
ManaCost:X G U
Types:Legendary Creature Serpent
PT:0/0
K:etbCounter:P1P1:Y:no Condition:CARDNAME enters the battlefield with a +1/+1 counter on it for each mana spent to cast it.
SVar:X:Count$xPaid
SVar:Y:Count$FirstSpellTotalManaSpent
T:Mode$ SpellCast | ValidSA$ Spell.Kicked | ValidActivatingPlayer$ You | Execute$ DBRemoveCounters | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ Whenever you cast a kicked spell, you may remove two +1/+1 counters from CARDNAME.
SVar:DBRemoveCounters:DB$ RemoveCounter | CounterType$ P1P1 | CounterNum$ 2 | RememberRemoved$ True | SubAbility$ DBCopy
SVar:DBCopy:DB$ CopySpellAbility | ConditionCheckSVar$ Z | ConditionSVarCompare$ GE1 | References$ Z | SubAbility$ DBCleanup | Defined$ TriggeredSpellAbility | AILogic$ Always | SpellDescription$ If you do, copy that spell. You may choose new targets for that copy. (A copy of a permanent spell becomes a token.)
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:Z:Count$RememberedSize
DeckHas:Ability$Counters
Oracle:Verazol, the Split Current enters the battlefield with a +1/+1 counter on it for each mana spent to cast it.\nWhenever you cast a kicked spell, you may remove two +1/+1 counters from Verazol, the Split Current. If you do, copy that spell. You may choose new targets for that copy. (A copy of a permanent spell becomes a token.)