mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
CardFactory: Token Copy of transforming permanent
This commit is contained in:
@@ -294,7 +294,7 @@ public class GameCopier {
|
|||||||
private Card createCardCopy(Game newGame, Player newOwner, Card c) {
|
private Card createCardCopy(Game newGame, Player newOwner, Card c) {
|
||||||
if (c.isToken() && !c.isImmutable()) {
|
if (c.isToken() && !c.isImmutable()) {
|
||||||
Card result = new TokenInfo(c).makeOneToken(newOwner);
|
Card result = new TokenInfo(c).makeOneToken(newOwner);
|
||||||
CardFactory.copyCopiableCharacteristics(c, result);
|
CardFactory.copyCopiableCharacteristics(c, result, null, null);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (USE_FROM_PAPER_CARD && !c.isImmutable() && c.getPaperCard() != null) {
|
if (USE_FROM_PAPER_CARD && !c.isImmutable() && c.getPaperCard() != null) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.google.common.collect.Lists;
|
|||||||
|
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
import forge.card.CardRulesPredicates;
|
import forge.card.CardRulesPredicates;
|
||||||
|
import forge.card.CardStateName;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -267,17 +268,27 @@ public class CopyPermanentEffect extends TokenEffectBase {
|
|||||||
copy.setOwner(newOwner);
|
copy.setOwner(newOwner);
|
||||||
copy.setSetCode(original.getSetCode());
|
copy.setSetCode(original.getSetCode());
|
||||||
|
|
||||||
if (sa.hasParam("Embalm")) {
|
copy.setTokenSpawningAbility(sa);
|
||||||
copy.setEmbalmed(true);
|
// 707.8a If an effect creates a token that is a copy of a transforming permanent or a transforming double-faced card not on the battlefield,
|
||||||
}
|
// the resulting token is a transforming token that has both a front face and a back face.
|
||||||
|
// The characteristics of each face are determined by the copiable values of the same face of the permanent it is a copy of, as modified by any other copy effects that apply to that permanent.
|
||||||
if (sa.hasParam("Eternalize")) {
|
// If the token is a copy of a transforming permanent with its back face up, the token enters the battlefield with its back face up.
|
||||||
copy.setEternalized(true);
|
// This rule does not apply to tokens that are created with their own set of characteristics and enter the battlefield as a copy of a transforming permanent due to a replacement effect.
|
||||||
|
if (original.isTransformable()) {
|
||||||
|
copy.setBackSide(original.isBackSide());
|
||||||
|
copy.setRules(original.getRules());
|
||||||
|
if (original.isTransformed()) {
|
||||||
|
copy.incrementTransformedTimestamp();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
copy.setStates(CardFactory.getCloneStates(original, copy, sa));
|
copy.setStates(CardFactory.getCloneStates(original, copy, sa));
|
||||||
// force update the now set State
|
// force update the now set State
|
||||||
copy.setState(copy.getCurrentStateName(), true, true);
|
if (original.isTransformable()) {
|
||||||
|
copy.setState(original.isTransformed() ? CardStateName.Transformed : CardStateName.Original, true, true);
|
||||||
|
} else {
|
||||||
|
copy.setState(copy.getCurrentStateName(), true, true);
|
||||||
|
}
|
||||||
copy.setToken(true);
|
copy.setToken(true);
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ public class ReplaceTokenEffect extends SpellAbilityEffect {
|
|||||||
if (token == null) {
|
if (token == null) {
|
||||||
throw new RuntimeException("don't find Token for TokenScript: " + script);
|
throw new RuntimeException("don't find Token for TokenScript: " + script);
|
||||||
}
|
}
|
||||||
|
token.setTokenSpawningAbility((SpellAbility)repSA.getReplacingObject(AbilityKey.Cause));
|
||||||
token.setController(e.getKey(), timestamp);
|
token.setController(e.getKey(), timestamp);
|
||||||
table.put(p, token, e.getValue());
|
table.put(p, token, e.getValue());
|
||||||
}
|
}
|
||||||
@@ -136,6 +137,7 @@ public class ReplaceTokenEffect extends SpellAbilityEffect {
|
|||||||
throw new RuntimeException("don't find Token for TokenScript: " + script);
|
throw new RuntimeException("don't find Token for TokenScript: " + script);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token.setTokenSpawningAbility((SpellAbility)repSA.getReplacingObject(AbilityKey.Cause));
|
||||||
token.setController(pe.getKey(), timestamp);
|
token.setController(pe.getKey(), timestamp);
|
||||||
// if token is created from ForEach keep that
|
// if token is created from ForEach keep that
|
||||||
token.addRemembered(pe.getValue().getRight());
|
token.addRemembered(pe.getValue().getRight());
|
||||||
|
|||||||
@@ -44,8 +44,7 @@ public abstract class TokenEffectBase extends SpellAbilityEffect {
|
|||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new RuntimeException("don't find Token for TokenScript: " + script);
|
throw new RuntimeException("don't find Token for TokenScript: " + script);
|
||||||
}
|
}
|
||||||
// set owner
|
result.setTokenSpawningAbility(sa);
|
||||||
result.setOwner(owner);
|
|
||||||
tokenTable.put(owner, result, finalAmount);
|
tokenTable.put(owner, result, finalAmount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,8 +58,7 @@ public abstract class TokenEffectBase extends SpellAbilityEffect {
|
|||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new RuntimeException("don't find Token for TokenScript: " + script);
|
throw new RuntimeException("don't find Token for TokenScript: " + script);
|
||||||
}
|
}
|
||||||
// set owner
|
result.setTokenSpawningAbility(sa);
|
||||||
result.setOwner(owner);
|
|
||||||
tokenTable.put(owner, result, finalAmount);
|
tokenTable.put(owner, result, finalAmount);
|
||||||
|
|
||||||
return tokenTable;
|
return tokenTable;
|
||||||
@@ -81,6 +79,7 @@ public abstract class TokenEffectBase extends SpellAbilityEffect {
|
|||||||
for (Player p : Sets.newHashSet(tokenTable.rowKeySet())) {
|
for (Player p : Sets.newHashSet(tokenTable.rowKeySet())) {
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(p);
|
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(p);
|
||||||
repParams.put(AbilityKey.Token, tokenTable);
|
repParams.put(AbilityKey.Token, tokenTable);
|
||||||
|
repParams.put(AbilityKey.Cause, sa);
|
||||||
repParams.put(AbilityKey.EffectOnly, true); // currently only effects can create tokens?
|
repParams.put(AbilityKey.EffectOnly, true); // currently only effects can create tokens?
|
||||||
|
|
||||||
switch (game.getReplacementHandler().run(ReplacementType.CreateToken, repParams)) {
|
switch (game.getReplacementHandler().run(ReplacementType.CreateToken, repParams)) {
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
private Card mergedTo;
|
private Card mergedTo;
|
||||||
|
|
||||||
private SpellAbility effectSourceAbility;
|
private SpellAbility effectSourceAbility;
|
||||||
|
private SpellAbility tokenSpawningAbility;
|
||||||
|
|
||||||
private GameEntity entityAttachedTo;
|
private GameEntity entityAttachedTo;
|
||||||
|
|
||||||
@@ -223,8 +224,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
private long prototypeTimestamp = -1;
|
private long prototypeTimestamp = -1;
|
||||||
private int timesMutated = 0;
|
private int timesMutated = 0;
|
||||||
private boolean tributed = false;
|
private boolean tributed = false;
|
||||||
private boolean embalmed = false;
|
|
||||||
private boolean eternalized = false;
|
|
||||||
private boolean discarded = false;
|
private boolean discarded = false;
|
||||||
|
|
||||||
private boolean flipped = false;
|
private boolean flipped = false;
|
||||||
@@ -1562,6 +1561,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
newValue = 0;
|
newValue = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int delta = oldValue - newValue;
|
final int delta = oldValue - newValue;
|
||||||
@@ -5885,18 +5886,22 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
tributed = b;
|
tributed = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isEmbalmed() {
|
public final SpellAbility getTokenSpawningAbility() {
|
||||||
return embalmed;
|
return tokenSpawningAbility;
|
||||||
}
|
}
|
||||||
public final void setEmbalmed(final boolean b) {
|
|
||||||
embalmed = b;
|
public void setTokenSpawningAbility(SpellAbility sa) {
|
||||||
|
tokenSpawningAbility = sa;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isEmbalmed() {
|
||||||
|
SpellAbility sa = getTokenSpawningAbility();
|
||||||
|
return sa != null && sa.hasParam("Embalm");
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isEternalized() {
|
public final boolean isEternalized() {
|
||||||
return eternalized;
|
SpellAbility sa = getTokenSpawningAbility();
|
||||||
}
|
return sa != null && sa.hasParam("Eternalize");
|
||||||
public final void setEternalized(final boolean b) {
|
|
||||||
eternalized = b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getExertedThisTurn() {
|
public final int getExertedThisTurn() {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import forge.game.CardTraitBase;
|
|||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
@@ -85,8 +86,7 @@ public class CardFactory {
|
|||||||
out.setToken(true);
|
out.setToken(true);
|
||||||
|
|
||||||
// need to copy this values for the tokens
|
// need to copy this values for the tokens
|
||||||
out.setEmbalmed(in.isEmbalmed());
|
out.setTokenSpawningAbility(in.getTokenSpawningAbility());
|
||||||
out.setEternalized(in.isEternalized());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.setZone(in.getZone());
|
out.setZone(in.getZone());
|
||||||
@@ -125,7 +125,7 @@ public class CardFactory {
|
|||||||
final Card original = targetSA.getHostCard();
|
final Card original = targetSA.getHostCard();
|
||||||
final Game game = source.getGame();
|
final Game game = source.getGame();
|
||||||
final Card c = new Card(game.nextCardId(), original.getPaperCard(), game);
|
final Card c = new Card(game.nextCardId(), original.getPaperCard(), game);
|
||||||
copyCopiableCharacteristics(original, c);
|
copyCopiableCharacteristics(original, c, sourceSA, targetSA);
|
||||||
|
|
||||||
if (sourceSA.hasParam("NonLegendary")) {
|
if (sourceSA.hasParam("NonLegendary")) {
|
||||||
c.removeType(CardType.Supertype.Legendary);
|
c.removeType(CardType.Supertype.Legendary);
|
||||||
@@ -525,12 +525,12 @@ public class CardFactory {
|
|||||||
* @param from the {@link Card} to copy from.
|
* @param from the {@link Card} to copy from.
|
||||||
* @param to the {@link Card} to copy to.
|
* @param to the {@link Card} to copy to.
|
||||||
*/
|
*/
|
||||||
public static void copyCopiableCharacteristics(final Card from, final Card to) {
|
public static void copyCopiableCharacteristics(final Card from, final Card to, SpellAbility sourceSA, SpellAbility targetSA) {
|
||||||
final boolean toIsFaceDown = to.isFaceDown();
|
final boolean toIsFaceDown = to.isFaceDown();
|
||||||
if (toIsFaceDown) {
|
if (toIsFaceDown) {
|
||||||
// If to is face down, copy to its front side
|
// If to is face down, copy to its front side
|
||||||
to.setState(CardStateName.Original, false);
|
to.setState(CardStateName.Original, false);
|
||||||
copyCopiableCharacteristics(from, to);
|
copyCopiableCharacteristics(from, to, sourceSA, targetSA);
|
||||||
to.setState(CardStateName.FaceDown, false);
|
to.setState(CardStateName.FaceDown, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -545,6 +545,18 @@ public class CardFactory {
|
|||||||
copyState(from, CardStateName.Original, to, to.getCurrentStateName());
|
copyState(from, CardStateName.Original, to, to.getCurrentStateName());
|
||||||
}
|
}
|
||||||
copyState(from, CardStateName.Flipped, to, CardStateName.Flipped);
|
copyState(from, CardStateName.Flipped, to, CardStateName.Flipped);
|
||||||
|
} else if (from.isTransformable()
|
||||||
|
&& sourceSA != null && ApiType.CopySpellAbility.equals(sourceSA.getApi())
|
||||||
|
&& targetSA != null && targetSA.isSpell() && targetSA.getHostCard().isPermanent()) {
|
||||||
|
copyState(from, CardStateName.Original, to, CardStateName.Original);
|
||||||
|
copyState(from, CardStateName.Transformed, to, CardStateName.Transformed);
|
||||||
|
// 707.10g If an effect creates a copy of a transforming permanent spell, the copy is also a transforming permanent spell that has both a front face and a back face.
|
||||||
|
// The characteristics of its front and back face are determined by the copiable values of the same face of the spell it is a copy of, as modified by any other copy effects.
|
||||||
|
// If the spell it is a copy of has its back face up, the copy is created with its back face up. The token that’s put onto the battlefield as that spell resolves is a transforming token.
|
||||||
|
to.setBackSide(from.isBackSide());
|
||||||
|
if (from.isTransformed()) {
|
||||||
|
to.incrementTransformedTimestamp();
|
||||||
|
}
|
||||||
} else if (fromIsTransformedCard) {
|
} else if (fromIsTransformedCard) {
|
||||||
copyState(from, from.getCurrentStateName(), to, CardStateName.Original);
|
copyState(from, from.getCurrentStateName(), to, CardStateName.Original);
|
||||||
} else {
|
} else {
|
||||||
@@ -585,6 +597,9 @@ public class CardFactory {
|
|||||||
|
|
||||||
c.setState(in.getCurrentStateName(), false);
|
c.setState(in.getCurrentStateName(), false);
|
||||||
c.setRules(in.getRules());
|
c.setRules(in.getRules());
|
||||||
|
if (in.isTransformed()) {
|
||||||
|
c.incrementTransformedTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@@ -738,6 +753,15 @@ public class CardFactory {
|
|||||||
final CardState ret2 = new CardState(out, CardStateName.Adventure);
|
final CardState ret2 = new CardState(out, CardStateName.Adventure);
|
||||||
ret2.copyFrom(in.getState(CardStateName.Adventure), false, sa);
|
ret2.copyFrom(in.getState(CardStateName.Adventure), false, sa);
|
||||||
result.put(CardStateName.Adventure, ret2);
|
result.put(CardStateName.Adventure, ret2);
|
||||||
|
} else if (in.isTransformable() && sa instanceof SpellAbility && ApiType.CopyPermanent.equals(((SpellAbility)sa).getApi())) {
|
||||||
|
// CopyPermanent can copy token
|
||||||
|
final CardState ret1 = new CardState(out, CardStateName.Original);
|
||||||
|
ret1.copyFrom(in.getState(CardStateName.Original), false, sa);
|
||||||
|
result.put(CardStateName.Original, ret1);
|
||||||
|
|
||||||
|
final CardState ret2 = new CardState(out, CardStateName.Transformed);
|
||||||
|
ret2.copyFrom(in.getState(CardStateName.Transformed), false, sa);
|
||||||
|
result.put(CardStateName.Transformed, ret2);
|
||||||
} else {
|
} else {
|
||||||
// in all other cases just copy the current state to original
|
// in all other cases just copy the current state to original
|
||||||
final CardState ret = new CardState(out, CardStateName.Original);
|
final CardState ret = new CardState(out, CardStateName.Original);
|
||||||
@@ -866,16 +890,18 @@ public class CardFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Special Rules for Embalm and Eternalize
|
// Special Rules for Embalm and Eternalize
|
||||||
if (sa.hasParam("Embalm") && out.isEmbalmed()) {
|
if (sa.hasParam("Embalm") && out.isEmbalmed()) {
|
||||||
state.addType("Zombie");
|
state.addType("Zombie");
|
||||||
state.setColor(MagicColor.WHITE);
|
state.setColor(MagicColor.WHITE);
|
||||||
state.setManaCost(ManaCost.NO_COST);
|
state.setManaCost(ManaCost.NO_COST);
|
||||||
|
|
||||||
String name = TextUtil.fastReplace(
|
if (sa.isIntrinsic()) {
|
||||||
TextUtil.fastReplace(host.getName(), ",", ""),
|
String name = TextUtil.fastReplace(
|
||||||
" ", "_").toLowerCase();
|
TextUtil.fastReplace(host.getName(), ",", ""),
|
||||||
String set = host.getSetCode().toLowerCase();
|
" ", "_").toLowerCase();
|
||||||
state.setImageKey(ImageKeys.getTokenKey("embalm_" + name + "_" + set));
|
String set = host.getSetCode().toLowerCase();
|
||||||
|
state.setImageKey(ImageKeys.getTokenKey("embalm_" + name + "_" + set));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("Eternalize") && out.isEternalized()) {
|
if (sa.hasParam("Eternalize") && out.isEternalized()) {
|
||||||
@@ -885,11 +911,13 @@ public class CardFactory {
|
|||||||
state.setBasePower(4);
|
state.setBasePower(4);
|
||||||
state.setBaseToughness(4);
|
state.setBaseToughness(4);
|
||||||
|
|
||||||
String name = TextUtil.fastReplace(
|
if (sa.isIntrinsic()) {
|
||||||
TextUtil.fastReplace(host.getName(), ",", ""),
|
String name = TextUtil.fastReplace(
|
||||||
" ", "_").toLowerCase();
|
TextUtil.fastReplace(host.getName(), ",", ""),
|
||||||
String set = host.getSetCode().toLowerCase();
|
" ", "_").toLowerCase();
|
||||||
state.setImageKey(ImageKeys.getTokenKey("eternalize_" + name + "_" + set));
|
String set = host.getSetCode().toLowerCase();
|
||||||
|
state.setImageKey(ImageKeys.getTokenKey("eternalize_" + name + "_" + set));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("GainTextOf") && originalState != null) {
|
if (sa.hasParam("GainTextOf") && originalState != null) {
|
||||||
|
|||||||
@@ -28,12 +28,6 @@ public class ReplaceToken extends ReplacementEffect {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
||||||
/*
|
|
||||||
if (((int) runParams.get(AbilityKey.TokenNum)) <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//*/
|
|
||||||
|
|
||||||
if (hasParam("EffectOnly")) {
|
if (hasParam("EffectOnly")) {
|
||||||
final Boolean effectOnly = (Boolean) runParams.get(AbilityKey.EffectOnly);
|
final Boolean effectOnly = (Boolean) runParams.get(AbilityKey.EffectOnly);
|
||||||
if (!effectOnly) {
|
if (!effectOnly) {
|
||||||
@@ -64,7 +58,7 @@ public class ReplaceToken extends ReplacementEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
|
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
|
||||||
sa.setReplacingObject(AbilityKey.TokenNum, filterAmount((TokenCreateTable) runParams.get(AbilityKey.Token)));
|
sa.setReplacingObject(AbilityKey.TokenNum, filterAmount((TokenCreateTable) runParams.get(AbilityKey.Token)));
|
||||||
sa.setReplacingObject(AbilityKey.Token, runParams.get(AbilityKey.Token));
|
sa.setReplacingObjectsFrom(runParams, AbilityKey.Token, AbilityKey.Cause);
|
||||||
sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected));
|
sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user