GUI: improved double faced tokens:

- added tokens support in test render dialog;
 - improved PT drawing;
 - fixed broken second side switch button (related to #10231);
 - fixed miss override in TokenImpl (all override with backFace must be removed, see todo);
This commit is contained in:
Oleg Agafonov 2023-04-29 19:13:13 +04:00
parent 8cb89a9a98
commit 583c5fe4e9
13 changed files with 270 additions and 140 deletions

View file

@ -395,8 +395,10 @@ public class MageBook extends JComponent {
draftRating.setHorizontalAlignment(SwingConstants.CENTER); draftRating.setHorizontalAlignment(SwingConstants.CENTER);
draftRating.setFont(jLayeredPane.getFont().deriveFont(jLayeredPane.getFont().getStyle() | Font.BOLD)); draftRating.setFont(jLayeredPane.getFont().deriveFont(jLayeredPane.getFont().getStyle() | Font.BOLD));
if (card.getOriginalCard() != null) { if (card.getOriginalCard() != null) {
// card
draftRating.setText("draft rating: " + RateCard.rateCard(card.getOriginalCard(), null)); draftRating.setText("draft rating: " + RateCard.rateCard(card.getOriginalCard(), null));
} else { } else {
// token
draftRating.setText(""); draftRating.setText("");
} }
jLayeredPane.add(draftRating); jLayeredPane.add(draftRating);

View file

@ -2,10 +2,7 @@ package mage.client.dialog;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.icon.CardIconImpl; import mage.abilities.icon.*;
import mage.abilities.icon.CardIconOrder;
import mage.abilities.icon.CardIconPosition;
import mage.abilities.icon.CardIconType;
import mage.abilities.keyword.TransformAbility; import mage.abilities.keyword.TransformAbility;
import mage.cards.*; import mage.cards.*;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
@ -34,6 +31,11 @@ import mage.game.mulligan.MulliganType;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentCard;
import mage.game.permanent.PermanentMeld; import mage.game.permanent.PermanentMeld;
import mage.game.permanent.PermanentToken;
import mage.game.permanent.token.IncubatorToken;
import mage.game.permanent.token.Phyrexian00Token;
import mage.game.permanent.token.Token;
import mage.game.permanent.token.ZombieToken;
import mage.players.Player; import mage.players.Player;
import mage.players.StubPlayer; import mage.players.StubPlayer;
import mage.util.CardUtil; import mage.util.CardUtil;
@ -47,7 +49,6 @@ import java.awt.*;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.List; import java.util.List;
import java.util.*; import java.util.*;
import mage.abilities.icon.CardIconColor;
/** /**
* App GUI: debug only, testing card renders and manipulations * App GUI: debug only, testing card renders and manipulations
@ -210,6 +211,27 @@ public class TestCardRenderDialog extends MageDialog {
return planeView; return planeView;
} }
private CardView createToken(Game game, UUID controllerId, Token token, String code, int damage, boolean tapped, boolean transformed) {
Token sourceToken = token.copy();
sourceToken.setExpansionSetCodeForImage(code);
sourceToken.setOriginalExpansionSetCode(code);
PermanentToken perm = new PermanentToken(sourceToken, controllerId, game);
Set<Card> cardsList = new HashSet<>();
cardsList.add(perm);
game.loadCards(cardsList, controllerId);
if (damage > 0) perm.damage(damage, controllerId, null, game);
perm.removeSummoningSickness();
perm.setTapped(tapped);
if (perm.isTransformable() && transformed) {
perm.setTransformed(true);
}
PermanentView cardView = new PermanentView(perm, game.getCard(perm.getId()), controllerId, game);
//cardView.setInViewerOnly(true); // ???
return cardView;
}
private void reloadCards() { private void reloadCards() {
// apply selected theme (warning, it will be applied for all app, so can be bugged in other dialogs - but it's ok for debug) // apply selected theme (warning, it will be applied for all app, so can be bugged in other dialogs - but it's ok for debug)
PreferencesDialog.setCurrentTheme((ThemeType) comboTheme.getSelectedItem()); PreferencesDialog.setCurrentTheme((ThemeType) comboTheme.getSelectedItem());
@ -289,7 +311,8 @@ public class TestCardRenderDialog extends MageDialog {
} }
List<CardView> cardViews = new ArrayList<>(); List<CardView> cardViews = new ArrayList<>();
/* // test morphed
/* test morphed
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "263", 0, 0, 0, false, null)); // mountain cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "263", 0, 0, 0, false, null)); // mountain
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true, null)); // Judith, the Scourge Diva cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true, null)); // Judith, the Scourge Diva
cardViews.add(createHandCard(game, playerYou.getId(), "DIS", "153")); // Odds // Ends (split card) cardViews.add(createHandCard(game, playerYou.getId(), "DIS", "153")); // Odds // Ends (split card)
@ -299,7 +322,7 @@ public class TestCardRenderDialog extends MageDialog {
cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, true, false)); // manifested cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, true, false)); // manifested
//*/ //*/
/* //test emblems /* test emblems
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false, false, null)); // Noxious Groodion cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false, false, null)); // Noxious Groodion
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false, false, null)); // Knight of Sorrows cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false, false, null)); // Knight of Sorrows
cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false, false, null)); // Huntmaster of the Fells, transforms cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false, false, null)); // Huntmaster of the Fells, transforms
@ -309,14 +332,14 @@ public class TestCardRenderDialog extends MageDialog {
cardViews.add(createPlane(new AkoumPlane())); // Plane - Akoum cardViews.add(createPlane(new AkoumPlane())); // Plane - Akoum
//*/ //*/
//test split, transform and mdf in hands /* test split, transform and mdf in hands
cardViews.add(createHandCard(game, playerYou.getId(), "SOI", "97")); // Accursed Witch cardViews.add(createHandCard(game, playerYou.getId(), "SOI", "97")); // Accursed Witch
//cardViews.add(createHandCard(game, playerYou.getId(), "UMA", "225")); // Fire // Ice cardViews.add(createHandCard(game, playerYou.getId(), "UMA", "225")); // Fire // Ice
//cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "14")); // Giant Killer cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "14")); // Giant Killer
//cardViews.add(createHandCard(game, playerYou.getId(), "ZNR", "134")); // Akoum Warrior cardViews.add(createHandCard(game, playerYou.getId(), "ZNR", "134")); // Akoum Warrior
//*/ //*/
/*// test meld cards in hands and battlefield /* test meld cards in hands and battlefield
cardViews.add(createHandCard(game, playerYou.getId(), "EMN", "204")); // Hanweir Battlements cardViews.add(createHandCard(game, playerYou.getId(), "EMN", "204")); // Hanweir Battlements
cardViews.add(createHandCard(game, playerYou.getId(), "EMN", "130a")); // Hanweir Garrison cardViews.add(createHandCard(game, playerYou.getId(), "EMN", "130a")); // Hanweir Garrison
cardViews.add(createHandCard(game, playerYou.getId(), "EMN", "130b")); // Hanweir, the Writhing Township cardViews.add(createHandCard(game, playerYou.getId(), "EMN", "130b")); // Hanweir, the Writhing Township
@ -325,7 +348,7 @@ public class TestCardRenderDialog extends MageDialog {
cardViews.add(createPermanentCard(game, playerYou.getId(), "EMN", "130b", 1, 1, 0, false, false, null)); // Hanweir, the Writhing Township cardViews.add(createPermanentCard(game, playerYou.getId(), "EMN", "130b", 1, 1, 0, false, false, null)); // Hanweir, the Writhing Township
//*/ //*/
// test variant double faced cards (main and second sides must be same pair) /* test variant double faced cards (main and second sides must be same pair)
// Jacob Hauken, Inspector -> Hauken's Insight // Jacob Hauken, Inspector -> Hauken's Insight
cardViews.add(createHandCard(game, playerYou.getId(), "VOW", "65")); cardViews.add(createHandCard(game, playerYou.getId(), "VOW", "65"));
cardViews.add(createHandCard(game, playerYou.getId(), "VOW", "320")); cardViews.add(createHandCard(game, playerYou.getId(), "VOW", "320"));
@ -335,14 +358,23 @@ public class TestCardRenderDialog extends MageDialog {
cardViews.add(createPermanentCard(game, playerYou.getId(), "VOW", "332", 1, 1, 0, false, false, null)); cardViews.add(createPermanentCard(game, playerYou.getId(), "VOW", "332", 1, 1, 0, false, false, null));
//*/ //*/
/*//test card icons /* test card icons
cardViews.add(createHandCard(game, playerYou.getId(), "POR", "169")); // Grizzly Bears cardViews.add(createHandCard(game, playerYou.getId(), "POR", "169")); // Grizzly Bears
cardViews.add(createHandCard(game, playerYou.getId(), "DKA", "140")); // Huntmaster of the Fells, transforms cardViews.add(createHandCard(game, playerYou.getId(), "DKA", "140")); // Huntmaster of the Fells, transforms
cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 3, 3, 1, false, true, additionalIcons)); // Huntmaster of the Fells, transforms cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 3, 3, 1, false, true, additionalIcons)); // Huntmaster of the Fells, transforms
cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "401", 1, 1, 0, false, false, additionalIcons)); // Hinterland Drake cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "401", 1, 1, 0, false, false, additionalIcons)); // Hinterland Drake
//cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "1441", 1, 1, 0, true, false, additionalIcons)); // Kathari Remnant //cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "1441", 1, 1, 0, true, false, additionalIcons)); // Kathari Remnant
//cardViews.add(createPermanentCard(game, playerYou.getId(), "KHM", "50", 1, 1, 0, true, false, additionalIcons)); // Cosima, God of the Voyage //cardViews.add(createPermanentCard(game, playerYou.getId(), "KHM", "50", 1, 1, 0, true, false, additionalIcons)); // Cosima, God of the Voyage
//*/
//* test tokens
// normal
cardViews.add(createToken(game, playerYou.getId(), new ZombieToken(), "10E", 0, false, false));
cardViews.add(createToken(game, playerYou.getId(), new ZombieToken(), "XXX", 1, false, false));
cardViews.add(createToken(game, playerYou.getId(), new ZombieToken(), null, 0, false, false));
// double faced
cardViews.add(createToken(game, playerYou.getId(), new IncubatorToken(), "MOM", 0, false, false));
cardViews.add(createToken(game, playerYou.getId(), new Phyrexian00Token(), "MOM", 1, false, true));
//*/ //*/
// duplicate cards // duplicate cards
@ -371,7 +403,7 @@ public class TestCardRenderDialog extends MageDialog {
if (main.getGameCard() instanceof PermanentView) { if (main.getGameCard() instanceof PermanentView) {
// new settings must be as a new copy -- it would activate the animations // new settings must be as a new copy -- it would activate the animations
PermanentView oldPermanent = (PermanentView) main.getGameCard(); PermanentView oldPermanent = (PermanentView) main.getGameCard();
PermanentView newPermament = new PermanentView( PermanentView newPermament = new PermanentView( // ???
(Permanent) oldPermanent.getOriginalCard(), (Permanent) oldPermanent.getOriginalCard(),
game.getCard(oldPermanent.getOriginalCard().getId()), game.getCard(oldPermanent.getOriginalCard().getId()),
UUID.randomUUID(), UUID.randomUUID(),

View file

@ -362,6 +362,8 @@ public class CardPanelRenderModeImage extends CardPanel {
int cardXOffset = 0; int cardXOffset = 0;
int cardYOffset = 0; int cardYOffset = 0;
CardView cardView = getGameCard();
// workaround to fix a rare NPE error with image loading // workaround to fix a rare NPE error with image loading
// reason: panel runs image load in another thread and that thread can be completed before top panel init, see updateArtImage // reason: panel runs image load in another thread and that thread can be completed before top panel init, see updateArtImage
if (getTopPanelRef() == null) { if (getTopPanelRef() == null) {
@ -381,7 +383,7 @@ public class CardPanelRenderModeImage extends CardPanel {
imagePanel.setLocation(realCardSize.x, realCardSize.y); imagePanel.setLocation(realCardSize.x, realCardSize.y);
imagePanel.setSize(realCardSize.width, realCardSize.height); imagePanel.setSize(realCardSize.width, realCardSize.height);
if (hasSickness() && getGameCard().isCreature() && isPermanent()) { if (hasSickness() && cardView.isCreature() && isPermanent()) {
overlayPanel.setLocation(realCardSize.x, realCardSize.y); overlayPanel.setLocation(realCardSize.x, realCardSize.y);
overlayPanel.setSize(realCardSize.width, realCardSize.height); overlayPanel.setSize(realCardSize.width, realCardSize.height);
} else { } else {
@ -441,10 +443,27 @@ public class CardPanelRenderModeImage extends CardPanel {
fullImageText.setBounds(titleText.getX(), titleText.getY(), titleText.getBounds().width, titleText.getBounds().height); fullImageText.setBounds(titleText.getX(), titleText.getY(), titleText.getBounds().width, titleText.getBounds().height);
// PT (font as title) // PT (font as title)
if (getGameCard().getOriginalCard() != null) { if (cardView.showPT()) {
prepareGlowFont(ptText1, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), getGameCard().getOriginalCard().getPower(), false);
// real PT info
MageInt currentPower;
MageInt currentToughness;
if (cardView.getOriginalCard() != null) {
// card
currentPower = cardView.getOriginalCard().getPower();
currentToughness = cardView.getOriginalCard().getToughness();
} else if (cardView.getOriginalToken() != null) {
// token
currentPower = cardView.getOriginalToken().getPower();
currentToughness = cardView.getOriginalToken().getToughness();
} else {
currentPower = null;
currentToughness = null;
}
prepareGlowFont(ptText1, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), currentPower, false);
prepareGlowFont(ptText2, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), null, false); prepareGlowFont(ptText2, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), null, false);
prepareGlowFont(ptText3, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), getGameCard().getOriginalCard().getToughness(), CardRendererUtils.isCardWithDamage(getGameCard())); prepareGlowFont(ptText3, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), currentToughness, CardRendererUtils.isCardWithDamage(cardView));
// right bottom corner with margin (sizes from any sample card) // right bottom corner with margin (sizes from any sample card)
int ptMarginRight = Math.round(64f / 672f * cardWidth); int ptMarginRight = Math.round(64f / 672f * cardWidth);

View file

@ -1,5 +1,6 @@
package org.mage.card.arcane; package org.mage.card.arcane;
import mage.MageInt;
import mage.ObjectColor; import mage.ObjectColor;
import mage.cards.ArtRect; import mage.cards.ArtRect;
import mage.cards.FrameStyle; import mage.cards.FrameStyle;
@ -1044,7 +1045,7 @@ public class ModernCardRenderer extends CardRenderer {
// Is it a creature? // Is it a creature?
boolean isVehicle = cardView.getSubTypes().contains(SubType.VEHICLE); boolean isVehicle = cardView.getSubTypes().contains(SubType.VEHICLE);
if (cardView.isCreature() || isVehicle) { if (cardView.showPT()) {
// draws p/t by parts // draws p/t by parts
int ptDeviderSpace = 1; // Arial font is too narrow for devider (2/2) and needs extra space int ptDeviderSpace = 1; // Arial font is too narrow for devider (2/2) and needs extra space
@ -1093,19 +1094,35 @@ public class ModernCardRenderer extends CardRenderer {
g.setColor(defaultTextColor); g.setColor(defaultTextColor);
g.setFont(ptTextFont); g.setFont(ptTextFont);
// real PT info
MageInt currentPower;
MageInt currentToughness;
if (cardView.getOriginalCard() != null) {
// card
currentPower = cardView.getOriginalCard().getPower();
currentToughness = cardView.getOriginalCard().getToughness();
} else if (cardView.getOriginalToken() != null) {
// token
currentPower = cardView.getOriginalToken().getPower();
currentToughness = cardView.getOriginalToken().getToughness();
} else {
currentPower = null;
currentToughness = null;
}
// draws // draws
int ptEmptySpace = (partBoxWidth - ptContentWidth) / 2; int ptEmptySpace = (partBoxWidth - ptContentWidth) / 2;
int ptPosStart1 = x + contentInset + ptEmptySpace; int ptPosStart1 = x + contentInset + ptEmptySpace;
int ptPosStart2 = ptPosStart1 + ptTextWidth1 + ptDeviderSpace; int ptPosStart2 = ptPosStart1 + ptTextWidth1 + ptDeviderSpace;
int ptPosStart3 = ptPosStart2 + ptTextWidth2 + ptDeviderSpace; int ptPosStart3 = ptPosStart2 + ptTextWidth2 + ptDeviderSpace;
// p // p
g.setColor(CardRendererUtils.getCardTextColor(cardView.getOriginalCard().getPower(), false, defaultTextColor, defaultTextLight)); g.setColor(CardRendererUtils.getCardTextColor(currentPower, false, defaultTextColor, defaultTextLight));
g.drawString(ptText1, ptPosStart1, curY - ptTextOffset - 1); // left g.drawString(ptText1, ptPosStart1, curY - ptTextOffset - 1); // left
// / // /
g.setColor(defaultTextColor); g.setColor(defaultTextColor);
g.drawString(ptText2, ptPosStart2, curY - ptTextOffset - 1); // center g.drawString(ptText2, ptPosStart2, curY - ptTextOffset - 1); // center
// t // t
g.setColor(CardRendererUtils.getCardTextColor(cardView.getOriginalCard().getToughness(), CardRendererUtils.isCardWithDamage(cardView), defaultTextColor, defaultTextLight)); g.setColor(CardRendererUtils.getCardTextColor(currentToughness, CardRendererUtils.isCardWithDamage(cardView), defaultTextColor, defaultTextLight));
g.drawString(ptText3, ptPosStart3, curY - ptTextOffset - 1); // right g.drawString(ptText3, ptPosStart3, curY - ptTextOffset - 1); // right
// //
g.setColor(defaultTextColor); g.setColor(defaultTextColor);

View file

@ -37,6 +37,7 @@ import mage.players.Player;
import mage.target.Target; import mage.target.Target;
import mage.target.Targets; import mage.target.Targets;
import mage.util.CardUtil; import mage.util.CardUtil;
import mage.util.ManaUtil;
import mage.util.SubTypes; import mage.util.SubTypes;
import java.util.*; import java.util.*;
@ -127,10 +128,10 @@ public class CardView extends SimpleCardView {
protected boolean canAttack; protected boolean canAttack;
protected boolean canBlock; protected boolean canBlock;
protected boolean inViewerOnly; protected boolean inViewerOnly; // GUI render: show object as a card instead permanent (without PT, etc)
protected List<CardIcon> cardIcons = new ArrayList<>(); // additional icons to render protected List<CardIcon> cardIcons = new ArrayList<>(); // additional icons to render
protected Card originalCard = null; protected MageObject originalObject = null; // GUI related: additional info about current object (example: real PT)
/** /**
* Non game usage like deck editor * Non game usage like deck editor
@ -233,7 +234,7 @@ public class CardView extends SimpleCardView {
this.canAttack = cardView.canAttack; this.canAttack = cardView.canAttack;
this.canBlock = cardView.canBlock; this.canBlock = cardView.canBlock;
this.inViewerOnly = cardView.inViewerOnly; this.inViewerOnly = cardView.inViewerOnly;
this.originalCard = cardView.originalCard == null ? null : cardView.originalCard.copy(); this.originalObject = cardView.originalObject == null ? null : cardView.originalObject.copy();
if (cardView.cardIcons != null) { if (cardView.cardIcons != null) {
cardView.cardIcons.forEach(icon -> this.cardIcons.add(icon.copy())); cardView.cardIcons.forEach(icon -> this.cardIcons.add(icon.copy()));
} }
@ -279,7 +280,7 @@ public class CardView extends SimpleCardView {
*/ */
public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) { public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) {
super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), game != null); super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), game != null);
this.originalCard = card; this.originalObject = card;
// no information available for face down cards as long it's not a controlled face down morph card // no information available for face down cards as long it's not a controlled face down morph card
// TODO: Better handle this in Framework (but currently I'm not sure how to do it there) LevelX2 // TODO: Better handle this in Framework (but currently I'm not sure how to do it there) LevelX2
@ -528,6 +529,12 @@ public class CardView extends SimpleCardView {
this.alternateName = meldsToCard.getName(); this.alternateName = meldsToCard.getName();
} }
if (card instanceof PermanentToken && card.isTransformable()) {
Token backFace = (Token) ((PermanentToken) card).getOtherFace();
this.secondCardFace = new CardView(backFace, game);
this.alternateName = backFace.getName();
}
if (card instanceof Spell) { if (card instanceof Spell) {
this.mageObjectType = MageObjectType.SPELL; this.mageObjectType = MageObjectType.SPELL;
Spell spell = (Spell) card; Spell spell = (Spell) card;
@ -606,7 +613,7 @@ public class CardView extends SimpleCardView {
public CardView(MageObject object, Game game) { public CardView(MageObject object, Game game) {
super(object.getId(), "", "0", false, true); super(object.getId(), "", "0", false, true);
this.originalCard = null; this.originalObject = object;
this.name = object.getName(); this.name = object.getName();
this.displayName = object.getName(); this.displayName = object.getName();
@ -688,6 +695,7 @@ public class CardView extends SimpleCardView {
public CardView(EmblemView emblem) { public CardView(EmblemView emblem) {
this(true); this(true);
this.originalObject = null;
this.gameObject = true; this.gameObject = true;
this.id = emblem.getId(); this.id = emblem.getId();
this.mageObjectType = MageObjectType.EMBLEM; this.mageObjectType = MageObjectType.EMBLEM;
@ -703,6 +711,7 @@ public class CardView extends SimpleCardView {
public CardView(DungeonView dungeon) { public CardView(DungeonView dungeon) {
this(true); this(true);
this.originalObject = null;
this.gameObject = true; this.gameObject = true;
this.id = dungeon.getId(); this.id = dungeon.getId();
this.mageObjectType = MageObjectType.DUNGEON; this.mageObjectType = MageObjectType.DUNGEON;
@ -718,6 +727,7 @@ public class CardView extends SimpleCardView {
public CardView(PlaneView plane) { public CardView(PlaneView plane) {
this(true); this(true);
this.originalObject = null;
this.gameObject = true; this.gameObject = true;
this.id = plane.getId(); this.id = plane.getId();
this.mageObjectType = MageObjectType.PLANE; this.mageObjectType = MageObjectType.PLANE;
@ -734,6 +744,7 @@ public class CardView extends SimpleCardView {
public CardView(Designation designation, StackAbility stackAbility) { public CardView(Designation designation, StackAbility stackAbility) {
this(true); this(true);
this.originalObject = null;
this.gameObject = true; this.gameObject = true;
this.id = designation.getId(); this.id = designation.getId();
this.mageObjectType = MageObjectType.NULL; this.mageObjectType = MageObjectType.NULL;
@ -756,6 +767,7 @@ public class CardView extends SimpleCardView {
} }
private void fillEmpty(Card card, boolean controlled) { private void fillEmpty(Card card, boolean controlled) {
this.originalObject = null;
this.name = "Face Down"; this.name = "Face Down";
this.displayName = name; this.displayName = name;
this.displayFullName = name; this.displayFullName = name;
@ -954,11 +966,25 @@ public class CardView extends SimpleCardView {
} }
public String getColorIdentityStr() { public String getColorIdentityStr() {
FilterMana filterMana = originalCard.getColorIdentity(); FilterMana colorInfo;
if (filterMana.getColorCount() == 0) { if (getOriginalCard() != null) {
return CardUtil.concatManaSymbols(CardInfo.SPLIT_MANA_SEPARATOR_FULL, "{C}", ""); // card
colorInfo = getOriginalCard().getColorIdentity();
} else if (getOriginalToken() != null) {
// token
colorInfo = ManaUtil.getColorIdentity(getOriginalToken());
} else {
colorInfo = new FilterMana();
} }
return CardUtil.concatManaSymbols(CardInfo.SPLIT_MANA_SEPARATOR_FULL, filterMana.toString(), "");
String colorRes;
if (colorInfo.getColorCount() == 0) {
colorRes = "{C}";
} else {
colorRes = colorInfo.toString();
}
return CardUtil.concatManaSymbols(CardInfo.SPLIT_MANA_SEPARATOR_FULL, colorRes, "");
} }
@Override @Override
@ -1236,10 +1262,26 @@ public class CardView extends SimpleCardView {
} }
public Card getOriginalCard() { public Card getOriginalCard() {
return this.originalCard; if (this.originalObject instanceof Card) {
return (Card) this.originalObject;
} else {
return null;
}
}
public Token getOriginalToken() {
if (this.originalObject instanceof Token) {
return (Token) this.originalObject;
} else {
return null;
}
} }
public List<CardIcon> getCardIcons() { public List<CardIcon> getCardIcons() {
return this.cardIcons; return this.cardIcons;
} }
public boolean showPT() {
return this.isCreature() || this.getSubTypes().contains(SubType.VEHICLE);
}
} }

View file

@ -60,10 +60,9 @@ 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)

View file

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

View file

@ -139,7 +139,7 @@ public class PermanentToken extends PermanentImpl {
} }
@Override @Override
protected MageObject getOtherFace() { public MageObject getOtherFace() {
return this.transformed ? token : this.token.getBackFace(); return this.transformed ? token : this.token.getBackFace();
} }

View file

@ -11,7 +11,7 @@ import java.util.Arrays;
*/ */
public class Phyrexian00Token extends TokenImpl { public class Phyrexian00Token extends TokenImpl {
Phyrexian00Token() { public Phyrexian00Token() {
super("Phyrexian Token", "0/0 Phyrexian artifact creature token"); super("Phyrexian Token", "0/0 Phyrexian artifact creature token");
cardType.add(CardType.ARTIFACT); cardType.add(CardType.ARTIFACT);
cardType.add(CardType.CREATURE); cardType.add(CardType.CREATURE);

View file

@ -1,4 +1,3 @@
package mage.game.permanent.token; package mage.game.permanent.token;
import mage.MageObject; import mage.MageObject;

View file

@ -87,6 +87,8 @@ 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());
// TODO: remove all override and backFace changes (bug example: active transform ability in back face)
if (backFace != null) { if (backFace != null) {
backFace.addAbility(ability); backFace.addAbility(ability);
} }
@ -443,6 +445,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.setStartingLoyalty(startingLoyalty); super.setStartingLoyalty(startingLoyalty);
} }
@Override
public void setStartingDefense(int intArg) { public void setStartingDefense(int intArg) {
if (backFace != null) { if (backFace != null) {
backFace.setStartingDefense(intArg); backFace.setStartingDefense(intArg);
@ -481,6 +484,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
return backFace; return backFace;
} }
@Override
public void retainAllArtifactSubTypes(Game game) { public void retainAllArtifactSubTypes(Game game) {
if (backFace != null) { if (backFace != null) {
backFace.retainAllArtifactSubTypes(game); backFace.retainAllArtifactSubTypes(game);
@ -488,6 +492,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.retainAllArtifactSubTypes(game); super.retainAllArtifactSubTypes(game);
} }
@Override
public void retainAllEnchantmentSubTypes(Game game) { public void retainAllEnchantmentSubTypes(Game game) {
if (backFace != null) { if (backFace != null) {
backFace.retainAllEnchantmentSubTypes(game); backFace.retainAllEnchantmentSubTypes(game);
@ -495,6 +500,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.retainAllEnchantmentSubTypes(game); super.retainAllEnchantmentSubTypes(game);
} }
@Override
public void addSuperType(SuperType superType) { public void addSuperType(SuperType superType) {
if (backFace != null) { if (backFace != null) {
backFace.addSuperType(superType); backFace.addSuperType(superType);
@ -502,6 +508,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.addSuperType(superType); super.addSuperType(superType);
} }
@Override
public void removeSuperType(SuperType superType) { public void removeSuperType(SuperType superType) {
if (backFace != null) { if (backFace != null) {
backFace.removeSuperType(superType); backFace.removeSuperType(superType);
@ -509,6 +516,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.removeSuperType(superType); super.removeSuperType(superType);
} }
@Override
public void addCardType(CardType... cardTypes) { public void addCardType(CardType... cardTypes) {
if (backFace != null) { if (backFace != null) {
backFace.addCardType(cardTypes); backFace.addCardType(cardTypes);
@ -516,6 +524,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.addCardType(cardTypes); super.addCardType(cardTypes);
} }
@Override
public void removeCardType(CardType... cardTypes) { public void removeCardType(CardType... cardTypes) {
if (backFace != null) { if (backFace != null) {
backFace.removeCardType(cardTypes); backFace.removeCardType(cardTypes);
@ -523,6 +532,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.removeCardType(cardTypes); super.removeCardType(cardTypes);
} }
@Override
public void removeAllCardTypes() { public void removeAllCardTypes() {
if (backFace != null) { if (backFace != null) {
backFace.removeAllCardTypes(); backFace.removeAllCardTypes();
@ -530,6 +540,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.removeAllCardTypes(); super.removeAllCardTypes();
} }
@Override
public void removeAllCardTypes(Game game) { public void removeAllCardTypes(Game game) {
if (backFace != null) { if (backFace != null) {
backFace.removeAllCardTypes(game); backFace.removeAllCardTypes(game);
@ -537,6 +548,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.removeAllCardTypes(game); super.removeAllCardTypes(game);
} }
@Override
public void addSubType(SubType... subTypes) { public void addSubType(SubType... subTypes) {
if (backFace != null) { if (backFace != null) {
backFace.addSubType(subTypes); backFace.addSubType(subTypes);
@ -544,6 +556,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.addSubType(subTypes); super.addSubType(subTypes);
} }
@Override
public void removeAllSubTypes(Game game, SubTypeSet subTypeSet) { public void removeAllSubTypes(Game game, SubTypeSet subTypeSet) {
if (backFace != null) { if (backFace != null) {
backFace.removeAllSubTypes(game, subTypeSet); backFace.removeAllSubTypes(game, subTypeSet);
@ -551,6 +564,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.removeAllSubTypes(game, subTypeSet); super.removeAllSubTypes(game, subTypeSet);
} }
@Override
public void removeAllSubTypes(Game game) { public void removeAllSubTypes(Game game) {
if (backFace != null) { if (backFace != null) {
backFace.removeAllSubTypes(game); backFace.removeAllSubTypes(game);
@ -558,6 +572,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.removeAllSubTypes(game); super.removeAllSubTypes(game);
} }
@Override
public void retainAllLandSubTypes(Game game) { public void retainAllLandSubTypes(Game game) {
if (backFace != null) { if (backFace != null) {
backFace.retainAllLandSubTypes(game); backFace.retainAllLandSubTypes(game);
@ -565,6 +580,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.retainAllLandSubTypes(game); super.retainAllLandSubTypes(game);
} }
@Override
public void removeAllCreatureTypes(Game game) { public void removeAllCreatureTypes(Game game) {
if (backFace != null) { if (backFace != null) {
backFace.removeAllCreatureTypes(game); backFace.removeAllCreatureTypes(game);
@ -572,6 +588,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.removeAllCreatureTypes(game); super.removeAllCreatureTypes(game);
} }
@Override
public void removeAllCreatureTypes() { public void removeAllCreatureTypes() {
if (backFace != null) { if (backFace != null) {
backFace.removeAllCreatureTypes(); backFace.removeAllCreatureTypes();
@ -579,6 +596,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.removeAllCreatureTypes(); super.removeAllCreatureTypes();
} }
@Override
public void removeSubType(Game game, SubType subType) { public void removeSubType(Game game, SubType subType) {
if (backFace != null) { if (backFace != null) {
backFace.removeSubType(game, subType); backFace.removeSubType(game, subType);
@ -586,6 +604,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.removeSubType(game, subType); super.removeSubType(game, subType);
} }
@Override
public void setIsAllCreatureTypes(boolean value) { public void setIsAllCreatureTypes(boolean value) {
if (backFace != null) { if (backFace != null) {
backFace.setIsAllCreatureTypes(value); backFace.setIsAllCreatureTypes(value);
@ -593,6 +612,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.setIsAllCreatureTypes(value); super.setIsAllCreatureTypes(value);
} }
@Override
public void removePTCDA() { public void removePTCDA() {
if (backFace != null) { if (backFace != null) {
backFace.removePTCDA(); backFace.removePTCDA();
@ -600,13 +620,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.removePTCDA(); super.removePTCDA();
} }
public String getName() { @Override
if (backFace != null) {
backFace.getName();
}
return super.getName();
}
public void setName(String name) { public void setName(String name) {
if (backFace != null) { if (backFace != null) {
backFace.setName(name); backFace.setName(name);
@ -614,6 +628,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
super.setName(name); super.setName(name);
} }
@Override
public void setColor(ObjectColor color) { public void setColor(ObjectColor color) {
if (backFace != null) { if (backFace != null) {
backFace.setColor(color); backFace.setColor(color);

View file

@ -41,22 +41,22 @@ public final class ZombieToken extends TokenImpl {
public void setExpansionSetCodeForImage(String code) { public void setExpansionSetCodeForImage(String code) {
super.setExpansionSetCodeForImage(code); super.setExpansionSetCodeForImage(code);
if (getOriginalExpansionSetCode().equals("ISD")) { if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("ISD")) {
this.setTokenType(RandomUtil.nextInt(3) + 1); this.setTokenType(RandomUtil.nextInt(3) + 1);
} }
if (getOriginalExpansionSetCode().equals("C14")) { if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C14")) {
this.setTokenType(1); this.setTokenType(1);
} }
if (getOriginalExpansionSetCode().equals("EMN")) { if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("EMN")) {
this.setTokenType(RandomUtil.nextInt(3) + 1); this.setTokenType(RandomUtil.nextInt(3) + 1);
} }
if (getOriginalExpansionSetCode().equals("C19")) { if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C19")) {
this.setTokenType(RandomUtil.nextInt(2) + 1); this.setTokenType(RandomUtil.nextInt(2) + 1);
} }
if (getOriginalExpansionSetCode().equals("MIC")) { if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("MIC")) {
this.setTokenType(1); this.setTokenType(1);
} }
if (getOriginalExpansionSetCode().equals("VOW")) { if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("VOW")) {
this.setTokenType(1); this.setTokenType(1);
} }
} }

View file

@ -22,6 +22,7 @@ import mage.constants.ColoredManaSymbol;
import mage.constants.ManaType; import mage.constants.ManaType;
import mage.filter.FilterMana; import mage.filter.FilterMana;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
import java.util.*; import java.util.*;
@ -638,6 +639,10 @@ public final class ManaUtil {
return getColorIdentity(card.getColor(), String.join("", card.getManaCostSymbols()), card.getRules(), secondSide); return getColorIdentity(card.getColor(), String.join("", card.getManaCostSymbols()), card.getRules(), secondSide);
} }
public static FilterMana getColorIdentity(Token token) {
return getColorIdentity(token.getColor(), String.join("", token.getManaCostSymbols()), token.getAbilities().getRules(token.getName()), null);
}
public static int getColorIdentityHash(FilterMana colorIdentity) { public static int getColorIdentityHash(FilterMana colorIdentity) {
int hash = 3; int hash = 3;
hash = 23 * hash + (colorIdentity.isWhite() ? 1 : 0); hash = 23 * hash + (colorIdentity.isWhite() ? 1 : 0);