Face down images and cards rework (#11873)

Face down changes:
* GUI: added visible face down type and real card name for controller/owner (opponent can see it after game ends);
* GUI: added day/night button to view real card for controller/owner (opponent can see it after game ends);
* game: fixed that faced-down card can render symbols, abilities and other hidden data from a real card;
* images: added image support for normal faced-down cards;
* images: added image support for morph and megamorph faced-down cards;
* images: added image support for foretell faced-down cards;

Other changes:
* images: fixed missing tokens from DDD set;
* images: no more client restart to apply newly downloaded images or render settings;
* images: improved backface image quality (use main menu -> symbols to download it);
This commit is contained in:
Oleg Agafonov 2024-02-29 01:14:54 +04:00 committed by GitHub
parent 4901de12c1
commit e38a79f231
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
104 changed files with 2178 additions and 1495 deletions

View file

@ -0,0 +1,97 @@
package mage.view;
import mage.cards.Card;
import mage.game.command.CommandObject;
import mage.game.permanent.token.Token;
import mage.util.Copyable;
import java.io.Serializable;
/**
* TODO: delete, no needs?!
*
* GUI: card drawing info
* Can be different from real card name, set code, etc - see morph, copy, etc)
*
* @author JayDi85
*/
public class CardImageView implements Serializable, Copyable<CardImageView> {
private boolean isTokenRepository; // card or token database
private String cardName; // card or token
private String setCode; // card or token
private String cardNumber; // card only, token has "0"
private Integer imageNumber; // token only
private boolean isUseVariousArt; // card only
public CardImageView() {
}
public CardImageView(final CardImageView cardImageView) {
this.isTokenRepository = cardImageView.isTokenRepository;
this.cardName = cardImageView.cardName;
this.setCode = cardImageView.setCode;
this.cardNumber = cardImageView.cardNumber;
this.imageNumber = cardImageView.imageNumber;
this.isUseVariousArt = cardImageView.isUseVariousArt;
}
public CardImageView fromCard(Card card) {
this.isTokenRepository = false;
this.cardName = card.getName();
this.setCode = card.getExpansionSetCode();
this.cardNumber = card.getCardNumber();
this.imageNumber = card.getImageNumber();
this.isUseVariousArt = card.getUsesVariousArt();
return this;
}
public CardImageView fromToken(Token token) {
this.isTokenRepository = true;
this.cardName = token.getName();
this.setCode = token.getExpansionSetCode();
this.cardNumber = token.getCardNumber();
this.imageNumber = token.getImageNumber();
this.isUseVariousArt = false;
return this;
}
public CardImageView fromCommandObject(CommandObject commandObject) {
this.isTokenRepository = true;
this.cardName = commandObject.getName();
this.setCode = commandObject.getExpansionSetCode();
this.cardNumber = commandObject.getCardNumber();
this.imageNumber = commandObject.getImageNumber();
this.isUseVariousArt = false;
return this;
}
@Override
public CardImageView copy() {
return new CardImageView(this);
}
public boolean isTokenRepository() {
return isTokenRepository;
}
public String getCardName() {
return cardName;
}
public String getSetCode() {
return setCode;
}
public String getCardNumber() {
return cardNumber;
}
public Integer getImageNumber() {
return imageNumber;
}
public boolean isUseVariousArt() {
return isUseVariousArt;
}
}

View file

@ -16,9 +16,12 @@ import mage.abilities.icon.CardIcon;
import mage.abilities.icon.CardIconImpl;
import mage.abilities.icon.CardIconType;
import mage.abilities.keyword.AftermathAbility;
import mage.abilities.keyword.ForetellAbility;
import mage.cards.*;
import mage.cards.mock.MockCard;
import mage.cards.repository.CardInfo;
import mage.cards.repository.TokenInfo;
import mage.cards.repository.TokenRepository;
import mage.constants.*;
import mage.counters.Counter;
import mage.counters.CounterType;
@ -89,7 +92,8 @@ public class CardView extends SimpleCardView {
protected boolean isToken;
protected CardView ability;
protected int imageNumber;
protected String imageFileName = "";
protected int imageNumber = 0;
protected boolean extraDeckCard;
protected boolean transformable; // can toggle one card side to another (transformable cards, modal double faces)
@ -134,12 +138,12 @@ public class CardView extends SimpleCardView {
protected List<CardIcon> cardIcons = new ArrayList<>(); // additional icons to render
// GUI related: additional info about current object (example: real PT)
// warning, do not send full object, use some fields only (client must not get any server side data)
// warning, don't forget to hide it in face down cards (null)
protected MageInt originalPower = null;
protected MageInt originalToughness = null;
protected FilterMana originalColorIdentity = null;
protected UUID originalId = null;
protected String originalColorIdentity = null; // GUI related info for sorting, searching, etc
protected boolean originalIsCopy = false;
protected boolean originalIsCard = false;
/**
* Non game usage like deck editor
@ -154,6 +158,17 @@ public class CardView extends SimpleCardView {
this(card, game, false);
}
/**
* @param card
* @param game
* @param showAsControlled is the card view created for the card controller - used
* for morph / face down cards to know which player may see information for
* the card TODO: turn controller can be here too?
*/
public CardView(Card card, Game game, boolean showAsControlled) {
this(card, game, showAsControlled, false);
}
public CardView(Card card, SimpleCardView simpleCardView) {
this(card, null, false);
this.id = simpleCardView.getId();
@ -163,11 +178,6 @@ public class CardView extends SimpleCardView {
this.isSelected = simpleCardView.isSelected;
}
public CardView(Card card, Game game, UUID cardId) {
this(card, game, false);
this.id = cardId;
}
public CardView(final CardView cardView) {
super(cardView);
@ -192,6 +202,7 @@ public class CardView extends SimpleCardView {
this.expansionSetCode = cardView.expansionSetCode;
this.cardNumber = cardView.cardNumber;
this.imageFileName = cardView.imageFileName;
this.imageNumber = cardView.imageNumber;
this.color = cardView.color.copy();
@ -246,32 +257,21 @@ public class CardView extends SimpleCardView {
this.canAttack = cardView.canAttack;
this.canBlock = cardView.canBlock;
this.inViewerOnly = cardView.inViewerOnly;
this.originalPower = cardView.originalPower;
this.originalToughness = cardView.originalToughness;
this.originalColorIdentity = cardView.originalColorIdentity;
this.originalId = cardView.originalId;
this.originalIsCard = cardView.originalIsCard;
this.originalIsCopy = cardView.originalIsCopy;
if (cardView.cardIcons != null) {
cardView.cardIcons.forEach(icon -> this.cardIcons.add(icon.copy()));
}
this.originalPower = cardView.originalPower;
this.originalToughness = cardView.originalToughness;
this.originalColorIdentity = cardView.originalColorIdentity;
this.originalIsCopy = cardView.originalIsCopy;
this.playableStats = cardView.playableStats.copy();
this.isChoosable = cardView.isChoosable;
this.isSelected = cardView.isSelected;
}
/**
* @param card
* @param game
* @param controlled is the card view created for the card controller - used
* for morph / face down cards to know which player may see information for
* the card
*/
public CardView(Card card, Game game, boolean controlled) {
this(card, game, controlled, false, false);
}
private static String getCardTypeLine(Game game, Card card) {
StringBuilder sbType = new StringBuilder();
for (SuperType superType : card.getSuperType(game)) {
@ -290,145 +290,184 @@ public class CardView extends SimpleCardView {
}
/**
* @param card
* @param sourceCard
* @param game
* @param controlled is the card view created for the card controller - used
* @param showAsControlled is the card view created for the card controller/owner - used
* for morph / face down cards to know which player may see information for
* the card
* @param showFaceDownCard if true and the card is not on the battlefield,
* also a face down card is shown in the view, face down cards will be shown
* @param storeZone if true the card zone will be set in the zone attribute.
*/
public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) {
super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), game != null);
this.setOriginalValues(card);
this.imageNumber = card.getImageNumber();
public CardView(Card sourceCard, Game game, boolean showAsControlled, boolean storeZone) {
super(sourceCard.getId(), sourceCard.getExpansionSetCode(), sourceCard.getCardNumber(), sourceCard.getUsesVariousArt(), game != null);
// TODO: it's too big and can be buggy (something miss?) - must check and refactor: setup face down/up params, setup shared data like counters and targets
// Visible logic:
// * Normal card:
// - original name, original image
// * Face down card:
// * my cards or game end:
// - face down status + original name, face down image, day/night button
// * opponent cards:
// - face down status, face down image
// find real name from original card, cause face down status can be applied to card/spell
String sourceName = sourceCard.getMainCard().getName();
// find real spell characteristics before resolve
Card card = sourceCard.copy();
if (game != null && card instanceof Spell) {
card = ((Spell) card).getSpellAbility().getCharacteristics(game);
}
// use isFaceDown(game) only here to find real status, all other code must use this.faceDown
this.faceDown = game != null && sourceCard.isFaceDown(game);
boolean showFaceUp = !this.faceDown;
// show real name and day/night button for controller or any player at the game's end
boolean showHiddenFaceDownData = showAsControlled || (game != null && game.hasEnded());
// default image info
this.expansionSetCode = card.getExpansionSetCode();
this.cardNumber = card.getCardNumber();
this.imageFileName = card.getImageFileName();
this.imageNumber = card.getImageNumber();
this.usesVariousArt = card.getUsesVariousArt();
// permanent data
if (showFaceUp) {
this.setOriginalValues(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
boolean showFaceUp = true;
if (game != null) {
Zone cardZone = game.getState().getZone(card.getId());
if (card.isFaceDown(game)) {
showFaceUp = false;
if (Zone.BATTLEFIELD != cardZone) {
if (showFaceDownCard) {
showFaceUp = true;
if (storeZone) {
// TODO: research, why it used here?
this.zone = cardZone;
}
}
// FACE DOWN
if (!showFaceUp) {
this.fillEmptyWithImageInfo(game, card, true);
// can show face up card name for controller or game end
String visibleName = CardUtil.getCardNameForGUI(showHiddenFaceDownData ? sourceName : "", this.imageFileName);
this.name = visibleName;
this.displayName = visibleName;
this.displayFullName = visibleName;
this.alternateName = visibleName;
// workaround to add PT, creature type and face up ability text (for stack and battlefield zones only)
// in other zones it has only face down status/name
if (sourceCard instanceof Spell
|| card instanceof Permanent) {
this.power = Integer.toString(card.getPower().getValue());
this.toughness = Integer.toString(card.getToughness().getValue());
this.cardTypes = new ArrayList<>(card.getCardType());
this.rules = new ArrayList<>(card.getRules());
// additional rules for stack (example: morph ability text)
if (sourceCard instanceof Spell) {
List<String> extraRules = sourceCard.getSpellAbility().getSpellAbilityCastMode().getAdditionalRulesOnStack();
if (extraRules != null) {
this.rules.addAll(extraRules);
}
}
}
if (storeZone) {
this.zone = cardZone;
// GUI: enable day/night button to view original face up card
if (showHiddenFaceDownData) {
this.transformable = true;
this.secondCardFace = new CardView(sourceCard.getMainCard()); // do not use game param, so it will take default card
this.alternateName = sourceCard.getMainCard().getName();
}
}
// boolean showFaceUp = game == null || !card.isFaceDown(game) || (!game.getState().getZone(card.getId()).equals(Zone.BATTLEFIELD) && showFaceDownCard);
if (!showFaceUp) {
this.fillEmpty(card, controlled);
if (card instanceof Spell) {
// TODO: add face down image here???
// special handling for casting of Morph cards
if (controlled) {
this.name = card.getName();
this.displayName = card.getName();
this.displayFullName = card.getName();
this.alternateName = card.getName();
// FACE UP and shared data like counters
if (showFaceUp) {
SplitCard splitCard = null;
if (card instanceof SplitCard) {
splitCard = (SplitCard) card;
rotate = (card.getSpellAbility().getSpellAbilityType()) != SpellAbilityType.SPLIT_AFTERMATH;
} else if (card instanceof Spell) {
switch (card.getSpellAbility().getSpellAbilityType()) {
case SPLIT_FUSED:
splitCard = (SplitCard) ((Spell) card).getCard();
rotate = true;
break;
case SPLIT_AFTERMATH:
splitCard = (SplitCard) ((Spell) card).getCard();
rotate = false;
break;
case SPLIT_LEFT:
case SPLIT_RIGHT:
rotate = true;
break;
case MODAL_LEFT:
case MODAL_RIGHT:
rotate = false;
break;
}
this.power = "2";
this.toughness = "2";
this.rules.add("You may cast this card as a 2/2 face-down creature, with no text,"
+ " no name, no subtypes, and no mana cost by paying {3} rather than paying its mana cost.");
return;
} else if (card instanceof Permanent) {
this.power = Integer.toString(card.getPower().getValue());
this.toughness = Integer.toString(card.getToughness().getValue());
this.cardTypes = new ArrayList<>(card.getCardType(game));
this.faceDown = card.isFaceDown(game);
}
String fullCardName;
if (splitCard != null) {
this.isSplitCard = true;
leftSplitName = splitCard.getLeftHalfCard().getName();
leftSplitCostsStr = String.join("", splitCard.getLeftHalfCard().getManaCostSymbols());
leftSplitRules = splitCard.getLeftHalfCard().getRules(game);
leftSplitTypeLine = getCardTypeLine(game, splitCard.getLeftHalfCard());
rightSplitName = splitCard.getRightHalfCard().getName();
rightSplitCostsStr = String.join("", splitCard.getRightHalfCard().getManaCostSymbols());
rightSplitRules = splitCard.getRightHalfCard().getRules(game);
rightSplitTypeLine = getCardTypeLine(game, splitCard.getRightHalfCard());
fullCardName = card.getName(); // split card contains full name as normal
this.manaCostLeftStr = splitCard.getLeftHalfCard().getManaCostSymbols();
this.manaCostRightStr = splitCard.getRightHalfCard().getManaCostSymbols();
} else if (card instanceof ModalDoubleFacedCard) {
this.isModalDoubleFacedCard = true;
ModalDoubleFacedCard mainCard = ((ModalDoubleFacedCard) card);
fullCardName = mainCard.getLeftHalfCard().getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + mainCard.getRightHalfCard().getName();
this.manaCostLeftStr = mainCard.getLeftHalfCard().getManaCostSymbols();
this.manaCostRightStr = mainCard.getRightHalfCard().getManaCostSymbols();
} else if (card instanceof AdventureCard) {
this.isSplitCard = true;
AdventureCard adventureCard = ((AdventureCard) card);
leftSplitName = adventureCard.getName();
leftSplitCostsStr = String.join("", adventureCard.getManaCostSymbols());
leftSplitRules = adventureCard.getSharedRules(game);
leftSplitTypeLine = getCardTypeLine(game, adventureCard);
AdventureCardSpell adventureCardSpell = adventureCard.getSpellCard();
rightSplitName = adventureCardSpell.getName();
rightSplitCostsStr = String.join("", adventureCardSpell.getManaCostSymbols());
rightSplitRules = adventureCardSpell.getRules(game);
rightSplitTypeLine = getCardTypeLine(game, adventureCardSpell);
fullCardName = adventureCard.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + adventureCardSpell.getName();
this.manaCostLeftStr = adventureCard.getManaCostSymbols();
this.manaCostRightStr = adventureCardSpell.getManaCostSymbols();
} else if (card instanceof MockCard) {
// deck editor cards
fullCardName = ((MockCard) card).getFullName(true);
this.manaCostLeftStr = ((MockCard) card).getManaCostStr(CardInfo.ManaCostSide.LEFT);
this.manaCostRightStr = ((MockCard) card).getManaCostStr(CardInfo.ManaCostSide.RIGHT);
} else {
// this.hideInfo = true;
return;
fullCardName = card.getName();
this.manaCostLeftStr = card.getManaCostSymbols();
this.manaCostRightStr = new ArrayList<>();
}
this.name = card.getName();
this.displayName = card.getName();
this.displayFullName = fullCardName;
this.rules = new ArrayList<>(card.getRules(game));
this.manaValue = card.getManaValue();
}
SplitCard splitCard = null;
if (card instanceof SplitCard) {
splitCard = (SplitCard) card;
rotate = (card.getSpellAbility().getSpellAbilityType()) != SpellAbilityType.SPLIT_AFTERMATH;
} else if (card instanceof Spell) {
switch (card.getSpellAbility().getSpellAbilityType()) {
case SPLIT_FUSED:
splitCard = (SplitCard) ((Spell) card).getCard();
rotate = true;
break;
case SPLIT_AFTERMATH:
splitCard = (SplitCard) ((Spell) card).getCard();
rotate = false;
break;
case SPLIT_LEFT:
case SPLIT_RIGHT:
rotate = true;
break;
case MODAL_LEFT:
case MODAL_RIGHT:
rotate = false;
break;
}
}
String fullCardName;
if (splitCard != null) {
this.isSplitCard = true;
leftSplitName = splitCard.getLeftHalfCard().getName();
leftSplitCostsStr = String.join("", splitCard.getLeftHalfCard().getManaCostSymbols());
leftSplitRules = splitCard.getLeftHalfCard().getRules(game);
leftSplitTypeLine = getCardTypeLine(game, splitCard.getLeftHalfCard());
rightSplitName = splitCard.getRightHalfCard().getName();
rightSplitCostsStr = String.join("", splitCard.getRightHalfCard().getManaCostSymbols());
rightSplitRules = splitCard.getRightHalfCard().getRules(game);
rightSplitTypeLine = getCardTypeLine(game, splitCard.getRightHalfCard());
fullCardName = card.getName(); // split card contains full name as normal
this.manaCostLeftStr = splitCard.getLeftHalfCard().getManaCostSymbols();
this.manaCostRightStr = splitCard.getRightHalfCard().getManaCostSymbols();
} else if (card instanceof ModalDoubleFacedCard) {
this.isModalDoubleFacedCard = true;
ModalDoubleFacedCard mainCard = ((ModalDoubleFacedCard) card);
fullCardName = mainCard.getLeftHalfCard().getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + mainCard.getRightHalfCard().getName();
this.manaCostLeftStr = mainCard.getLeftHalfCard().getManaCostSymbols();
this.manaCostRightStr = mainCard.getRightHalfCard().getManaCostSymbols();
} else if (card instanceof AdventureCard) {
this.isSplitCard = true;
AdventureCard adventureCard = ((AdventureCard) card);
leftSplitName = adventureCard.getName();
leftSplitCostsStr = String.join("", adventureCard.getManaCostSymbols());
leftSplitRules = adventureCard.getSharedRules(game);
leftSplitTypeLine = getCardTypeLine(game, adventureCard);
AdventureCardSpell adventureCardSpell = adventureCard.getSpellCard();
rightSplitName = adventureCardSpell.getName();
rightSplitCostsStr = String.join("", adventureCardSpell.getManaCostSymbols());
rightSplitRules = adventureCardSpell.getRules(game);
rightSplitTypeLine = getCardTypeLine(game, adventureCardSpell);
fullCardName = adventureCard.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + adventureCardSpell.getName();
this.manaCostLeftStr = adventureCard.getManaCostSymbols();
this.manaCostRightStr = adventureCardSpell.getManaCostSymbols();
} else if (card instanceof MockCard) {
// deck editor cards
fullCardName = ((MockCard) card).getFullName(true);
this.manaCostLeftStr = ((MockCard) card).getManaCostStr(CardInfo.ManaCostSide.LEFT);
this.manaCostRightStr = ((MockCard) card).getManaCostStr(CardInfo.ManaCostSide.RIGHT);
} else {
fullCardName = card.getName();
this.manaCostLeftStr = card.getManaCostSymbols();
this.manaCostRightStr = new ArrayList<>();
}
this.name = card.getName();
this.displayName = card.getName();
this.displayFullName = fullCardName;
this.rules = new ArrayList<>(card.getRules(game));
this.manaValue = card.getManaValue();
// shared info - counters and other
if (card instanceof Permanent) {
this.mageObjectType = MageObjectType.PERMANENT;
Permanent permanent = (Permanent) card;
@ -466,61 +505,64 @@ public class CardView extends SimpleCardView {
}
}
this.power = Integer.toString(card.getPower().getValue());
this.toughness = Integer.toString(card.getToughness().getValue());
this.cardTypes = new ArrayList<>(card.getCardType(game));
this.subTypes = card.getSubtype(game).copy();
this.superTypes = card.getSuperType(game);
this.color = card.getColor(game).copy();
this.flipCard = card.isFlipCard();
this.faceDown = !showFaceUp;
// FACE UP INFO
if (showFaceUp) {
this.power = Integer.toString(card.getPower().getValue());
this.toughness = Integer.toString(card.getToughness().getValue());
this.cardTypes = new ArrayList<>(card.getCardType(game));
this.subTypes = card.getSubtype(game).copy();
this.superTypes = card.getSuperType(game);
this.color = card.getColor(game).copy();
this.flipCard = card.isFlipCard();
if (card instanceof PermanentToken) {
this.isToken = true;
this.mageObjectType = MageObjectType.TOKEN;
this.rarity = Rarity.COMMON;
this.rules = new ArrayList<>(card.getRules(game));
} else {
this.rarity = card.getRarity();
this.isToken = false;
}
this.extraDeckCard = card.isExtraDeckCard();
// transformable, double faces cards
this.transformable = card.isTransformable();
Card secondSideCard = card.getSecondCardFace();
if (secondSideCard != null) {
this.secondCardFace = new CardView(secondSideCard, game);
this.alternateName = secondCardFace.getName();
}
this.flipCard = card.isFlipCard();
if (card.isFlipCard() && card.getFlipCardName() != null) {
this.alternateName = card.getFlipCardName();
}
if (card instanceof ModalDoubleFacedCard) {
this.transformable = true; // enable GUI day/night button
ModalDoubleFacedCard mdfCard = (ModalDoubleFacedCard) card;
this.secondCardFace = new CardView(mdfCard.getRightHalfCard(), game);
this.alternateName = mdfCard.getRightHalfCard().getName();
}
Card meldsToCard = card.getMeldsToCard();
if (meldsToCard != null) {
this.transformable = true; // enable GUI day/night button
this.secondCardFace = new CardView(meldsToCard, game);
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 PermanentToken) {
this.isToken = true;
this.mageObjectType = MageObjectType.TOKEN;
this.rarity = Rarity.SPECIAL;
this.rules = new ArrayList<>(card.getRules(game));
} else {
this.rarity = card.getRarity();
this.isToken = false;
}
this.extraDeckCard = card.isExtraDeckCard();
// transformable, double faces cards
this.transformable = card.isTransformable();
Card secondSideCard = card.getSecondCardFace();
if (secondSideCard != null) {
this.secondCardFace = new CardView(secondSideCard, game);
this.alternateName = secondCardFace.getName();
}
this.flipCard = card.isFlipCard();
if (card.isFlipCard() && card.getFlipCardName() != null) {
this.alternateName = card.getFlipCardName();
}
if (card instanceof ModalDoubleFacedCard) {
this.transformable = true; // enable GUI day/night button
ModalDoubleFacedCard mdfCard = (ModalDoubleFacedCard) card;
this.secondCardFace = new CardView(mdfCard.getRightHalfCard(), game);
this.alternateName = mdfCard.getRightHalfCard().getName();
}
Card meldsToCard = card.getMeldsToCard();
if (meldsToCard != null) {
this.transformable = true; // enable GUI day/night button
this.secondCardFace = new CardView(meldsToCard, game);
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();
}
}
// shared info - targets
if (card instanceof Spell) {
this.mageObjectType = MageObjectType.SPELL;
Spell spell = (Spell) card;
@ -533,31 +575,6 @@ public class CardView extends SimpleCardView {
}
}
// Determine what part of the art to slice out for spells on the stack which originate
// from a split, fuse, or aftermath split card.
// Modal double faces cards draws as normal cards
SpellAbilityType ty = spell.getSpellAbility().getSpellAbilityType();
if (ty == SpellAbilityType.SPLIT_RIGHT || ty == SpellAbilityType.SPLIT_LEFT || ty == SpellAbilityType.SPLIT_FUSED) {
// Needs a special art rect
if (ty == SpellAbilityType.SPLIT_FUSED) {
artRect = ArtRect.SPLIT_FUSED;
} else if (spell.getCard() != null) {
SplitCard wholeCard = ((SplitCardHalf) spell.getCard()).getParentCard();
Abilities<Ability> aftermathHalfAbilities = wholeCard.getRightHalfCard().getAbilities(game);
if (aftermathHalfAbilities.stream().anyMatch(AftermathAbility.class::isInstance)) {
if (ty == SpellAbilityType.SPLIT_RIGHT) {
artRect = ArtRect.AFTERMATH_BOTTOM;
} else {
artRect = ArtRect.AFTERMATH_TOP;
}
} else if (ty == SpellAbilityType.SPLIT_RIGHT) {
artRect = ArtRect.SPLIT_RIGHT;
} else {
artRect = ArtRect.SPLIT_LEFT;
}
}
}
// show for modal spell, which mode was chosen
if (spell.getSpellAbility().isModal()) {
for (UUID modeId : spell.getSpellAbility().getModes().getSelectedModes()) {
@ -580,32 +597,62 @@ public class CardView extends SimpleCardView {
}
}
}
}
}
// Cases, classes and sagas have portrait art
if (card.getSubtype().contains(SubType.CASE) ||
card.getSubtype().contains(SubType.CLASS)) {
artRect = ArtRect.FULL_LENGTH_LEFT;
} else if (card.getSubtype().contains(SubType.SAGA)) {
artRect = ArtRect.FULL_LENGTH_RIGHT;
// render info
if (showFaceUp) {
if (card instanceof Spell) {
Spell spell = (Spell) card;
// Determine what part of the art to slice out for spells on the stack which originate
// from a split, fuse, or aftermath split card.
// Modal double faces cards draws as normal cards
SpellAbilityType ty = spell.getSpellAbility().getSpellAbilityType();
if (ty == SpellAbilityType.SPLIT_RIGHT || ty == SpellAbilityType.SPLIT_LEFT || ty == SpellAbilityType.SPLIT_FUSED) {
// Needs a special art rect
if (ty == SpellAbilityType.SPLIT_FUSED) {
artRect = ArtRect.SPLIT_FUSED;
} else if (spell.getCard() != null) {
SplitCard wholeCard = ((SplitCardHalf) spell.getCard()).getParentCard();
Abilities<Ability> aftermathHalfAbilities = wholeCard.getRightHalfCard().getAbilities(game);
if (aftermathHalfAbilities.stream().anyMatch(AftermathAbility.class::isInstance)) {
if (ty == SpellAbilityType.SPLIT_RIGHT) {
artRect = ArtRect.AFTERMATH_BOTTOM;
} else {
artRect = ArtRect.AFTERMATH_TOP;
}
} else if (ty == SpellAbilityType.SPLIT_RIGHT) {
artRect = ArtRect.SPLIT_RIGHT;
} else {
artRect = ArtRect.SPLIT_LEFT;
}
}
}
}
// Cases, classes and sagas have portrait art
if (card.getSubtype().contains(SubType.CASE) ||
card.getSubtype().contains(SubType.CLASS)) {
artRect = ArtRect.FULL_LENGTH_LEFT;
} else if (card.getSubtype().contains(SubType.SAGA)) {
artRect = ArtRect.FULL_LENGTH_RIGHT;
}
// Frame color
this.frameColor = card.getFrameColor(game).copy();
// Frame style
this.frameStyle = card.getFrameStyle();
// Get starting loyalty
this.startingLoyalty = CardUtil.convertLoyaltyOrDefense(card.getStartingLoyalty());
// Get starting defense
this.startingDefense = CardUtil.convertLoyaltyOrDefense(card.getStartingDefense());
// add card icons at the end, so it will have full card view data
this.generateCardIcons(null, card, game);
}
// Frame color
this.frameColor = card.getFrameColor(game).copy();
// Frame style
this.frameStyle = card.getFrameStyle();
// Get starting loyalty
this.startingLoyalty = CardUtil.convertLoyaltyOrDefense(card.getStartingLoyalty());
// Get starting defense
this.startingDefense = CardUtil.convertLoyaltyOrDefense(card.getStartingDefense());
// add card icons at the end, so it will have full card view data
this.generateCardIcons(null, card, game);
}
/**
@ -740,11 +787,14 @@ public class CardView extends SimpleCardView {
}
}
@Deprecated // TODO: research and raplace all usages to normal calls, see constructors for EmblemView and other
public CardView(MageObject object, Game game) {
super(object.getId(), object.getExpansionSetCode(), object.getCardNumber(), false, true);
this.setOriginalValues(object);
this.imageFileName = object.getImageFileName();
this.imageNumber = object.getImageNumber();
this.name = object.getName();
this.displayName = object.getName();
this.displayFullName = object.getName();
@ -770,7 +820,7 @@ public class CardView extends SimpleCardView {
if (object instanceof PermanentToken) {
this.mageObjectType = MageObjectType.TOKEN;
PermanentToken permanentToken = (PermanentToken) object;
this.rarity = Rarity.COMMON;
this.rarity = Rarity.SPECIAL;
this.rules = new ArrayList<>(permanentToken.getRules(game));
} else if (object instanceof Emblem) {
this.mageObjectType = MageObjectType.EMBLEM;
@ -837,9 +887,10 @@ public class CardView extends SimpleCardView {
this.frameStyle = FrameStyle.M15_NORMAL;
this.expansionSetCode = emblem.getExpansionSetCode();
this.cardNumber = emblem.getCardNumber();
this.imageFileName = emblem.getImageFileName();
this.imageNumber = emblem.getImageNumber();
this.usesVariousArt = emblem.getUsesVariousArt();
this.rarity = Rarity.COMMON;
this.rarity = Rarity.SPECIAL;
this.playableStats = emblem.playableStats.copy();
this.isChoosable = emblem.isChoosable();
@ -859,8 +910,9 @@ public class CardView extends SimpleCardView {
this.frameStyle = FrameStyle.M15_NORMAL;
this.expansionSetCode = dungeon.getExpansionSetCode();
this.cardNumber = "";
this.imageFileName = "";
this.imageNumber = 0;
this.rarity = Rarity.COMMON;
this.rarity = Rarity.SPECIAL;
this.playableStats = dungeon.playableStats.copy();
this.isChoosable = dungeon.isChoosable();
@ -881,8 +933,9 @@ public class CardView extends SimpleCardView {
this.frameStyle = FrameStyle.M15_NORMAL;
this.expansionSetCode = plane.getExpansionSetCode();
this.cardNumber = "";
this.imageFileName = "";
this.imageNumber = 0;
this.rarity = Rarity.COMMON;
this.rarity = Rarity.SPECIAL;
this.playableStats = plane.playableStats.copy();
this.isChoosable = plane.isChoosable();
@ -903,8 +956,9 @@ public class CardView extends SimpleCardView {
this.cardNumber = designation.getCardNumber();
this.expansionSetCode = designation.getExpansionSetCode();
this.cardNumber = "";
this.imageFileName = "";
this.imageNumber = 0;
this.rarity = Rarity.COMMON;
this.rarity = Rarity.SPECIAL;
// no playable/chooseable marks for designations
}
@ -913,7 +967,7 @@ public class CardView extends SimpleCardView {
if (!empty) {
throw new IllegalArgumentException("Not supported.");
}
fillEmpty(null, false);
fillEmptyWithImageInfo(null, null, false);
}
public static boolean cardViewEquals(CardView a, CardView b) { // TODO: This belongs in CardView
@ -936,14 +990,21 @@ public class CardView extends SimpleCardView {
&& a.getManaCostStr().equals(b.getManaCostStr())
&& a.getRules().equals(b.getRules())
&& Objects.equals(a.getRarity(), b.getRarity())
&& Objects.equals(a.getCardNumber(), b.getCardNumber())
&& Objects.equals(a.getExpansionSetCode(), b.getExpansionSetCode())
&& a.getFrameStyle() == b.getFrameStyle()
&& Objects.equals(a.getCounters(), b.getCounters())
&& a.isFaceDown() == b.isFaceDown())) {
return false;
}
if (!(Objects.equals(a.getExpansionSetCode(), b.getExpansionSetCode())
&& Objects.equals(a.getCardNumber(), b.getCardNumber())
&& Objects.equals(a.getImageNumber(), b.getImageNumber())
&& Objects.equals(a.getImageFileName(), b.getImageFileName())
)) {
return false;
}
if (!(a instanceof PermanentView)) {
return true;
}
@ -953,10 +1014,16 @@ public class CardView extends SimpleCardView {
&& aa.getDamage() == bb.getDamage();
}
private void fillEmpty(Card card, boolean controlled) {
this.name = "Face Down";
this.displayName = name;
this.displayFullName = name;
private void fillEmptyWithImageInfo(Game game, Card imageSourceCard, boolean isFaceDown) {
this.name = "";
this.displayName = "";
this.displayFullName = "";
this.expansionSetCode = "";
this.cardNumber = "0";
this.imageFileName = "";
this.imageNumber = 0;
this.usesVariousArt = false;
this.rules = new ArrayList<>();
this.power = "";
this.toughness = "";
@ -973,33 +1040,68 @@ public class CardView extends SimpleCardView {
this.manaCostLeftStr = new ArrayList<>();
this.manaCostRightStr = new ArrayList<>();
this.manaValue = 0;
this.rarity = Rarity.SPECIAL; // hide rarity info
// the controller can see more information (e.g. enlarged image) than other players for face down cards (e.g. Morph played face down)
if (!controlled) {
this.rarity = Rarity.COMMON;
this.expansionSetCode = "";
this.cardNumber = "0";
this.imageNumber = 0;
} else {
this.rarity = card.getRarity();
}
if (imageSourceCard != null) {
// keep inner images info (server side card already contain actual info)
String imageSetCode = imageSourceCard.getExpansionSetCode();
String imageCardNumber = imageSourceCard.getCardNumber();
String imageFileName = imageSourceCard.getImageFileName();
Integer imageNumber = imageSourceCard.getImageNumber();
boolean imageUsesVariousArt = imageSourceCard.getUsesVariousArt();
if (imageSetCode.equals(TokenRepository.XMAGE_TOKENS_SET_CODE)) {
this.expansionSetCode = imageSetCode;
this.cardNumber = imageCardNumber;
this.imageFileName = imageFileName;
this.imageNumber = imageNumber;
this.usesVariousArt = imageUsesVariousArt;
}
if (card != null) {
if (card instanceof Permanent) {
if (imageSourceCard instanceof PermanentToken) {
this.mageObjectType = MageObjectType.TOKEN;
} else if (imageSourceCard instanceof Permanent) {
this.mageObjectType = MageObjectType.PERMANENT;
} else if (card.isCopy()) {
} else if (imageSourceCard.isCopy()) {
this.mageObjectType = MageObjectType.COPY_CARD;
} else if (imageSourceCard instanceof Spell) {
this.mageObjectType = MageObjectType.SPELL;
} else {
this.mageObjectType = MageObjectType.CARD;
}
if (card instanceof PermanentToken) {
this.mageObjectType = MageObjectType.TOKEN;
}
if (card instanceof Spell) {
this.mageObjectType = MageObjectType.SPELL;
}
}
// make default face down image
// TODO: implement diff backface images someday and insert here (user data + card owner)
if (isFaceDown && this.imageFileName.isEmpty()) {
this.name = "";
this.displayName = this.name;
this.displayFullName = this.name;
// as foretell face down
// TODO: it's not ok to use that code - server side objects must has all data, see BecomesFaceDownCreatureEffect.makeFaceDownObject
// it must be a more global bug for card characteristics, not client side viewer
if (game != null && imageSourceCard != null && ForetellAbility.isCardInForetell(imageSourceCard, game)) {
TokenInfo tokenInfo = TokenRepository.instance.findPreferredTokenInfoForXmage(TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_FORETELL, this.getId());
if (tokenInfo != null) {
this.expansionSetCode = tokenInfo.getSetCode();
this.cardNumber = "0";
this.imageFileName = tokenInfo.getName();
this.imageNumber = tokenInfo.getImageNumber();
this.usesVariousArt = false;
}
return;
}
// as normal face down
TokenInfo tokenInfo = TokenRepository.instance.findPreferredTokenInfoForXmage(TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_MANUAL, this.getId());
if (tokenInfo != null) {
this.expansionSetCode = tokenInfo.getSetCode();
this.cardNumber = "0";
this.imageFileName = tokenInfo.getName();
this.imageNumber = tokenInfo.getImageNumber();
this.usesVariousArt = false;
}
}
}
CardView(Token token, Game game) {
@ -1026,9 +1128,9 @@ public class CardView extends SimpleCardView {
this.manaCostRightStr = new ArrayList<>();
this.rarity = Rarity.SPECIAL;
// source object is a token, so no card number
this.expansionSetCode = token.getExpansionSetCode();
this.cardNumber = token.getCardNumber();
this.imageFileName = token.getImageFileName();
this.imageNumber = token.getImageNumber();
}
@ -1063,18 +1165,15 @@ public class CardView extends SimpleCardView {
if (object == null) {
return;
}
// Only valid objects to transfer original values are Card and Token
// only valid objects to transfer original values are Card and Token
if (object instanceof Card || object instanceof Token) {
this.originalPower = object.getPower();
this.originalToughness = object.getToughness();
this.originalIsCopy = object.isCopy();
this.originalId = object.getId();
if (object instanceof Card) {
this.originalColorIdentity = ((Card) object).getColorIdentity();
this.originalIsCard = true;
} else if (object instanceof Token) {
this.originalColorIdentity = ManaUtil.getColorIdentity((Token) object);
this.originalColorIdentity = findColorIdentityStr(((Card) object).getColorIdentity());
} else {
this.originalColorIdentity = findColorIdentityStr(ManaUtil.getColorIdentity((Token) object));
}
}
}
@ -1190,9 +1289,8 @@ public class CardView extends SimpleCardView {
return rarity;
}
public String getColorIdentityStr() {
FilterMana colorInfo = this.originalColorIdentity;
if (colorInfo != null) {
public String findColorIdentityStr(FilterMana colorInfo) {
if (colorInfo == null) {
colorInfo = new FilterMana();
}
@ -1350,6 +1448,10 @@ public class CardView extends SimpleCardView {
return bandedCards;
}
public String getImageFileName() {
return imageFileName;
}
public int getImageNumber() {
return imageNumber;
}
@ -1488,18 +1590,14 @@ public class CardView extends SimpleCardView {
return this.originalToughness;
}
public UUID getOriginalId() {
return this.originalId;
public String getOriginalColorIdentity() {
return this.originalColorIdentity != null ? this.originalColorIdentity : "";
}
public boolean isOriginalACopy() {
return this.originalIsCopy;
}
public boolean isOriginalACard() {
return this.originalIsCard;
}
public List<CardIcon> getCardIcons() {
return this.cardIcons;
}

View file

@ -12,6 +12,7 @@ import mage.game.command.Plane;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.target.targetpointer.TargetPointer;
import mage.util.CardUtil;
import mage.util.GameLog;
import org.apache.log4j.Logger;
@ -50,15 +51,15 @@ public class CardsView extends LinkedHashMap<UUID, CardView> {
}
}
public CardsView(Game game, Collection<? extends Card> cards) {
public CardsView(Game game, Collection<? extends Card> cards, UUID createdForPlayerId) {
for (Card card : cards) {
this.put(card.getId(), new CardView(card, game, false));
this.put(card.getId(), new CardView(card, game, CardUtil.canShowAsControlled(card, createdForPlayerId)));
}
}
public CardsView(Game game, Collection<? extends Card> cards, boolean showFaceDown, boolean storeZone) {
public CardsView(Game game, Collection<? extends Card> cards, UUID createdForPlayerId, boolean storeZone) {
for (Card card : cards) {
this.put(card.getId(), new CardView(card, game, false, showFaceDown, storeZone));
this.put(card.getId(), new CardView(card, game, CardUtil.canShowAsControlled(card, createdForPlayerId), storeZone));
}
}

View file

@ -14,6 +14,8 @@ public interface CommandObjectView extends SelectableObjectView {
UUID getId();
String getImageFileName();
int getImageNumber();
List<String> getRules();

View file

@ -2,10 +2,13 @@
package mage.view;
import java.io.Serializable;
import java.util.UUID;
import mage.cards.Card;
import mage.constants.MageObjectType;
import mage.game.Game;
import mage.game.command.Commander;
import mage.util.CardUtil;
/**
*
@ -13,8 +16,8 @@ import mage.game.command.Commander;
*/
public class CommanderView extends CardView implements CommandObjectView, Serializable{
public CommanderView(Commander commander, Card sourceCard, Game game) {
super(sourceCard, game, false);
public CommanderView(Commander commander, Card sourceCard, Game game, UUID createdForPlayerId) {
super(sourceCard, game, CardUtil.canShowAsControlled(sourceCard, createdForPlayerId));
this.mageObjectType = MageObjectType.COMMANDER;
}
}

View file

@ -14,15 +14,17 @@ public class DungeonView implements CommandObjectView, Serializable {
protected UUID id;
protected String name;
protected int imageNum;
protected String expansionSetCode;
protected String imageFileName = "";
protected int imageNumber = 0;
protected String expansionSetCode = "";
protected List<String> rules;
protected PlayableObjectStats playableStats = new PlayableObjectStats();
public DungeonView(Dungeon dungeon) {
this.id = dungeon.getId();
this.name = dungeon.getName();
this.imageNum = dungeon.getImageNumber();
this.imageFileName = dungeon.getImageFileName();
this.imageNumber = dungeon.getImageNumber();
this.expansionSetCode = dungeon.getExpansionSetCode();
this.rules = dungeon.getRules();
}
@ -42,9 +44,14 @@ public class DungeonView implements CommandObjectView, Serializable {
return id;
}
@Override
public String getImageFileName() {
return imageFileName;
}
@Override
public int getImageNumber() {
return imageNum;
return imageNumber;
}
@Override

View file

@ -16,7 +16,8 @@ public class EmblemView implements CommandObjectView, Serializable {
protected UUID id;
protected String name;
protected String cardNumber = "";
protected int imageNum;
protected String imageFileName = "";
protected int imageNumber;
protected boolean usesVariousArt = false;
protected String expansionSetCode;
protected List<String> rules;
@ -25,7 +26,8 @@ public class EmblemView implements CommandObjectView, Serializable {
public EmblemView(Emblem emblem) {
this.id = emblem.getId();
this.name = emblem.getName();
this.imageNum = emblem.getImageNumber();
this.imageFileName = emblem.getImageFileName();
this.imageNumber = emblem.getImageNumber();
this.expansionSetCode = emblem.getExpansionSetCode();
this.rules = emblem.getAbilities().getRules(emblem.getName());
if (emblem instanceof EmblemOfCard) {
@ -54,9 +56,15 @@ public class EmblemView implements CommandObjectView, Serializable {
}
@Override
public int getImageNumber() {
return imageNum;
public String getImageFileName() {
return imageFileName;
}
@Override
public int getImageNumber() {
return imageNumber;
}
public boolean getUsesVariousArt() {
return this.usesVariousArt;
}

View file

@ -6,6 +6,7 @@ import java.util.UUID;
import mage.cards.Card;
import mage.game.ExileZone;
import mage.game.Game;
import mage.util.CardUtil;
/**
*
@ -17,11 +18,11 @@ public class ExileView extends CardsView {
private final String name;
private final UUID id;
public ExileView(ExileZone exileZone, Game game) {
public ExileView(ExileZone exileZone, Game game, UUID createdForPlayerId) {
this.name = exileZone.getName();
this.id = exileZone.getId();
for (Card card: exileZone.getCards(game)) {
this.put(card.getId(), new CardView(card, game, false));
this.put(card.getId(), new CardView(card, game, CardUtil.canShowAsControlled(card, createdForPlayerId)));
}
}

View file

@ -24,6 +24,7 @@ import mage.game.stack.StackAbility;
import mage.game.stack.StackObject;
import mage.players.PlayableObjectsList;
import mage.players.Player;
import mage.util.CardUtil;
import org.apache.log4j.Logger;
import java.io.Serializable;
@ -73,15 +74,16 @@ public class GameView implements Serializable {
if (player.getId().equals(createdForPlayerId)) {
createdForPlayer = player;
this.myPlayerId = player.getId();
this.myHand.putAll(new CardsView(game, player.getHand().getCards(game)));
this.myHand.putAll(new CardsView(game, player.getHand().getCards(game), createdForPlayerId));
}
}
for (StackObject stackObject : state.getStack()) {
if (stackObject instanceof Spell) {
// Spell
CardView spellView = new CardView((Spell) stackObject, game, stackObject.getControllerId().equals(createdForPlayerId));
spellView.paid = ((Spell) stackObject).getSpellAbility().getManaCostsToPay().isPaid();
stack.put(stackObject.getId(), spellView);
Spell spell = (Spell) stackObject;
CardView spellView = new CardView(spell, game, CardUtil.canShowAsControlled(spell, createdForPlayerId));
spellView.paid = spell.getSpellAbility().getManaCostsToPay().isPaid();
stack.put(spell.getId(), spellView);
} else if (stackObject instanceof StackAbility) {
// Stack Ability
MageObject object = game.getObject(stackObject.getSourceId());
@ -93,9 +95,9 @@ public class GameView implements Serializable {
if (object != null) {
if (object instanceof Permanent) {
boolean controlled = ((Permanent) object).getControllerId().equals(createdForPlayerId);
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, object.getName(), object, new CardView(((Permanent) object), game, controlled, false, false)));
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, object.getName(), object, new CardView(((Permanent) object), game, controlled, false)));
} else {
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, card.getName(), card, new CardView(card, game, false, false, false)));
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, card.getName(), card, new CardView(card, game, false, false)));
}
} else {
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, "", card, new CardView(card, game)));
@ -132,9 +134,9 @@ public class GameView implements Serializable {
} else if (object instanceof Designation) {
Designation designation = (Designation) game.getObject(object.getId());
if (designation != null) {
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, designation.getName(), designation, new CardView(designation, game)));
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, designation.getName(), designation, new CardView(designation, (StackAbility) stackObject)));
} else {
LOGGER.fatal("Designation object not found: " + object.getName() + ' ' + object.toString() + ' ' + object.getClass().toString());
throw new IllegalArgumentException("Designation object not found: " + object + " - " + object.getClass().toString());
}
} else if (object instanceof StackAbility) {
StackAbility stackAbility = ((StackAbility) object);
@ -142,20 +144,19 @@ public class GameView implements Serializable {
stack.put(stackObject.getId(), new CardView(stackObject, game));
checkPaid(stackObject.getId(), ((StackAbility) stackObject));
} else {
LOGGER.fatal("Object can't be cast to StackAbility: " + object.getName() + ' ' + object.toString() + ' ' + object.getClass().toString());
throw new IllegalArgumentException("Object can't be cast to StackAbility: " + object + " - " + object.getClass().toString());
}
} else {
// can happen if a player times out while ability is on the stack
LOGGER.debug("Stack Object for stack ability not found: " + stackObject.getStackAbility().getRule());
}
} else if (stackObject != null) {
LOGGER.fatal("Unknown type of StackObject: " + stackObject.getName() + ' ' + stackObject.toString() + ' ' + stackObject.getClass().toString());
throw new IllegalArgumentException("Unknown type of StackObject: " + stackObject + " - " + stackObject.getClass().toString());
}
//stackOrder.add(stackObject.getId());
}
//Collections.reverse(stackOrder);
for (ExileZone exileZone : state.getExile().getExileZones()) {
exiles.add(new ExileView(exileZone, game));
exiles.add(new ExileView(exileZone, game, createdForPlayerId));
}
for (String name : state.getRevealed().keySet()) {
revealed.add(new RevealedView(name, state.getRevealed().get(name), game));

View file

@ -1,17 +1,15 @@
package mage.view;
import mage.abilities.Ability;
import mage.abilities.common.TurnFaceUpAbility;
import mage.cards.Card;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author BetaSteward_at_googlemail.com
@ -39,9 +37,8 @@ public class PermanentView extends CardView {
private final boolean attachedControllerDiffers;
public PermanentView(Permanent permanent, Card card, UUID createdForPlayerId, Game game) {
super(permanent, game, permanent.getControllerId() != null && permanent.getControllerId().equals(createdForPlayerId));
super(permanent, game, CardUtil.canShowAsControlled(permanent, createdForPlayerId));
this.controlled = permanent.getControllerId() != null && permanent.getControllerId().equals(createdForPlayerId);
this.rules = permanent.getRules(game);
this.tapped = permanent.isTapped();
this.flipped = permanent.isFlipped();
this.phasedIn = permanent.isPhasedIn();
@ -52,26 +49,26 @@ public class PermanentView extends CardView {
this.attachments = new ArrayList<>(permanent.getAttachments());
this.attachedTo = permanent.getAttachedTo();
// show face down cards to all players at the game end
boolean showFaceDownInfo = controlled || (game != null && game.hasEnded());
// store original card, e.g. for sides switch in GUI
if (isToken()) {
original = new CardView(((PermanentToken) permanent).getToken().copy(), (Game) null);
original.expansionSetCode = permanent.getExpansionSetCode();
original.expansionSetCode = permanent.getExpansionSetCode(); // TODO: miss card number and other?
expansionSetCode = permanent.getExpansionSetCode();
} else {
// face down card must be hidden from opponent, but shown on game end for all
boolean showFaceDownInfo = controlled || (game != null && game.hasEnded());
if (card != null && showFaceDownInfo) {
// face down card must be hidden from opponent, but shown on game end for all
original = new CardView(card.copy(), (Game) null);
} else {
original = null;
}
}
this.transformed = permanent.isTransformed();
//this.transformed = permanent.isTransformed();
this.copy = permanent.isCopy();
// for fipped, transformed or copied cards, switch the names
if (original != null && !original.getName().equals(this.getName())) {
// TODO: wtf, why copy check here?! Need research
if (permanent.isCopy() && permanent.isFlipCard()) {
this.alternateName = permanent.getFlipCardName();
} else {
@ -98,31 +95,16 @@ public class PermanentView extends CardView {
}
this.nameController = nameController;
// add info for face down permanents
if (permanent.isFaceDown(game) && card != null) {
if (showFaceDownInfo) {
// must be a morphed or manifested card
for (Ability permanentAbility : permanent.getAbilities(game)) {
if (permanentAbility.getWorksFaceDown()) {
this.rules.add(permanentAbility.getRule(true));
} else if (permanentAbility instanceof TurnFaceUpAbility && !permanentAbility.getRuleVisible()) {
this.rules.add(permanentAbility.getRule());
}
}
this.name = card.getName();
this.displayName = card.getName();
this.expansionSetCode = card.getExpansionSetCode();
this.cardNumber = card.getCardNumber();
} else {
if (permanent.isManifested()) {
this.rules.add("A manifested creature card can be turned face up any time for it's mana cost."
+ " A face-down card can also be turned face up for its morph cost.");
} else if (permanent.isMorphed()) {
this.rules.add("If the controller has priority, they may turn this permanent face up."
+ " This is a special action; it doesn't use the stack. To do this they pay the morph costs,"
+ " then turns this permanent face up.");
}
}
// add additional info for face down permanents
if (permanent.isFaceDown(game)) {
//if (permanent.isManifested()) {
// this.rules.add("A manifested creature card can be turned face up any time for it's mana cost."
// + " A face-down card can also be turned face up for its morph cost.");
//} else if (permanent.isMorphed()) {
// this.rules.add("If the controller has priority, they may turn this permanent face up."
// + " This is a special action; it doesn't use the stack. To do this they pay the morph costs,"
// + " then turns this permanent face up.");
//}
}
// determines if shown in it's own column

View file

@ -1,6 +1,5 @@
package mage.view;
import mage.cards.Card;
import mage.game.command.Plane;
import mage.players.PlayableObjectStats;
@ -15,15 +14,17 @@ public class PlaneView implements CommandObjectView, Serializable {
protected UUID id;
protected String name;
protected int imageNum;
protected String expansionSetCode;
protected String imageFileName = "";
protected int imageNumber = 0;
protected String expansionSetCode = "";
protected List<String> rules;
protected PlayableObjectStats playableStats = new PlayableObjectStats();
public PlaneView(Plane plane) {
this.id = plane.getId();
this.name = plane.getName();
this.imageNum = plane.getImageNumber();
this.imageFileName = plane.getImageFileName();
this.imageNumber = plane.getImageNumber();
this.expansionSetCode = plane.getExpansionSetCode();
this.rules = plane.getAbilities().getRules(plane.getName());
}
@ -43,9 +44,14 @@ public class PlaneView implements CommandObjectView, Serializable {
return id;
}
@Override
public String getImageFileName() {
return imageFileName;
}
@Override
public int getImageNumber() {
return imageNum;
return imageNumber;
}
@Override

View file

@ -10,6 +10,7 @@ import mage.game.command.*;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.players.net.UserData;
import mage.util.CardUtil;
import java.io.Serializable;
import java.util.*;
@ -82,19 +83,19 @@ public class PlayerView implements Serializable {
this.hasLeft = player.hasLeft();
for (Card card : player.getGraveyard().getCards(game)) {
graveyard.put(card.getId(), new CardView(card, game, false));
graveyard.put(card.getId(), new CardView(card, game, CardUtil.canShowAsControlled(card, createdForPlayerId)));
}
for (ExileZone exileZone : game.getExile().getExileZones()) {
for (Card card : exileZone.getCards(game)) {
if (player.getId().equals(card.getOwnerId())) {
exile.put(card.getId(), new CardView(card, game, false)); // unnown if it's allowed to look under a face down card
exile.put(card.getId(), new CardView(card, game, CardUtil.canShowAsControlled(card, createdForPlayerId)));
}
}
}
if (this.controlled || !player.isHuman()) {
// sideboard available for itself or for computer only
for (Card card : player.getSideboard().getCards(game)) {
sideboard.put(card.getId(), new CardView(card, game, false));
sideboard.put(card.getId(), new CardView(card, game, CardUtil.canShowAsControlled(card, createdForPlayerId)));
}
}
@ -137,7 +138,7 @@ public class PlayerView implements Serializable {
if (commander.getControllerId().equals(this.playerId)) {
Card sourceCard = game.getCard(commander.getSourceId());
if (sourceCard != null) {
commandList.add(new CommanderView(commander, sourceCard, game));
commandList.add(new CommanderView(commander, sourceCard, game, createdForPlayerId));
}
}
}

View file

@ -19,7 +19,7 @@ public class RevealedView implements Serializable {
public RevealedView(String name, Cards cards, Game game) {
this.name = name;
for (Card card : cards.getCards(game)) {
this.cards.put(card.getId(), new CardView(card, game, card.getId()));
this.cards.put(card.getId(), new CardView(card, game));
}
}