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 rules; @Expose protected String power; @Expose protected String toughness; @Expose protected String loyalty = ""; protected String startingLoyalty; protected List cardTypes; protected SubTypes subTypes; protected Set 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 leftSplitRules; protected String leftSplitTypeLine; protected String rightSplitName; protected String rightSplitCostsStr; protected List rightSplitRules; protected String rightSplitTypeLine; protected boolean isModalDoubleFacesCard; protected ArtRect artRect = ArtRect.NORMAL; protected List targets; protected UUID pairedCard; protected List bandedCards; protected boolean paid; protected List 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 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 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("Chosen mode: " + mode.getEffects().getText(mode) + ""); } } } // 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 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 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 getRules() { return rules; } public void overrideRules(List 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 getCardTypes() { return cardTypes; } public SubTypes getSubTypes() { return subTypes; } public Set 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 getTargets() { return targets; } public void overrideTargets(List 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 getLeftSplitRules() { return leftSplitRules; } public String getLeftSplitTypeLine() { return leftSplitTypeLine; } public String getRightSplitName() { return rightSplitName; } public String getRightSplitCostsStr() { return rightSplitCostsStr; } public List 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 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 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 getCardIcons() { return this.cardIcons; } }