diff --git a/src/main/java/forge/card/CardDb.java b/src/main/java/forge/card/CardDb.java index 0180d4d960d..1446c31e9a5 100644 --- a/src/main/java/forge/card/CardDb.java +++ b/src/main/java/forge/card/CardDb.java @@ -47,7 +47,7 @@ public final class CardDb { this(new MtgDataParser()); // I wish cardname.txt parser was be here. } - private CardDb(final Iterator parser) { + private CardDb(final Iterator parser) { while (parser.hasNext()) { addNewCard(parser.next()); } @@ -57,33 +57,31 @@ public final class CardDb { if (null == card) { return; } //System.out.println(card.getName()); String cardName = card.getName(); - + // 1. register among oracle uniques cards.put(cardName, card); - - // 2. fill refs into two lists: one with + + // 2. Save refs into two lists: one flat and other keyed with sets & name CardPrinted lastAdded = null; for (Entry s : card.getSetsPrinted()) { String set = s.getKey(); - - // get this set storage, if not found, create it! + + // get this set storage, if not found, create it! Map setMap = allCardsBySet.get(set); if (null == setMap) { setMap = new Hashtable(); allCardsBySet.put(set, setMap); } - + int count = s.getValue().getCopiesCount(); - CardPrinted[] cards = new CardPrinted[count]; - setMap.put(cardName, cards); + CardPrinted[] cardCopies = new CardPrinted[count]; + setMap.put(cardName, cardCopies); for (int i = 0; i < count; i++) { - lastAdded = CardPrinted.build(card, set, s.getValue().getRarity(), i+1); + lastAdded = CardPrinted.build(card, set, s.getValue().getRarity(), i); allCardsFlat.add(lastAdded); - cards[i] = lastAdded; + cardCopies[i] = lastAdded; } - } - uniqueCards.put(cardName, lastAdded); } @@ -113,8 +111,8 @@ public final class CardDb { throw new NoSuchElementException(err); } // 3. Get the proper copy - if (artIndex > 0 && artIndex <= cards.length) { return cards[artIndex-1]; } - String err = String.format("Asked for '%s' from '%s' #%d: db didn't find that copy. Note: artIndex is 1-based", name, set, artIndex); + if (artIndex > 0 && artIndex <= cards.length) { return cards[artIndex]; } + String err = String.format("Asked for '%s' from '%s' #%d: db didn't find that copy.", name, set, artIndex); throw new NoSuchElementException(err); } diff --git a/src/main/java/forge/card/CardInSet.java b/src/main/java/forge/card/CardInSet.java index 621a3cfcc0c..1d406a7f4b4 100644 --- a/src/main/java/forge/card/CardInSet.java +++ b/src/main/java/forge/card/CardInSet.java @@ -8,38 +8,14 @@ package forge.card; */ public class CardInSet { - private CardRarity rarity; - private int numCopies; + private final CardRarity rarity; + private final int numCopies; public CardInSet(final CardRarity rarity, final int cntCopies) { this.rarity = rarity; this.numCopies = cntCopies; } - public static CardInSet parse(final String unparsed) { - int spaceAt = unparsed.indexOf(' '); - char rarity = unparsed.charAt(spaceAt + 1); - CardRarity rating; - switch (rarity) { - case 'L': rating = CardRarity.BasicLand; break; - case 'C': rating = CardRarity.Common; break; - case 'U': rating = CardRarity.Uncommon; break; - case 'R': rating = CardRarity.Rare; break; - case 'M': rating = CardRarity.MythicRare; break; - case 'S': rating = CardRarity.Special; break; - default: rating = CardRarity.MythicRare; break; - } - - int number = 1; - int bracketAt = unparsed.indexOf('('); - if (-1 != bracketAt) { - String sN = unparsed.substring(bracketAt + 2, bracketAt + 3); - number = Integer.parseInt(sN); - } - return new CardInSet(rating, number); - - } - public final int getCopiesCount() { return numCopies; } public final CardRarity getRarity() { return rarity; } } diff --git a/src/main/java/forge/card/CardRules.java b/src/main/java/forge/card/CardRules.java index 7000771005c..5a88bc75566 100644 --- a/src/main/java/forge/card/CardRules.java +++ b/src/main/java/forge/card/CardRules.java @@ -1,6 +1,9 @@ package forge.card; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -31,7 +34,7 @@ public final class CardRules { private String loyalty = null; - private HashMap setsPrinted = null; + private Map setsPrinted = null; // Ctor and builders are needed here public String getName() { return name; } @@ -54,7 +57,7 @@ public final class CardRules { } public CardRules(final String cardName, final CardType cardType, final String manacost, - final String ptLine, final String[] cardRules, final String[] setsData) + final String ptLine, final String[] cardRules, final Map setsData) { this.name = cardName; this.type = cardType; @@ -73,12 +76,7 @@ public final class CardRules { } else if (cardType.isPlaneswalker()) { this.loyalty = ptLine; } - - this.setsPrinted = new HashMap(); - for (int iSet = 0; iSet < setsData.length; iSet++) { - String setCode = setsData[iSet].substring(0, setsData[iSet].indexOf(' ')); - setsPrinted.put(setCode, CardInSet.parse(setsData[iSet])); - } + setsPrinted = setsData; } public boolean rulesContain(final String text) { @@ -334,8 +332,20 @@ public final class CardRules { public static final Predicate isBlack = isColor(CardColor.BLACK); public static final Predicate isRed = isColor(CardColor.RED); public static final Predicate isGreen = isColor(CardColor.GREEN); + + public static final Predicate isColorless = hasCntColors((byte) 0); public static final Predicate isMulticolor = hasAtLeastCntColors((byte) 2); + + public static final List> colors = new ArrayList>(); + static { + colors.add(isWhite); + colors.add(isBlue); + colors.add(isBlack); + colors.add(isRed); + colors.add(isGreen); + colors.add(isColorless); + } // Think twice before using these, since rarity is a prop of printed card. public static final Predicate isInLatestSetCommon = rarityInCardsLatestSet(true, CardRarity.Common); diff --git a/src/main/java/forge/card/CardType.java b/src/main/java/forge/card/CardType.java index 149e153d94a..865261ceea1 100644 --- a/src/main/java/forge/card/CardType.java +++ b/src/main/java/forge/card/CardType.java @@ -83,6 +83,9 @@ public final class CardType implements Comparable { public boolean isSorcery() { return coreType.contains(CardCoreType.Sorcery); } public boolean isEnchantment() { return coreType.contains(CardCoreType.Enchantment); } + public boolean isBasic() { return superType.contains(CardSuperType.Basic); } + public boolean isLegendary() { return superType.contains(CardSuperType.Legendary); } + public String getTypesBeforeDash() { ArrayList types = new ArrayList(); for (CardSuperType st : superType) { types.add(st.name()); } @@ -109,5 +112,6 @@ public final class CardType implements Comparable { public int compareTo(final CardType o) { return toString().compareTo(o.toString()); } + } diff --git a/src/main/java/forge/card/MtgDataParser.java b/src/main/java/forge/card/MtgDataParser.java index a373ee2c03f..bff90388b96 100644 --- a/src/main/java/forge/card/MtgDataParser.java +++ b/src/main/java/forge/card/MtgDataParser.java @@ -1,10 +1,16 @@ package forge.card; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.tools.ant.taskdefs.compilers.Sj; import forge.FileUtil; import forge.properties.ForgeProps; @@ -19,6 +25,48 @@ public final class MtgDataParser implements Iterator { it = mtgDataLines.iterator(); skipSetList(); } + + private static List setsToSkipPrefixes = new ArrayList(); + private static List unSets = new ArrayList(); // take only lands from there + static { + setsToSkipPrefixes.add("VG"); // Vanguard + setsToSkipPrefixes.add("ME"); // Mtgo master's editions + setsToSkipPrefixes.add("FV"); // From the vaults + + // Duel decks... or... should I keep them? + setsToSkipPrefixes.add("DVD"); + setsToSkipPrefixes.add("EVT"); + setsToSkipPrefixes.add("EVG"); + setsToSkipPrefixes.add("GVL"); + setsToSkipPrefixes.add("JVC"); + setsToSkipPrefixes.add("DDG"); + setsToSkipPrefixes.add("PVC"); + + // Archenemy - we cannot play it now anyway + setsToSkipPrefixes.add("ARC"); + + // Planechase - this too + setsToSkipPrefixes.add("HOP"); + + // Reprints + setsToSkipPrefixes.add("BRB"); + setsToSkipPrefixes.add("BTD"); + setsToSkipPrefixes.add("DKM"); + //setsToSkipPrefixes.add("ATH"); // No need to skip it really. + // On gatherer's opinion this cards was releases twice in original set + + // Promo sets - all cards have been issued in other sets + setsToSkipPrefixes.add("SDC"); + setsToSkipPrefixes.add("ASTRAL"); + + // Premium decks + setsToSkipPrefixes.add("H09"); + setsToSkipPrefixes.add("H10"); + + // Un-sets are weird, but lands from there are valuable + unSets.add("UNH"); + unSets.add("UGL"); + } private boolean weHaveNext; private void skipSetList() { @@ -38,6 +86,7 @@ public final class MtgDataParser implements Iterator { public CardRules next() { if (!it.hasNext()) { weHaveNext = false; return null; } String name = it.next(); + if (!it.hasNext()) { weHaveNext = false; return null; } String manaCost = it.next(); CardType type = null; @@ -61,12 +110,55 @@ public final class MtgDataParser implements Iterator { strs.add(nextLine); nextLine = it.next(); } - String[] sets = strs.remove(strs.size() - 1).split(", "); + // feel free to return null after this line + + String setsLine = strs.remove(strs.size() - 1); + boolean isBasicLand = type.isLand() && type.isBasic(); + Map sets = getValidEditions(setsLine, isBasicLand); + + if (sets.isEmpty()) { return null; } // that was a bad card - it won't be added by invoker return new CardRules(name, type, manaCost, ptOrLoyalty, strs.toArray(emptyArray), sets); } - @Override public void remove() { } + private Map getValidEditions(final String sets, final boolean isBasicLand) { + String[] setsData = sets.split(", "); + Map result = new HashMap(); + for (int iSet = 0; iSet < setsData.length; iSet++) { + int spacePos = setsData[iSet].indexOf(' '); + String setCode = setsData[iSet].substring(0, spacePos); + boolean shouldSkip = false; + for (String s : setsToSkipPrefixes) { if (setCode.startsWith(s)) { shouldSkip = true; break; } } + for (String s : unSets) { if (setCode.startsWith(s) && !isBasicLand) { shouldSkip = true; break; } } + if (shouldSkip) { continue; } + result.put(setCode, parseCardInSet(setsData[iSet], spacePos)); + } + return result; + } + + public static CardInSet parseCardInSet(final String unparsed, final int spaceAt) { + char rarity = unparsed.charAt(spaceAt + 1); + CardRarity rating; + switch (rarity) { + case 'L': rating = CardRarity.BasicLand; break; + case 'C': rating = CardRarity.Common; break; + case 'U': rating = CardRarity.Uncommon; break; + case 'R': rating = CardRarity.Rare; break; + case 'M': rating = CardRarity.MythicRare; break; + case 'S': rating = CardRarity.Special; break; + default: rating = CardRarity.MythicRare; break; + } + + int number = 1; + int bracketAt = unparsed.indexOf('(', spaceAt); + if (-1 != bracketAt) { + String sN = unparsed.substring(bracketAt + 2, unparsed.indexOf(')', bracketAt)); + number = Integer.parseInt(sN); + } + return new CardInSet(rating, number); + } + + @Override public void remove() { } } diff --git a/src/main/java/forge/card/cardFactory/LazyCardFactory.java b/src/main/java/forge/card/cardFactory/LazyCardFactory.java index acd9ab5c8bc..039a23acc9f 100644 --- a/src/main/java/forge/card/cardFactory/LazyCardFactory.java +++ b/src/main/java/forge/card/cardFactory/LazyCardFactory.java @@ -1,7 +1,9 @@ package forge.card.cardFactory; import java.io.File; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Map; import net.slightlymagic.braids.util.NotImplementedError; @@ -20,7 +22,8 @@ import forge.Player; public class LazyCardFactory extends AbstractCardFactory { private final CardReader cardReader; - + private final List cardsFailedToLoad = new ArrayList(); + /** * Construct an instance, pointing it to a specific cardsfolder. * @@ -65,18 +68,28 @@ public class LazyCardFactory extends AbstractCardFactory { protected Card getCard2(final String cardName, final Player owner) { final Map cardNamesToCards = getMap(); Card result = null; - boolean cardExists = false; + boolean wasLoaded = cardNamesToCards.containsKey(cardName); + + if (!wasLoaded) { + + if (cardsFailedToLoad.contains(cardName)) { + return null; // no more System.err, exceptions of other drama - just return null. + } - if (!cardNamesToCards.containsKey(cardName)) { final String canonicalASCIIName = CardUtil.canonicalizeCardName(cardName); - getCardReader().findCard(canonicalASCIIName); - - if (cardNamesToCards.containsKey(cardName)) { - cardExists = true; + Card cardRequested = getCardReader().findCard(canonicalASCIIName); + if (null != cardRequested) { + cardNamesToCards.put(cardName, cardRequested); + wasLoaded = true; + } else { + cardsFailedToLoad.add(cardName); + System.err.println(String.format("LazyCF: Tried to read from disk card '%s' but not found it!", cardName)); + return null; } } - if (cardExists) { + // Factory should return us a copy, ready for changes. + if (wasLoaded) { result = super.getCard2(cardName, owner); }