forked from External/mage
1186 lines
41 KiB
Java
1186 lines
41 KiB
Java
package mage.view;
|
|
|
|
import com.google.gson.annotations.Expose;
|
|
import mage.MageObject;
|
|
import mage.ObjectColor;
|
|
import mage.abilities.Abilities;
|
|
import mage.abilities.Ability;
|
|
import mage.abilities.Mode;
|
|
import mage.abilities.SpellAbility;
|
|
import mage.abilities.effects.Effect;
|
|
import mage.abilities.effects.Effects;
|
|
import mage.abilities.icon.CardIcon;
|
|
import mage.abilities.icon.other.FaceDownStatusIcon;
|
|
import mage.abilities.keyword.AftermathAbility;
|
|
import mage.cards.*;
|
|
import mage.cards.mock.MockCard;
|
|
import mage.cards.repository.CardInfo;
|
|
import mage.constants.*;
|
|
import mage.counters.Counter;
|
|
import mage.counters.CounterType;
|
|
import mage.designations.Designation;
|
|
import mage.filter.FilterMana;
|
|
import mage.game.Game;
|
|
import mage.game.command.Dungeon;
|
|
import mage.game.command.Emblem;
|
|
import mage.game.command.Plane;
|
|
import mage.game.permanent.Permanent;
|
|
import mage.game.permanent.PermanentToken;
|
|
import mage.game.permanent.token.Token;
|
|
import mage.game.stack.Spell;
|
|
import mage.game.stack.StackAbility;
|
|
import mage.target.Target;
|
|
import mage.target.Targets;
|
|
import mage.util.CardUtil;
|
|
import mage.util.SubTypes;
|
|
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
|
|
/**
|
|
* @author BetaSteward_at_googlemail.com
|
|
*/
|
|
public class CardView extends SimpleCardView {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
protected UUID parentId;
|
|
@Expose
|
|
protected String name;
|
|
@Expose
|
|
protected String displayName;
|
|
@Expose
|
|
protected String displayFullName;
|
|
@Expose
|
|
protected List<String> rules;
|
|
@Expose
|
|
protected String power;
|
|
@Expose
|
|
protected String toughness;
|
|
@Expose
|
|
protected String loyalty = "";
|
|
protected String startingLoyalty;
|
|
protected List<CardType> cardTypes;
|
|
protected SubTypes subTypes;
|
|
protected Set<SuperType> superTypes;
|
|
protected ObjectColor color;
|
|
protected ObjectColor frameColor;
|
|
protected FrameStyle frameStyle;
|
|
// can combine multiple costs for MockCard from deck editor or db (left/right, card/adventure)
|
|
protected String manaCostLeftStr;
|
|
protected String manaCostRightStr;
|
|
protected int manaValue;
|
|
protected Rarity rarity;
|
|
|
|
protected MageObjectType mageObjectType = MageObjectType.NULL;
|
|
|
|
protected boolean isAbility;
|
|
protected AbilityType abilityType;
|
|
protected boolean isToken;
|
|
|
|
protected CardView ability;
|
|
protected int type;
|
|
|
|
protected boolean transformable; // can toggle one card side to another (transformable cards, modal double faces)
|
|
protected CardView secondCardFace;
|
|
protected boolean transformed;
|
|
|
|
protected boolean flipCard;
|
|
protected boolean faceDown;
|
|
|
|
protected String alternateName;
|
|
protected String originalName;
|
|
|
|
protected boolean isSplitCard;
|
|
protected String leftSplitName;
|
|
protected String leftSplitCostsStr;
|
|
protected List<String> leftSplitRules;
|
|
protected String leftSplitTypeLine;
|
|
protected String rightSplitName;
|
|
protected String rightSplitCostsStr;
|
|
protected List<String> rightSplitRules;
|
|
protected String rightSplitTypeLine;
|
|
|
|
protected boolean isModalDoubleFacesCard;
|
|
|
|
protected ArtRect artRect = ArtRect.NORMAL;
|
|
|
|
protected List<UUID> targets;
|
|
protected UUID pairedCard;
|
|
protected List<UUID> bandedCards;
|
|
protected boolean paid;
|
|
protected List<CounterView> counters;
|
|
|
|
protected boolean controlledByOwner = true;
|
|
|
|
protected Zone zone;
|
|
|
|
protected boolean rotate;
|
|
protected boolean hideInfo; // controls if the tooltip window is shown (eg. controlled face down morph card)
|
|
|
|
protected boolean canAttack;
|
|
protected boolean canBlock;
|
|
protected boolean inViewerOnly;
|
|
protected List<CardIcon> cardIcons = new ArrayList<>(); // additional icons to render
|
|
|
|
protected Card originalCard = null;
|
|
|
|
/**
|
|
* Non game usage like deck editor
|
|
*
|
|
* @param card
|
|
*/
|
|
public CardView(Card card) {
|
|
this(card, (Game) null);
|
|
}
|
|
|
|
public CardView(Card card, Game game) {
|
|
this(card, game, false);
|
|
}
|
|
|
|
public CardView(Card card, SimpleCardView simpleCardView) {
|
|
this(card, null, false);
|
|
this.id = simpleCardView.getId();
|
|
|
|
this.playableStats = simpleCardView.playableStats.copy();
|
|
this.isChoosable = simpleCardView.isChoosable;
|
|
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);
|
|
|
|
// generetate new ID (TODO: why new ID?)
|
|
this.id = UUID.randomUUID();
|
|
this.parentId = cardView.parentId;
|
|
|
|
this.name = cardView.name;
|
|
this.displayName = cardView.displayName;
|
|
this.displayFullName = cardView.displayFullName;
|
|
this.rules = new ArrayList<>(cardView.rules);
|
|
|
|
this.power = cardView.power;
|
|
this.toughness = cardView.toughness;
|
|
this.loyalty = cardView.loyalty;
|
|
this.startingLoyalty = cardView.startingLoyalty;
|
|
this.cardTypes = new ArrayList<>(cardView.cardTypes);
|
|
this.subTypes = new SubTypes(cardView.subTypes);
|
|
this.superTypes = cardView.superTypes;
|
|
|
|
this.color = cardView.color;
|
|
this.frameColor = cardView.frameColor;
|
|
this.frameStyle = cardView.frameStyle;
|
|
this.manaCostLeftStr = cardView.manaCostLeftStr;
|
|
this.manaCostRightStr = cardView.manaCostRightStr;
|
|
this.manaValue = cardView.manaValue;
|
|
this.rarity = cardView.rarity;
|
|
|
|
this.mageObjectType = cardView.mageObjectType;
|
|
this.isAbility = cardView.isAbility;
|
|
this.abilityType = cardView.abilityType;
|
|
this.isToken = cardView.isToken;
|
|
this.ability = cardView.ability; // reference, not copy
|
|
this.type = cardView.type;
|
|
|
|
this.transformable = cardView.transformable;
|
|
this.secondCardFace = cardView.secondCardFace == null ? null : new CardView(cardView.secondCardFace);
|
|
this.transformed = cardView.transformed;
|
|
this.flipCard = cardView.flipCard;
|
|
this.faceDown = cardView.faceDown;
|
|
this.alternateName = cardView.alternateName;
|
|
this.originalName = cardView.originalName;
|
|
|
|
this.isSplitCard = cardView.isSplitCard;
|
|
this.leftSplitName = cardView.leftSplitName;
|
|
this.leftSplitCostsStr = cardView.leftSplitCostsStr;
|
|
this.leftSplitRules = cardView.leftSplitRules == null ? null : new ArrayList<>(cardView.leftSplitRules);
|
|
this.leftSplitTypeLine = cardView.leftSplitTypeLine;
|
|
this.rightSplitName = cardView.rightSplitName;
|
|
this.rightSplitCostsStr = cardView.rightSplitCostsStr;
|
|
this.rightSplitRules = cardView.rightSplitRules == null ? null : new ArrayList<>(cardView.rightSplitRules);
|
|
this.rightSplitTypeLine = cardView.rightSplitTypeLine;
|
|
|
|
this.isModalDoubleFacesCard = cardView.isModalDoubleFacesCard;
|
|
|
|
this.artRect = cardView.artRect;
|
|
this.targets = cardView.targets == null ? null : new ArrayList<>(cardView.targets);
|
|
this.pairedCard = cardView.pairedCard;
|
|
this.bandedCards = cardView.bandedCards == null ? null : new ArrayList<>(cardView.bandedCards);
|
|
this.paid = cardView.paid;
|
|
if (cardView.counters != null) {
|
|
this.counters = new ArrayList<>();
|
|
cardView.counters.forEach(c -> this.counters.add(new CounterView(c)));
|
|
}
|
|
|
|
this.controlledByOwner = cardView.controlledByOwner;
|
|
this.zone = cardView.zone;
|
|
this.rotate = cardView.rotate;
|
|
this.hideInfo = cardView.hideInfo;
|
|
|
|
this.canAttack = cardView.canAttack;
|
|
this.canBlock = cardView.canBlock;
|
|
this.inViewerOnly = cardView.inViewerOnly;
|
|
this.originalCard = cardView.originalCard == null ? null : cardView.originalCard.copy();
|
|
if (cardView.cardIcons != null) {
|
|
cardView.cardIcons.forEach(icon -> this.cardIcons.add(icon.copy()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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()) {
|
|
sbType.append(superType).append(' ');
|
|
}
|
|
for (CardType cardType : card.getCardType(game)) {
|
|
sbType.append(cardType.toString()).append(' ');
|
|
}
|
|
if (!card.getSubtype(game).isEmpty()) {
|
|
sbType.append("- ");
|
|
for (SubType subType : card.getSubtype(game)) {
|
|
sbType.append(subType).append(' ');
|
|
}
|
|
}
|
|
return sbType.toString();
|
|
}
|
|
|
|
/**
|
|
* @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
|
|
* @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(), card.getTokenSetCode(), game != null, card.getTokenDescriptor());
|
|
this.originalCard = 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) {
|
|
this.zone = cardZone;
|
|
}
|
|
}
|
|
// 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) {
|
|
// 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();
|
|
}
|
|
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 = card.getCardType(game);
|
|
this.faceDown = card.isFaceDown(game);
|
|
} else {
|
|
// this.hideInfo = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
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 = String.join("", splitCard.getLeftHalfCard().getManaCostSymbols());
|
|
this.manaCostRightStr = String.join("", splitCard.getRightHalfCard().getManaCostSymbols());
|
|
} else if (card instanceof ModalDoubleFacesCard) {
|
|
this.isModalDoubleFacesCard = true;
|
|
ModalDoubleFacesCard mainCard = ((ModalDoubleFacesCard) card);
|
|
fullCardName = mainCard.getLeftHalfCard().getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + mainCard.getRightHalfCard().getName();
|
|
this.manaCostLeftStr = String.join("", mainCard.getLeftHalfCard().getManaCostSymbols());
|
|
this.manaCostRightStr = String.join("", mainCard.getRightHalfCard().getManaCostSymbols());
|
|
} else if (card instanceof AdventureCard) {
|
|
AdventureCard adventureCard = ((AdventureCard) card);
|
|
AdventureCardSpell adventureCardSpell = ((AdventureCardSpell) adventureCard.getSpellCard());
|
|
fullCardName = adventureCard.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + adventureCardSpell.getName();
|
|
this.manaCostLeftStr = String.join("", adventureCardSpell.getManaCostSymbols());
|
|
this.manaCostRightStr = String.join("", adventureCard.getManaCostSymbols());
|
|
} else if (card instanceof MockCard) {
|
|
// deck editor cards
|
|
fullCardName = ((MockCard) card).getFullName(true);
|
|
this.manaCostLeftStr = String.join("", ((MockCard) card).getManaCostStr(CardInfo.ManaCostSide.LEFT));
|
|
this.manaCostRightStr = String.join("", ((MockCard) card).getManaCostStr(CardInfo.ManaCostSide.RIGHT));
|
|
} else {
|
|
fullCardName = card.getName();
|
|
this.manaCostLeftStr = String.join("", card.getManaCostSymbols());
|
|
this.manaCostRightStr = "";
|
|
}
|
|
|
|
this.name = card.getImageName();
|
|
this.displayName = card.getName();
|
|
this.displayFullName = fullCardName;
|
|
if (game == null) {
|
|
this.rules = card.getRules();
|
|
} else {
|
|
this.rules = card.getRules(game);
|
|
}
|
|
this.manaValue = card.getManaValue();
|
|
|
|
if (card instanceof Permanent) {
|
|
this.mageObjectType = MageObjectType.PERMANENT;
|
|
Permanent permanent = (Permanent) card;
|
|
if (game != null) {
|
|
if (permanent.getCounters(game) != null && !permanent.getCounters(game).isEmpty()) {
|
|
this.loyalty = Integer.toString(permanent.getCounters(game).getCount(CounterType.LOYALTY));
|
|
counters = new ArrayList<>();
|
|
for (Counter counter : permanent.getCounters(game).values()) {
|
|
counters.add(new CounterView(counter));
|
|
}
|
|
}
|
|
this.pairedCard = permanent.getPairedCard() != null ? permanent.getPairedCard().getSourceId() : null;
|
|
this.bandedCards = new ArrayList<>();
|
|
for (UUID bandedCard : permanent.getBandedCards()) {
|
|
bandedCards.add(bandedCard);
|
|
}
|
|
if (!permanent.getControllerId().equals(permanent.getOwnerId())) {
|
|
controlledByOwner = false;
|
|
}
|
|
}
|
|
|
|
// card icons for permanents on battlefield
|
|
// abilities
|
|
permanent.getAbilities(game).forEach(ability -> {
|
|
this.cardIcons.addAll(ability.getIcons(game));
|
|
});
|
|
// other
|
|
if (permanent.isFaceDown(game)) {
|
|
this.cardIcons.add(FaceDownStatusIcon.instance);
|
|
}
|
|
|
|
} else {
|
|
if (card.isCopy()) {
|
|
this.mageObjectType = MageObjectType.COPY_CARD;
|
|
} else {
|
|
this.mageObjectType = MageObjectType.CARD;
|
|
}
|
|
this.loyalty = "";
|
|
if (game != null && card.getCounters(game) != null && !card.getCounters(game).isEmpty()) {
|
|
counters = new ArrayList<>();
|
|
for (Counter counter : card.getCounters(game).values()) {
|
|
counters.add(new CounterView(counter));
|
|
}
|
|
}
|
|
}
|
|
this.power = Integer.toString(card.getPower().getValue());
|
|
this.toughness = Integer.toString(card.getToughness().getValue());
|
|
this.cardTypes = card.getCardType(game);
|
|
this.subTypes = card.getSubtype(game);
|
|
this.superTypes = card.getSuperType();
|
|
this.color = card.getColor(game);
|
|
this.flipCard = card.isFlipCard();
|
|
this.faceDown = !showFaceUp;
|
|
|
|
if (card instanceof PermanentToken) {
|
|
this.isToken = true;
|
|
this.mageObjectType = MageObjectType.TOKEN;
|
|
this.rarity = Rarity.COMMON;
|
|
boolean originalCardNumberIsNull = ((PermanentToken) card).getToken().getOriginalCardNumber() == null;
|
|
if (!originalCardNumberIsNull && !"0".equals(((PermanentToken) card).getToken().getOriginalCardNumber())) {
|
|
// a token copied from permanent
|
|
this.expansionSetCode = ((PermanentToken) card).getToken().getOriginalExpansionSetCode();
|
|
this.cardNumber = ((PermanentToken) card).getToken().getOriginalCardNumber();
|
|
} else {
|
|
// a created token
|
|
this.expansionSetCode = card.getExpansionSetCode();
|
|
this.tokenDescriptor = card.getTokenDescriptor();
|
|
}
|
|
//
|
|
// set code and card number for token copies to get the image
|
|
this.rules = card.getRules(game);
|
|
this.type = ((PermanentToken) card).getToken().getTokenType();
|
|
} else {
|
|
this.rarity = card.getRarity();
|
|
this.isToken = false;
|
|
}
|
|
|
|
// 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.originalName = card.getName();
|
|
}
|
|
|
|
this.flipCard = card.isFlipCard();
|
|
if (card.isFlipCard() && card.getFlipCardName() != null) {
|
|
this.alternateName = card.getFlipCardName();
|
|
this.originalName = card.getName();
|
|
}
|
|
|
|
if (card instanceof ModalDoubleFacesCard) {
|
|
this.transformable = true; // enable GUI day/night button
|
|
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card;
|
|
this.secondCardFace = new CardView(mdfCard.getRightHalfCard(), game);
|
|
this.alternateName = mdfCard.getRightHalfCard().getName();
|
|
this.originalName = card.getName();
|
|
}
|
|
|
|
if (card instanceof Spell) {
|
|
this.mageObjectType = MageObjectType.SPELL;
|
|
Spell spell = (Spell) card;
|
|
for (SpellAbility spellAbility : spell.getSpellAbilities()) {
|
|
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
|
|
Mode mode = spellAbility.getModes().get(modeId);
|
|
if (!mode.getTargets().isEmpty()) {
|
|
addTargets(mode.getTargets(), mode.getEffects(), spellAbility, game);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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(halfAbility -> halfAbility instanceof AftermathAbility)) {
|
|
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 choosen
|
|
if (spell.getSpellAbility().isModal()) {
|
|
for (UUID modeId : spell.getSpellAbility().getModes().getSelectedModes()) {
|
|
Mode mode = spell.getSpellAbility().getModes().get(modeId);
|
|
this.rules.add("<span color='green'><i>Chosen mode: " + mode.getEffects().getText(mode) + "</i></span>");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Frame color
|
|
this.frameColor = card.getFrameColor(game);
|
|
|
|
// Frame style
|
|
this.frameStyle = card.getFrameStyle();
|
|
|
|
// Get starting loyalty
|
|
this.startingLoyalty = "" + card.getStartingLoyalty();
|
|
}
|
|
|
|
public CardView(MageObject object, Game game) {
|
|
super(object.getId(), "", "0", false, "", true, "");
|
|
this.originalCard = null;
|
|
|
|
this.name = object.getName();
|
|
this.displayName = object.getName();
|
|
this.displayFullName = object.getName();
|
|
if (object instanceof Permanent) {
|
|
this.mageObjectType = MageObjectType.PERMANENT;
|
|
this.power = Integer.toString(object.getPower().getValue());
|
|
this.toughness = Integer.toString(object.getToughness().getValue());
|
|
this.loyalty = Integer.toString(((Permanent) object).getCounters((Game) null).getCount(CounterType.LOYALTY));
|
|
} else {
|
|
this.power = object.getPower().toString();
|
|
this.toughness = object.getToughness().toString();
|
|
this.loyalty = "";
|
|
}
|
|
this.cardTypes = object.getCardType(game);
|
|
this.subTypes = object.getSubtype(game);
|
|
this.superTypes = object.getSuperType();
|
|
this.color = object.getColor(game);
|
|
this.manaCostLeftStr = String.join("", object.getManaCostSymbols());
|
|
this.manaCostRightStr = "";
|
|
this.manaValue = object.getManaCost().manaValue();
|
|
if (object instanceof PermanentToken) {
|
|
this.mageObjectType = MageObjectType.TOKEN;
|
|
PermanentToken permanentToken = (PermanentToken) object;
|
|
this.rarity = Rarity.COMMON;
|
|
this.expansionSetCode = permanentToken.getExpansionSetCode();
|
|
this.rules = permanentToken.getRules();
|
|
this.type = permanentToken.getToken().getTokenType();
|
|
} else if (object instanceof Emblem) {
|
|
this.mageObjectType = MageObjectType.EMBLEM;
|
|
Emblem emblem = (Emblem) object;
|
|
this.rarity = Rarity.SPECIAL;
|
|
this.rules = emblem.getAbilities().getRules(emblem.getName());
|
|
} else if (object instanceof Dungeon) {
|
|
this.mageObjectType = MageObjectType.DUNGEON;
|
|
Dungeon dungeon = (Dungeon) object;
|
|
this.rarity = Rarity.SPECIAL;
|
|
this.rules = dungeon.getRules();
|
|
} else if (object instanceof Plane) {
|
|
this.mageObjectType = MageObjectType.PLANE;
|
|
Plane plane = (Plane) object;
|
|
this.rarity = Rarity.SPECIAL;
|
|
this.frameStyle = FrameStyle.M15_NORMAL;
|
|
// Display in landscape/rotated/on its side
|
|
this.rotate = true;
|
|
this.rules = plane.getAbilities().getRules(plane.getName());
|
|
} else if (object instanceof Designation) {
|
|
this.mageObjectType = MageObjectType.DESIGNATION;
|
|
Designation designation = (Designation) object;
|
|
this.rarity = Rarity.SPECIAL;
|
|
this.frameStyle = FrameStyle.M15_NORMAL;
|
|
// Display in landscape/rotated/on its side
|
|
this.rules = designation.getAbilities().getRules(designation.getName());
|
|
}
|
|
if (this.rarity == null && object instanceof StackAbility) {
|
|
StackAbility stackAbility = (StackAbility) object;
|
|
this.rarity = Rarity.SPECIAL;
|
|
this.rules = new ArrayList<>();
|
|
this.rules.add(stackAbility.getRule());
|
|
if (stackAbility.getZone() == Zone.COMMAND) {
|
|
this.expansionSetCode = stackAbility.getExpansionSetCode();
|
|
}
|
|
}
|
|
// Frame color
|
|
this.frameColor = object.getFrameColor(game);
|
|
// Frame style
|
|
this.frameStyle = object.getFrameStyle();
|
|
// Starting loyalty. Must be extracted from an ability
|
|
this.startingLoyalty = "" + object.getStartingLoyalty();
|
|
}
|
|
|
|
protected CardView() {
|
|
super(null, "", "0", false, "", true, "");
|
|
}
|
|
|
|
public CardView(EmblemView emblem) {
|
|
this(true);
|
|
this.gameObject = true;
|
|
this.id = emblem.getId();
|
|
this.mageObjectType = MageObjectType.EMBLEM;
|
|
this.name = emblem.getName();
|
|
this.displayName = name;
|
|
this.displayFullName = name;
|
|
this.rules = emblem.getRules();
|
|
// emblem images are always with common (black) symbol
|
|
this.frameStyle = FrameStyle.M15_NORMAL;
|
|
this.expansionSetCode = emblem.getExpansionSetCode();
|
|
this.rarity = Rarity.COMMON;
|
|
}
|
|
|
|
public CardView(DungeonView dungeon) {
|
|
this(true);
|
|
this.gameObject = true;
|
|
this.id = dungeon.getId();
|
|
this.mageObjectType = MageObjectType.DUNGEON;
|
|
this.name = dungeon.getName();
|
|
this.displayName = name;
|
|
this.displayFullName = name;
|
|
this.rules = dungeon.getRules();
|
|
// emblem images are always with common (black) symbol
|
|
this.frameStyle = FrameStyle.M15_NORMAL;
|
|
this.expansionSetCode = dungeon.getExpansionSetCode();
|
|
this.rarity = Rarity.COMMON;
|
|
}
|
|
|
|
public CardView(PlaneView plane) {
|
|
this(true);
|
|
this.gameObject = true;
|
|
this.id = plane.getId();
|
|
this.mageObjectType = MageObjectType.PLANE;
|
|
this.name = plane.getName();
|
|
this.displayName = name;
|
|
this.displayFullName = name;
|
|
this.rules = plane.getRules();
|
|
// Display the plane in landscape (similar to Fused cards)
|
|
this.rotate = true;
|
|
this.frameStyle = FrameStyle.M15_NORMAL;
|
|
this.expansionSetCode = plane.getExpansionSetCode();
|
|
this.rarity = Rarity.COMMON;
|
|
}
|
|
|
|
public CardView(Designation designation, StackAbility stackAbility) {
|
|
this(true);
|
|
this.gameObject = true;
|
|
this.id = designation.getId();
|
|
this.mageObjectType = MageObjectType.NULL;
|
|
this.name = designation.getName();
|
|
this.displayName = name;
|
|
this.displayFullName = name;
|
|
this.rules = new ArrayList<>();
|
|
this.rules.add(stackAbility.getRule(designation.getName()));
|
|
this.frameStyle = FrameStyle.M15_NORMAL;
|
|
this.expansionSetCode = designation.getExpansionSetCodeForImage();
|
|
this.rarity = Rarity.COMMON;
|
|
}
|
|
|
|
public CardView(boolean empty) {
|
|
super(null, "", "0", false, "", "");
|
|
if (!empty) {
|
|
throw new IllegalArgumentException("Not supported.");
|
|
}
|
|
fillEmpty(null, false);
|
|
}
|
|
|
|
private void fillEmpty(Card card, boolean controlled) {
|
|
this.name = "Face Down";
|
|
this.displayName = name;
|
|
this.displayFullName = name;
|
|
this.rules = new ArrayList<>();
|
|
this.power = "";
|
|
this.toughness = "";
|
|
this.loyalty = "";
|
|
this.startingLoyalty = "";
|
|
this.cardTypes = new ArrayList<>();
|
|
this.subTypes = new SubTypes();
|
|
this.superTypes = EnumSet.noneOf(SuperType.class);
|
|
this.color = new ObjectColor();
|
|
this.frameColor = new ObjectColor();
|
|
this.frameStyle = FrameStyle.M15_NORMAL;
|
|
this.manaCostLeftStr = "";
|
|
this.manaCostRightStr = "";
|
|
this.manaValue = 0;
|
|
|
|
// 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";
|
|
} else {
|
|
this.rarity = card.getRarity();
|
|
}
|
|
|
|
if (card != null) {
|
|
if (card instanceof Permanent) {
|
|
this.mageObjectType = MageObjectType.PERMANENT;
|
|
} else if (card.isCopy()) {
|
|
this.mageObjectType = MageObjectType.COPY_CARD;
|
|
} else {
|
|
this.mageObjectType = MageObjectType.CARD;
|
|
}
|
|
if (card instanceof PermanentToken) {
|
|
this.mageObjectType = MageObjectType.TOKEN;
|
|
}
|
|
if (card instanceof Spell) {
|
|
this.mageObjectType = MageObjectType.SPELL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
CardView(Token token, Game game) {
|
|
super(token.getId(), "", "0", false, "", "");
|
|
this.isToken = true;
|
|
this.id = token.getId();
|
|
this.name = token.getName();
|
|
this.displayName = token.getName();
|
|
this.displayFullName = token.getName();
|
|
this.rules = token.getAbilities().getRules(this.name);
|
|
this.power = token.getPower().toString();
|
|
this.toughness = token.getToughness().toString();
|
|
this.loyalty = "";
|
|
this.startingLoyalty = "";
|
|
this.cardTypes = token.getCardType(game);
|
|
this.subTypes = token.getSubtype(game);
|
|
this.superTypes = token.getSuperType();
|
|
this.color = token.getColor(game);
|
|
this.frameColor = token.getFrameColor(game);
|
|
this.frameStyle = token.getFrameStyle();
|
|
this.manaCostLeftStr = String.join("", token.getManaCostSymbols());
|
|
this.manaCostRightStr = "";
|
|
this.rarity = Rarity.SPECIAL;
|
|
this.type = token.getTokenType();
|
|
this.tokenDescriptor = token.getTokenDescriptor();
|
|
this.tokenSetCode = token.getOriginalExpansionSetCode();
|
|
}
|
|
|
|
protected final void addTargets(Targets targets, Effects effects, Ability source, Game game) {
|
|
if (this.targets == null) {
|
|
this.targets = new ArrayList<>();
|
|
}
|
|
|
|
// need only unique targets for arrow drawning
|
|
Set<UUID> newTargets = new HashSet<>();
|
|
|
|
// from normal targets
|
|
for (Target target : targets) {
|
|
if (target.isChosen()) {
|
|
newTargets.addAll(target.getTargets());
|
|
}
|
|
}
|
|
|
|
// from targetPointers (can be same as normal targets)
|
|
List<UUID> fromPointers = effects.stream()
|
|
.map(Effect::getTargetPointer)
|
|
.filter(Objects::nonNull)
|
|
.map(p -> p.getTargets(game, source))
|
|
.flatMap(Collection::stream)
|
|
.collect(Collectors.toList());
|
|
newTargets.addAll(fromPointers);
|
|
|
|
this.targets.addAll(newTargets);
|
|
}
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
public String getDisplayName() {
|
|
return displayName;
|
|
}
|
|
|
|
public String getDisplayFullName() {
|
|
return displayFullName;
|
|
}
|
|
|
|
public List<String> getRules() {
|
|
return rules;
|
|
}
|
|
|
|
public void overrideRules(List<String> rules) {
|
|
this.rules = rules;
|
|
}
|
|
|
|
public void setIsAbility(boolean isAbility) {
|
|
this.isAbility = isAbility;
|
|
}
|
|
|
|
public boolean isAbility() {
|
|
return isAbility;
|
|
}
|
|
|
|
public AbilityType getAbilityType() {
|
|
return abilityType;
|
|
}
|
|
|
|
public void setAbilityType(AbilityType abilityType) {
|
|
this.abilityType = abilityType;
|
|
}
|
|
|
|
public String getPower() {
|
|
return power;
|
|
}
|
|
|
|
public String getToughness() {
|
|
return toughness;
|
|
}
|
|
|
|
public String getLoyalty() {
|
|
return loyalty;
|
|
}
|
|
|
|
public String getStartingLoyalty() {
|
|
return startingLoyalty;
|
|
}
|
|
|
|
public List<CardType> getCardTypes() {
|
|
return cardTypes;
|
|
}
|
|
|
|
public SubTypes getSubTypes() {
|
|
return subTypes;
|
|
}
|
|
|
|
public Set<SuperType> getSuperTypes() {
|
|
return superTypes;
|
|
}
|
|
|
|
public ObjectColor getColor() {
|
|
return color;
|
|
}
|
|
|
|
public ObjectColor getFrameColor() {
|
|
return frameColor;
|
|
}
|
|
|
|
public FrameStyle getFrameStyle() {
|
|
return frameStyle;
|
|
}
|
|
|
|
public String getManaCostStr() {
|
|
return CardUtil.concatManaSymbols(CardInfo.SPLIT_MANA_SEPARATOR_FULL, this.manaCostLeftStr, this.manaCostRightStr);
|
|
}
|
|
|
|
public int getManaValue() {
|
|
return manaValue;
|
|
}
|
|
|
|
public Rarity getRarity() {
|
|
return rarity;
|
|
}
|
|
|
|
public String getColorIdentityStr() {
|
|
FilterMana filterMana = originalCard.getColorIdentity();
|
|
if (filterMana.getColorCount() == 0) {
|
|
return CardUtil.concatManaSymbols(CardInfo.SPLIT_MANA_SEPARATOR_FULL, "{C}", "");
|
|
}
|
|
return CardUtil.concatManaSymbols(CardInfo.SPLIT_MANA_SEPARATOR_FULL, filterMana.toString(), "");
|
|
}
|
|
|
|
@Override
|
|
public String getExpansionSetCode() {
|
|
if (expansionSetCode == null) {
|
|
expansionSetCode = "";
|
|
}
|
|
return expansionSetCode;
|
|
}
|
|
|
|
public void setExpansionSetCode(String expansionSetCode) {
|
|
this.expansionSetCode = expansionSetCode;
|
|
}
|
|
|
|
@Override
|
|
public UUID getId() {
|
|
return id;
|
|
}
|
|
|
|
@Override
|
|
public String getCardNumber() {
|
|
return cardNumber;
|
|
}
|
|
|
|
/**
|
|
* Returns UUIDs for targets. Can be null if there is no target selected.
|
|
*
|
|
* @return
|
|
*/
|
|
public List<UUID> getTargets() {
|
|
return targets;
|
|
}
|
|
|
|
public void overrideTargets(List<UUID> newTargets) {
|
|
this.targets = newTargets;
|
|
}
|
|
|
|
public void overrideId(UUID id) {
|
|
if (parentId == null) {
|
|
parentId = this.id;
|
|
}
|
|
this.id = id;
|
|
}
|
|
|
|
public UUID getParentId() {
|
|
if (parentId != null) {
|
|
return parentId;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
public void setAbility(CardView ability) {
|
|
this.ability = ability;
|
|
}
|
|
|
|
public CardView getAbility() {
|
|
return this.ability;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return getName() + " [" + getId() + ']';
|
|
}
|
|
|
|
public boolean isFaceDown() {
|
|
return faceDown;
|
|
}
|
|
|
|
public boolean canTransform() {
|
|
return this.transformable;
|
|
}
|
|
|
|
public boolean isSplitCard() {
|
|
return this.isSplitCard;
|
|
}
|
|
|
|
/**
|
|
* Name of the other side (transform), flipped, modal double faces card or copying card name.
|
|
*
|
|
* @return name
|
|
*/
|
|
public String getAlternateName() {
|
|
return alternateName;
|
|
}
|
|
|
|
/**
|
|
* Stores the name of the original name, to provide it for a flipped or
|
|
* transformed or copying card
|
|
*
|
|
* @return
|
|
*/
|
|
public String getOriginalName() {
|
|
return originalName;
|
|
}
|
|
|
|
public void setAlternateName(String alternateName) {
|
|
this.alternateName = alternateName;
|
|
}
|
|
|
|
public void setOriginalName(String originalName) {
|
|
this.originalName = originalName;
|
|
}
|
|
|
|
public String getLeftSplitName() {
|
|
return leftSplitName;
|
|
}
|
|
|
|
public String getLeftSplitCostsStr() {
|
|
return leftSplitCostsStr;
|
|
}
|
|
|
|
public List<String> getLeftSplitRules() {
|
|
return leftSplitRules;
|
|
}
|
|
|
|
public String getLeftSplitTypeLine() {
|
|
return leftSplitTypeLine;
|
|
}
|
|
|
|
public String getRightSplitName() {
|
|
return rightSplitName;
|
|
}
|
|
|
|
public String getRightSplitCostsStr() {
|
|
return rightSplitCostsStr;
|
|
}
|
|
|
|
public List<String> getRightSplitRules() {
|
|
return rightSplitRules;
|
|
}
|
|
|
|
public String getRightSplitTypeLine() {
|
|
return rightSplitTypeLine;
|
|
}
|
|
|
|
public ArtRect getArtRect() {
|
|
return artRect;
|
|
}
|
|
|
|
public CardView getSecondCardFace() {
|
|
return this.secondCardFace;
|
|
}
|
|
|
|
public boolean isToken() {
|
|
return this.isToken;
|
|
}
|
|
|
|
public void setTransformed(boolean transformed) {
|
|
this.transformed = transformed;
|
|
}
|
|
|
|
public boolean isTransformed() {
|
|
return this.transformed;
|
|
}
|
|
|
|
public UUID getPairedCard() {
|
|
return pairedCard;
|
|
}
|
|
|
|
public List<UUID> getBandedCards() {
|
|
return bandedCards;
|
|
}
|
|
|
|
public int getType() {
|
|
return type;
|
|
}
|
|
|
|
public MageObjectType getMageObjectType() {
|
|
return mageObjectType;
|
|
}
|
|
|
|
public void setMageObjectType(MageObjectType mageObjectType) {
|
|
this.mageObjectType = mageObjectType;
|
|
}
|
|
|
|
public boolean isPaid() {
|
|
return paid;
|
|
}
|
|
|
|
public void setPaid(boolean paid) {
|
|
this.paid = paid;
|
|
}
|
|
|
|
public List<CounterView> getCounters() {
|
|
return counters;
|
|
}
|
|
|
|
public boolean isControlledByOwner() {
|
|
return controlledByOwner;
|
|
}
|
|
|
|
public Zone getZone() {
|
|
return zone;
|
|
}
|
|
|
|
public boolean isFlipCard() {
|
|
return flipCard;
|
|
}
|
|
|
|
public boolean isToRotate() {
|
|
return rotate;
|
|
}
|
|
|
|
public boolean hideInfo() {
|
|
return hideInfo;
|
|
}
|
|
|
|
public boolean isCanAttack() {
|
|
return canAttack;
|
|
}
|
|
|
|
public void setCanAttack(boolean canAttack) {
|
|
this.canAttack = canAttack;
|
|
}
|
|
|
|
public boolean isCanBlock() {
|
|
return canBlock;
|
|
}
|
|
|
|
public void setCanBlock(boolean canBlock) {
|
|
this.canBlock = canBlock;
|
|
}
|
|
|
|
public boolean isCreature() {
|
|
return cardTypes.contains(CardType.CREATURE);
|
|
}
|
|
|
|
public boolean isPlanesWalker() {
|
|
return cardTypes.contains(CardType.PLANESWALKER);
|
|
}
|
|
|
|
public String getColorText() {
|
|
String colorText = getColor().getDescription();
|
|
return colorText.substring(0, 1).toUpperCase(Locale.ENGLISH) + colorText.substring(1);
|
|
}
|
|
|
|
public String getTypeText() {
|
|
StringBuilder typeText = new StringBuilder();
|
|
if (!getSuperTypes().isEmpty()) {
|
|
typeText.append(String.join(" ", getSuperTypes().stream().map(SuperType::toString).collect(Collectors.toList())));
|
|
typeText.append(" ");
|
|
}
|
|
if (!getCardTypes().isEmpty()) {
|
|
typeText.append(String.join(" ", getCardTypes().stream().map(CardType::toString).collect(Collectors.toList())));
|
|
typeText.append(" ");
|
|
}
|
|
if (!getSubTypes().isEmpty()) {
|
|
typeText.append(" - ");
|
|
typeText.append(String.join(" ", getSubTypes().stream().map(SubType::toString).collect(Collectors.toList())));
|
|
}
|
|
return typeText.toString();
|
|
}
|
|
|
|
public boolean isLand() {
|
|
return cardTypes.contains(CardType.LAND);
|
|
}
|
|
|
|
public boolean isInstant() {
|
|
return cardTypes.contains(CardType.INSTANT);
|
|
}
|
|
|
|
public boolean isSorcery() {
|
|
return cardTypes.contains(CardType.SORCERY);
|
|
}
|
|
|
|
public boolean isEnchantment() {
|
|
return cardTypes.contains(CardType.ENCHANTMENT);
|
|
}
|
|
|
|
public boolean isArtifact() {
|
|
return cardTypes.contains(CardType.ARTIFACT);
|
|
}
|
|
|
|
public boolean isTribal() {
|
|
return cardTypes.contains(CardType.TRIBAL);
|
|
}
|
|
|
|
public void setInViewerOnly(boolean inViewerOnly) {
|
|
this.inViewerOnly = inViewerOnly;
|
|
}
|
|
|
|
public boolean inViewerOnly() {
|
|
return inViewerOnly;
|
|
}
|
|
|
|
public Card getOriginalCard() {
|
|
return this.originalCard;
|
|
}
|
|
|
|
public List<CardIcon> getCardIcons() {
|
|
return this.cardIcons;
|
|
}
|
|
}
|