package mage.cards.repository; import com.j256.ormlite.field.DataType; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.cards.*; import mage.cards.mock.MockCard; import mage.cards.mock.MockSplitCard; import mage.constants.*; import mage.util.CardUtil; import mage.util.SubTypeList; import org.apache.log4j.Logger; import java.util.*; import java.util.stream.Collectors; /** * @author North */ @DatabaseTable(tableName = "card") public class CardInfo { private static final int MAX_RULE_LENGTH = 750; private static final String SEPARATOR = "@@@"; public static final String SPLIT_MANA_SEPARATOR_SHORT = "*"; public static final String SPLIT_MANA_SEPARATOR_FULL = "{" + SPLIT_MANA_SEPARATOR_SHORT + "}"; public static final String SPLIT_MANA_SEPARATOR_RENDER = " / "; @DatabaseField(indexName = "name_index") protected String name; @DatabaseField(indexName = "setCode_cardNumber_index") protected String cardNumber; @DatabaseField(indexName = "setCode_cardNumber_index") protected String setCode; @DatabaseField(indexName = "className_index") protected String className; @DatabaseField protected String power; @DatabaseField protected String toughness; @DatabaseField protected String startingLoyalty; @DatabaseField protected int convertedManaCost; @DatabaseField(dataType = DataType.ENUM_STRING) protected Rarity rarity; @DatabaseField protected String types; @DatabaseField protected String subtypes; @DatabaseField protected String supertypes; @DatabaseField protected String manaCosts; @DatabaseField(dataType = DataType.STRING, width = MAX_RULE_LENGTH) protected String rules; @DatabaseField protected boolean black; @DatabaseField protected boolean blue; @DatabaseField protected boolean green; @DatabaseField protected boolean red; @DatabaseField protected boolean white; @DatabaseField protected String frameColor; @DatabaseField protected String frameStyle; @DatabaseField protected boolean variousArt; @DatabaseField protected boolean splitCard; @DatabaseField protected boolean splitCardFuse; @DatabaseField protected boolean splitCardAftermath; @DatabaseField protected boolean splitCardHalf; @DatabaseField protected boolean flipCard; @DatabaseField protected boolean doubleFaced; @DatabaseField(indexName = "name_index") protected boolean nightCard; @DatabaseField protected String flipCardName; @DatabaseField protected String secondSideName; @DatabaseField protected boolean adventureCard; @DatabaseField protected String adventureSpellName; public enum ManaCostSide { LEFT, RIGHT, ALL } public CardInfo() { } public CardInfo(Card card) { this.name = card.getName(); this.cardNumber = card.getCardNumber(); this.setCode = card.getExpansionSetCode(); this.className = card.getClass().getCanonicalName(); this.power = card.getPower().toString(); this.toughness = card.getToughness().toString(); this.convertedManaCost = card.getConvertedManaCost(); this.rarity = card.getRarity(); this.splitCard = card.isSplitCard(); this.splitCardFuse = card.getSpellAbility() != null && card.getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED; this.splitCardAftermath = card.getSpellAbility() != null && card.getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_AFTERMATH; this.flipCard = card.isFlipCard(); this.flipCardName = card.getFlipCardName(); this.doubleFaced = card.isTransformable() && card.getSecondCardFace() != null; this.nightCard = card.isNightCard(); Card secondSide = card.getSecondCardFace(); if (secondSide != null) { this.secondSideName = secondSide.getName(); } if (card instanceof AdventureCard) { this.adventureCard = true; this.adventureSpellName = ((AdventureCard) card).getSpellCard().getName(); } this.frameStyle = card.getFrameStyle().toString(); this.frameColor = card.getFrameColor(null).toString(); this.variousArt = card.getUsesVariousArt(); this.blue = card.getColor(null).isBlue(); this.black = card.getColor(null).isBlack(); this.green = card.getColor(null).isGreen(); this.red = card.getColor(null).isRed(); this.white = card.getColor(null).isWhite(); this.setTypes(card.getCardType()); this.setSubtypes(card.getSubtype(null).stream().map(SubType::toString).collect(Collectors.toList())); this.setSuperTypes(card.getSuperType()); // mana cost can contains multiple cards (split left/right, card/adventure) if (card instanceof SplitCard) { List manaCostLeft = ((SplitCard) card).getLeftHalfCard().getManaCost().getSymbols(); List manaCostRight = ((SplitCard) card).getRightHalfCard().getManaCost().getSymbols(); this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight)); } else if (card instanceof AdventureCard) { List manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols(); // Spell from left like MTGA List manaCostRight = card.getManaCost().getSymbols(); this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight)); } else { this.setManaCosts(card.getManaCost().getSymbols()); } int length = 0; List rulesList = new ArrayList<>(); if (card instanceof SplitCard) { for (String rule : ((SplitCard) card).getLeftHalfCard().getRules()) { length += rule.length(); rulesList.add(rule); } for (String rule : ((SplitCard) card).getRightHalfCard().getRules()) { length += rule.length(); rulesList.add(rule); } for (String rule : card.getRules()) { length += rule.length(); rulesList.add(rule); } } else { for (String rule : card.getRules()) { length += rule.length(); rulesList.add(rule); } } if (length > MAX_RULE_LENGTH) { length = 0; ArrayList shortRules = new ArrayList<>(); for (String rule : rulesList) { if (length + rule.length() + 3 <= MAX_RULE_LENGTH) { shortRules.add(rule); length += rule.length() + 3; } else { shortRules.add(rule.substring(0, MAX_RULE_LENGTH - (length + 3))); break; } } Logger.getLogger(CardInfo.class).warn("Card rule text was cut - cardname: " + card.getName()); this.setRules(shortRules); } else { this.setRules(rulesList); } SpellAbility spellAbility = card.getSpellAbility(); if (spellAbility != null) { SpellAbilityType spellAbilityType = spellAbility.getSpellAbilityType(); if (spellAbilityType == SpellAbilityType.SPLIT_LEFT || spellAbilityType == SpellAbilityType.SPLIT_RIGHT) { this.className = this.setCode + '.' + this.name; this.splitCardHalf = true; } } // Starting loyalty if (card.isPlaneswalker()) { for (Ability ab : card.getAbilities()) { if (ab instanceof PlaneswalkerEntersWithLoyaltyCountersAbility) { this.startingLoyalty = "" + ((PlaneswalkerEntersWithLoyaltyCountersAbility) ab).getStartingLoyalty(); } } if (this.startingLoyalty == null) { //Logger.getLogger(CardInfo.class).warn("Planeswalker `" + card.getName() + "` missing starting loyalty"); this.startingLoyalty = ""; } } else { this.startingLoyalty = ""; } } public Card getCard() { return CardImpl.createCard(className, new CardSetInfo(name, setCode, cardNumber, rarity, new CardGraphicInfo(FrameStyle.valueOf(frameStyle), variousArt))); } public Card getMockCard() { if (this.splitCard) { return new MockSplitCard(this); } else { return new MockCard(this); } } public boolean usesVariousArt() { return variousArt; } public ObjectColor getColor() { ObjectColor color = new ObjectColor(); color.setBlack(black); color.setBlue(blue); color.setGreen(green); color.setRed(red); color.setWhite(white); return color; } public ObjectColor getFrameColor() { return new ObjectColor(frameColor); } public FrameStyle getFrameStyle() { return FrameStyle.valueOf(this.frameStyle); } private String joinList(List items) { StringBuilder sb = new StringBuilder(); for (Object item : items) { sb.append(item.toString()).append(SEPARATOR); } return sb.toString(); } private List parseList(String list, ManaCostSide manaCostSide) { if (list.isEmpty()) { return Collections.emptyList(); } List res = new ArrayList<>(); boolean leftSide = true; for (String s : list.split(SEPARATOR)) { if (s.equals(SPLIT_MANA_SEPARATOR_FULL)) { leftSide = false; continue; } if (manaCostSide.equals(ManaCostSide.ALL) || (manaCostSide.equals(ManaCostSide.LEFT) && leftSide) || (manaCostSide.equals(ManaCostSide.RIGHT) && !leftSide)) { res.add(s); } } return res; } public final Set getTypes() { Set list = EnumSet.noneOf(CardType.class); for (String type : this.types.split(SEPARATOR)) { try { list.add(CardType.valueOf(type)); } catch (IllegalArgumentException e) { } } return list; } public final void setTypes(Set types) { StringBuilder sb = new StringBuilder(); for (CardType item : types) { sb.append(item.name()).append(SEPARATOR); } this.types = sb.toString(); } public int getConvertedManaCost() { return convertedManaCost; } public final List getManaCosts(ManaCostSide manaCostSide) { return parseList(manaCosts, manaCostSide); } public final void setManaCosts(List manaCosts) { this.manaCosts = joinList(manaCosts); } public String getName() { return name; } public String getPower() { return power; } public Rarity getRarity() { return rarity; } public final List getRules() { return parseList(rules, ManaCostSide.ALL); } public final void setRules(List rules) { this.rules = joinList(rules); } public final SubTypeList getSubTypes() { SubTypeList sl = new SubTypeList(); if (subtypes.trim().isEmpty()) { return sl; } for (String s : subtypes.split(SEPARATOR)) { sl.add(SubType.fromString(s)); } return sl; } public final void setSubtypes(List subtypes) { this.subtypes = joinList(subtypes); } public final Set getSupertypes() { Set list = EnumSet.noneOf(SuperType.class); for (String type : this.supertypes.split(SEPARATOR)) { try { list.add(SuperType.valueOf(type)); } catch (IllegalArgumentException e) { } } return list; } public final void setSuperTypes(Set superTypes) { StringBuilder sb = new StringBuilder(); for (SuperType item : superTypes) { sb.append(item.name()).append(SEPARATOR); } this.supertypes = sb.toString(); } public String getToughness() { return toughness; } public String getStartingLoyalty() { return startingLoyalty; } public String getSetCode() { return setCode; } public String getClassName() { return className; } public String getCardNumber() { return cardNumber; } public int getCardNumberAsInt() { return CardUtil.parseCardNumberAsInt(cardNumber); } public boolean isSplitCard() { return splitCard; } public boolean isSplitFuseCard() { return splitCardFuse; } public boolean isSplitAftermathCard() { return splitCardAftermath; } public boolean isSplitCardHalf() { return splitCardHalf; } public boolean isFlipCard() { return flipCard; } public String getFlipCardName() { return flipCardName; } public boolean isDoubleFaced() { return doubleFaced; } public boolean isNightCard() { return nightCard; } public String getSecondSideName() { return secondSideName; } public boolean isAdventureCard() { return adventureCard; } public String getAdventureSpellName() { return adventureSpellName; } }