Tokens rework: fixed morph support, fixed test (related to #10139)

This commit is contained in:
Oleg Agafonov 2023-05-01 19:41:25 +04:00
parent 94819ff91c
commit 3986196aa4
6 changed files with 110 additions and 66 deletions

View file

@ -12,10 +12,13 @@ import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect.FaceDownType;
import mage.cards.Card;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken;
import mage.game.permanent.token.Token;
import mage.game.stack.Spell;
import mage.util.CardUtil;
@ -134,24 +137,40 @@ public class MorphAbility extends AlternativeSourceCostsImpl {
morphCosts.getText() + (isMana ? ' ' : ". ") + alternativeCost.getReminderText();
}
public static void setPermanentToFaceDownCreature(MageObject mageObject, Game game) {
mageObject.getPower().setModifiedBaseValue(2);
mageObject.getToughness().setModifiedBaseValue(2);
mageObject.getAbilities().clear();
mageObject.getColor(game).setColor(new ObjectColor());
mageObject.setName("");
mageObject.removeAllCardTypes(game);
mageObject.addCardType(game, CardType.CREATURE);
mageObject.removeAllSubTypes(game);
mageObject.getSuperType().clear();
mageObject.getManaCost().clear();
/**
* Hide all info and make it a 2/2 creature
*
* @param targetObject
* @param sourcePermanent source of the face down status
* @param game
*/
public static void setPermanentToFaceDownCreature(MageObject targetObject, Permanent sourcePermanent, Game game) {
targetObject.getPower().setModifiedBaseValue(2);
targetObject.getToughness().setModifiedBaseValue(2);
targetObject.getAbilities().clear();
targetObject.getColor(game).setColor(new ObjectColor());
targetObject.setName("");
targetObject.removeAllCardTypes(game);
targetObject.addCardType(game, CardType.CREATURE);
targetObject.removeAllSubTypes(game);
targetObject.getSuperType().clear();
targetObject.getManaCost().clear();
Token emptyImage = new EmptyToken();
emptyImage.setOriginalExpansionSetCode("");
emptyImage.setExpansionSetCodeForImage("");
emptyImage.setOriginalCardNumber("");
// TODO: add morph image here?
if (mageObject instanceof Permanent) {
if (targetObject instanceof Permanent) {
// hide image info
CardUtil.copySetAndCardNumber((Permanent) mageObject, "", "", 0);
CardUtil.copySetAndCardNumber(targetObject, emptyImage);
// hide rarity info
((Permanent) mageObject).setRarity(Rarity.SPECIAL);
((Permanent) targetObject).setRarity(Rarity.SPECIAL);
} else if (targetObject instanceof Token) {
CardUtil.copySetAndCardNumber(targetObject, emptyImage);
} else {
throw new IllegalArgumentException("Wrong code usage: un-supported targetObject in face down method: " + targetObject.getClass().getSimpleName());
}
}
}

View file

@ -1912,9 +1912,10 @@ public abstract class GameImpl implements Game {
newBluePrint.reset(this);
//getState().addCard(permanent);
if (copyFromPermanent.isMorphed() || copyFromPermanent.isManifested()
if (copyFromPermanent.isMorphed()
|| copyFromPermanent.isManifested()
|| copyFromPermanent.isFaceDown(this)) {
MorphAbility.setPermanentToFaceDownCreature(newBluePrint, this);
MorphAbility.setPermanentToFaceDownCreature(newBluePrint, copyFromPermanent, this);
}
newBluePrint.assignNewId();
if (copyFromPermanent.isTransformed()) {

View file

@ -1185,9 +1185,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
@Override
public boolean entersBattlefield(Ability source, Game game, Zone fromZone, boolean fireEvent) {
controlledFromStartOfControllerTurn = false;
if (this.isFaceDown(game)) {
if (this.isFaceDown(game)) { // ?? add morphed/manifested here ??
// remove some attributes here, because first apply effects comes later otherwise abilities (e.g. color related) will unintended trigger
MorphAbility.setPermanentToFaceDownCreature(this, game);
MorphAbility.setPermanentToFaceDownCreature(this, this, game);
}
if (game.replaceEvent(new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone, EnterEventType.SELF))) {

View file

@ -19,6 +19,8 @@ import java.util.UUID;
*/
public class PermanentToken extends PermanentImpl {
// non-modifyable container with token characteristics
// this PermanentToken resets to it on each game cycle
protected Token token;
public PermanentToken(Token token, UUID controllerId, Game game) {

View file

@ -1789,7 +1789,7 @@ public final class CardUtil {
* Copy image related data from one object to another (card number, set code, token type)
* Use it in copy/transform effects
*/
public static void copySetAndCardNumber(Permanent permanent, MageObject copyFromObject) {
public static void copySetAndCardNumber(MageObject targetObject, MageObject copyFromObject) {
String needSetCode;
String needCardNumber;
int needTokenType;
@ -1810,23 +1810,37 @@ public final class CardUtil {
needCardNumber = ((Card) copyFromObject).getCardNumber();
needTokenType = 0;
} else if (copyFromObject instanceof Token) {
// TODO: make this work
return;
needSetCode = ((Token) copyFromObject).getOriginalExpansionSetCode();
needCardNumber = ((Token) copyFromObject).getOriginalCardNumber();
needTokenType = ((Token) copyFromObject).getTokenType();
} else {
throw new IllegalStateException("Unsupported copyFromObject class: " + copyFromObject.getClass().getSimpleName());
}
copySetAndCardNumber(permanent, needSetCode, needCardNumber, needTokenType);
}
public static void copySetAndCardNumber(Permanent permanent, String newSetCode, String newCardNumber, Integer newTokenType) {
if (permanent instanceof PermanentToken) {
((PermanentToken) permanent).getToken().setOriginalExpansionSetCode(newSetCode);
((PermanentToken) permanent).getToken().setExpansionSetCodeForImage(newCardNumber);
((PermanentToken) permanent).getToken().setTokenType(newTokenType);
if (targetObject instanceof Permanent) {
copySetAndCardNumber((Permanent) targetObject, needSetCode, needCardNumber, needTokenType);
} else if (targetObject instanceof Token) {
copySetAndCardNumber((Token) targetObject, needSetCode, needCardNumber, needTokenType);
} else {
permanent.setExpansionSetCode(newSetCode);
permanent.setCardNumber(newCardNumber);
throw new IllegalStateException("Unsupported target object class: " + targetObject.getClass().getSimpleName());
}
}
private static void copySetAndCardNumber(Permanent targetPermanent, String newSetCode, String newCardNumber, Integer newTokenType) {
if (targetPermanent instanceof PermanentToken) {
copySetAndCardNumber(((PermanentToken) targetPermanent).getToken(), newSetCode, newCardNumber, newTokenType);
} else if (targetPermanent instanceof PermanentCard) {
targetPermanent.setExpansionSetCode(newSetCode);
targetPermanent.setCardNumber(newCardNumber);
} else {
throw new IllegalArgumentException("Wrong code usage: un-supported target permanent type: " + targetPermanent.getClass().getSimpleName());
}
}
private static void copySetAndCardNumber(Token targetToken, String newSetCode, String newCardNumber, Integer newTokenType) {
targetToken.setOriginalExpansionSetCode(newSetCode);
targetToken.setExpansionSetCodeForImage(newSetCode);
targetToken.setOriginalCardNumber(newCardNumber);
targetToken.setTokenType(newTokenType);
}
}

View file

@ -13,6 +13,7 @@ import mage.game.permanent.PermanentToken;
import mage.game.permanent.token.EmptyToken;
import mage.game.permanent.token.Token;
import mage.game.stack.Spell;
import mage.util.CardUtil;
/**
* @author nantuko
@ -44,59 +45,68 @@ public class CopyTokenFunction {
// else gained abililies would be copied too.
target.setEntersTransformed(source instanceof Permanent && ((Permanent) source).isTransformed());
MageObject sourceObj;
// from another token
if (source instanceof PermanentToken) {
// create token from another token
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(sourceToken.getOriginalExpansionSetCode());
target.setOriginalCardNumber(sourceToken.getOriginalCardNumber());
target.setCopySourceCard(((PermanentToken) source).getToken().getCopySourceCard());
copyToToken(target, sourceObj, game);
CardUtil.copySetAndCardNumber(target, source);
if (sourceToken.getBackFace() != null) {
copyToToken(target.getBackFace(), sourceToken.getBackFace(), game);
CardUtil.copySetAndCardNumber(target.getBackFace(), sourceToken.getBackFace());
}
return;
}
// from a permanent
if (source instanceof PermanentCard) {
// create token from non-token permanent
if (((PermanentCard) source).isMorphed() || ((PermanentCard) source).isManifested()) {
MorphAbility.setPermanentToFaceDownCreature(target, game);
// morph/manifest must hide all info
if (((PermanentCard) source).isMorphed()
|| ((PermanentCard) source).isManifested()
|| source.isFaceDown(game)) {
MorphAbility.setPermanentToFaceDownCreature(target, (PermanentCard) source, game);
return;
}
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());
sourceObj = source.getMainCard();
target.setCopySourceCard((Card) sourceObj);
copyToToken(target, sourceObj, game);
CardUtil.copySetAndCardNumber(target, sourceObj);
if (((Card) sourceObj).isTransformable()) {
copyToToken(target.getBackFace(), ((Card) sourceObj).getSecondCardFace(), game);
CardUtil.copySetAndCardNumber(target.getBackFace(), ((Card) sourceObj).getSecondCardFace());
}
return;
}
// from another object like card (example: Embalm ability)
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);
copyToToken(target, sourceObj, game);
CardUtil.copySetAndCardNumber(target, sourceObj);
if (source.isTransformable()) {
if (target.getBackFace() == null) {
// if you catch this then a weird use case here: card with single face copy another token with double face??
// must create back face??
throw new IllegalStateException("Wrong code usage: back face must be non null: " + target.getName() + " - " + target.getClass().getSimpleName());
}
copyToToken(target.getBackFace(), source.getSecondCardFace(), game);
CardUtil.copySetAndCardNumber(target.getBackFace(), source.getSecondCardFace());
}
}
private static Token copyToToken(Token target, MageObject sourceObj, Game game) {
private static void copyToToken(Token target, MageObject sourceObj, Game game) {
// modify all attributes permanently (without game usage)
// ignore images settings here, it will be setup later due needs in face down
// (after copy or after put to battlefield)
target.setName(sourceObj.getName());
target.getColor().setColor(sourceObj.getColor());
target.getManaCost().clear();
@ -128,8 +138,6 @@ public class CopyTokenFunction {
target.setToughness(sourceObj.getToughness().getBaseValue());
target.setStartingLoyalty(sourceObj.getStartingLoyalty());
target.setStartingDefense(sourceObj.getStartingDefense());
return target;
}
private Token from(Card source, Game game, Spell spell) {