mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 03:08:02 +00:00
New lightweight classes for cards management in deck editors and quest mode (no other parts or project changed)
This commit is contained in:
16
.gitattributes
vendored
16
.gitattributes
vendored
@@ -9583,7 +9583,21 @@ src/main/java/forge/Time.java svneol=native#text/plain
|
|||||||
src/main/java/forge/UndoCommand.java svneol=native#text/plain
|
src/main/java/forge/UndoCommand.java svneol=native#text/plain
|
||||||
src/main/java/forge/Upkeep.java svneol=native#text/plain
|
src/main/java/forge/Upkeep.java svneol=native#text/plain
|
||||||
src/main/java/forge/ZCTrigger.java svneol=native#text/plain
|
src/main/java/forge/ZCTrigger.java svneol=native#text/plain
|
||||||
src/main/java/forge/card/CardReference.java -text
|
src/main/java/forge/card/CardColor.java -text
|
||||||
|
src/main/java/forge/card/CardCoreType.java -text
|
||||||
|
src/main/java/forge/card/CardDb.java -text
|
||||||
|
src/main/java/forge/card/CardInSet.java -text
|
||||||
|
src/main/java/forge/card/CardManaCost.java -text
|
||||||
|
src/main/java/forge/card/CardManaCostShard.java -text
|
||||||
|
src/main/java/forge/card/CardPool.java -text
|
||||||
|
src/main/java/forge/card/CardPoolView.java -text
|
||||||
|
src/main/java/forge/card/CardPrinted.java -text
|
||||||
|
src/main/java/forge/card/CardRarity.java -text
|
||||||
|
src/main/java/forge/card/CardRules.java -text
|
||||||
|
src/main/java/forge/card/CardSet.java -text
|
||||||
|
src/main/java/forge/card/CardSuperType.java -text
|
||||||
|
src/main/java/forge/card/CardType.java -text
|
||||||
|
src/main/java/forge/card/MtgDataParser.java -text
|
||||||
src/main/java/forge/card/abilityFactory/AbilityFactory.java svneol=native#text/plain
|
src/main/java/forge/card/abilityFactory/AbilityFactory.java svneol=native#text/plain
|
||||||
src/main/java/forge/card/abilityFactory/AbilityFactory_AlterLife.java svneol=native#text/plain
|
src/main/java/forge/card/abilityFactory/AbilityFactory_AlterLife.java svneol=native#text/plain
|
||||||
src/main/java/forge/card/abilityFactory/AbilityFactory_Animate.java svneol=native#text/plain
|
src/main/java/forge/card/abilityFactory/AbilityFactory_Animate.java svneol=native#text/plain
|
||||||
|
|||||||
66
src/main/java/forge/card/CardColor.java
Normal file
66
src/main/java/forge/card/CardColor.java
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import org.apache.tools.ant.taskdefs.Concat;
|
||||||
|
|
||||||
|
import forge.Constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CardColor class.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardColor.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
public final class CardColor implements Comparable<CardColor> {
|
||||||
|
|
||||||
|
public static final byte WHITE = 1 << 1;
|
||||||
|
public static final byte BLUE = 1 << 2;
|
||||||
|
public static final byte BLACK = 1 << 3;
|
||||||
|
public static final byte RED = 1 << 4;
|
||||||
|
public static final byte GREEN = 1 << 5;
|
||||||
|
|
||||||
|
private final byte myColor;
|
||||||
|
|
||||||
|
// TODO: some cards state "CardName is %color%" (e.g. pacts of...) - fix this later
|
||||||
|
public CardColor(final CardManaCost mana) {
|
||||||
|
myColor = mana.getColorProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAnyColor(final byte colormask) { return (myColor & colormask) != 0; }
|
||||||
|
public boolean hasAllColors(final byte colormask) { return (myColor & colormask) == colormask; }
|
||||||
|
|
||||||
|
public int countColors() { byte v = myColor; int c = 0; for (; v != 0; c++) { v &= v - 1; } return c; } // bit count
|
||||||
|
|
||||||
|
public boolean isColorless() { return myColor == 0; }
|
||||||
|
public boolean isMulticolor() { return countColors() > 1; }
|
||||||
|
public boolean isMonoColor() { return countColors() == 1; }
|
||||||
|
public boolean isEqual(final byte color) { return color == myColor; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CardColor other) { return myColor - other.myColor; }
|
||||||
|
|
||||||
|
// Presets
|
||||||
|
public boolean hasWhite() { return hasAnyColor(WHITE); }
|
||||||
|
public boolean hasBlue() { return hasAnyColor(BLUE); }
|
||||||
|
public boolean hasBlack() { return hasAnyColor(BLACK); }
|
||||||
|
public boolean hasRed() { return hasAnyColor(RED); }
|
||||||
|
public boolean hasGreen() { return hasAnyColor(GREEN); }
|
||||||
|
|
||||||
|
public boolean isWhite() { return isEqual(WHITE); }
|
||||||
|
public boolean isBlue() { return isEqual(BLUE); }
|
||||||
|
public boolean isBlack() { return isEqual(BLACK); }
|
||||||
|
public boolean isRed() { return isEqual(RED); }
|
||||||
|
public boolean isGreen() { return isEqual(GREEN); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
switch (myColor) {
|
||||||
|
case 0: return "";
|
||||||
|
case WHITE: return "White"; // Constant.Color.White;
|
||||||
|
case BLUE: return "Blue"; // Constant.Color.Blue;
|
||||||
|
case BLACK: return "Black"; // Constant.Color.Black;
|
||||||
|
case RED: return "Red"; // Constant.Color.Red;
|
||||||
|
case GREEN: return "Green"; // Constant.Color.Green;
|
||||||
|
default: return "Multi";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/main/java/forge/card/CardCoreType.java
Normal file
7
src/main/java/forge/card/CardCoreType.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
|
||||||
|
public enum CardCoreType
|
||||||
|
{
|
||||||
|
Artifact, Creature, Enchantment, Instant, Land, Plane, Planeswalker, Scheme, Sorcery, Tribal, Vanguard
|
||||||
|
}
|
||||||
145
src/main/java/forge/card/CardDb.java
Normal file
145
src/main/java/forge/card/CardDb.java
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import forge.Card;
|
||||||
|
import forge.FileUtil;
|
||||||
|
import forge.properties.ForgeProps;
|
||||||
|
import forge.properties.NewConstants;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CardOracleDatabase class.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardOracleDatabase.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
public final class CardDb {
|
||||||
|
private static volatile CardDb onlyInstance = null; // 'volatile' keyword makes this working
|
||||||
|
public static CardDb instance() {
|
||||||
|
if (onlyInstance == null) {
|
||||||
|
synchronized (CardDb.class) {
|
||||||
|
if (onlyInstance == null) { // It's broken under 1.4 and below, on 1.5+ works again!
|
||||||
|
onlyInstance = new CardDb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return onlyInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here oracle cards
|
||||||
|
private final Map<String, CardRules> cards = new Hashtable<String, CardRules>();
|
||||||
|
// Here are refs, get them by name
|
||||||
|
private final Map<String, CardPrinted> uniqueCards = new Hashtable<String, CardPrinted>();
|
||||||
|
|
||||||
|
// need this to obtain cardReference by name+set+artindex
|
||||||
|
private final Map<String, Map<String, CardPrinted[]>> allCardsBySet = new Hashtable<String, Map<String, CardPrinted[]>>();
|
||||||
|
// this is the same list in flat storage
|
||||||
|
private final List<CardPrinted> allCardsFlat = new ArrayList<CardPrinted>();
|
||||||
|
|
||||||
|
|
||||||
|
private CardDb() {
|
||||||
|
List<String> mtgDataLines = FileUtil.readFile(ForgeProps.getFile(NewConstants.MTG_DATA));
|
||||||
|
MtgDataParser parser = new MtgDataParser(mtgDataLines);
|
||||||
|
while (parser.hasNext()) {
|
||||||
|
addNewCard(parser.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNewCard(final CardRules card) {
|
||||||
|
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
|
||||||
|
CardPrinted lastAdded = null;
|
||||||
|
for (Entry<String, CardInSet> s : card.getSetsPrinted()) {
|
||||||
|
String set = s.getKey();
|
||||||
|
|
||||||
|
// get this set storage, if not found, create it!
|
||||||
|
Map<String, CardPrinted[]> setMap = allCardsBySet.get(set);
|
||||||
|
if (null == setMap) {
|
||||||
|
setMap = new Hashtable<String, CardPrinted[]>();
|
||||||
|
allCardsBySet.put(set, setMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = s.getValue().getCopiesCount();
|
||||||
|
CardPrinted[] cards = new CardPrinted[count];
|
||||||
|
setMap.put(cardName, cards);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
lastAdded = CardPrinted.build(card, set, s.getValue().getRarity(), i+1);
|
||||||
|
allCardsFlat.add(lastAdded);
|
||||||
|
cards[i] = lastAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueCards.put(cardName, lastAdded);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single fetch
|
||||||
|
public CardPrinted getCard(final String name) {
|
||||||
|
// Sometimes they read from decks things like "CardName|Set" - but we can handle it
|
||||||
|
int pipePos = name.indexOf('|');
|
||||||
|
if (pipePos >= 0) { return getCard(name.substring(0, pipePos), name.substring(pipePos+1)); }
|
||||||
|
// OK, plain name here
|
||||||
|
CardPrinted card = uniqueCards.get(name);
|
||||||
|
if (card != null) { return card; }
|
||||||
|
throw new NoSuchElementException(String.format("Card '%s' not found in our database.", name));
|
||||||
|
}
|
||||||
|
// Advanced fetch by name+set
|
||||||
|
public CardPrinted getCard(final String name, final String set) { return getCard(name, set, 1); }
|
||||||
|
public CardPrinted getCard(final String name, final String set, final int artIndex) {
|
||||||
|
// 1. get set
|
||||||
|
Map<String, CardPrinted[]> cardsFromset = allCardsBySet.get(set);
|
||||||
|
if (null == cardsFromset) {
|
||||||
|
String err = String.format("Asked for card '%s' from set '%s': that set was not found. :(", name, set);
|
||||||
|
throw new NoSuchElementException(err);
|
||||||
|
}
|
||||||
|
// 2. Find the card itself
|
||||||
|
CardPrinted[] cards = cardsFromset.get(name);
|
||||||
|
if (null == cards) {
|
||||||
|
String err = String.format("Asked for card '%s' from '%s': set found, but the card wasn't. :(", name, set);
|
||||||
|
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);
|
||||||
|
throw new NoSuchElementException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch from Forge's Card instance. Well, there should be no errors, but we'll still check
|
||||||
|
public CardPrinted getCard(final Card forgeCard) {
|
||||||
|
String name = forgeCard.getName();
|
||||||
|
String set = forgeCard.getCurSetCode();
|
||||||
|
if (StringUtils.isNotBlank(set)) { return getCard(name, set); }
|
||||||
|
return getCard(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple fetch
|
||||||
|
public List<CardPrinted> getCards(final List<String> names) {
|
||||||
|
List<CardPrinted> result = new ArrayList<CardPrinted>();
|
||||||
|
for (String name : names) { result.add(getCard(name)); }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a list of all cards from their respective latest editions
|
||||||
|
public Iterable<CardPrinted> getAllUniqueCards() {
|
||||||
|
return uniqueCards.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CardPrinted> getAllCards() { return allCardsFlat; }
|
||||||
|
|
||||||
|
}
|
||||||
45
src/main/java/forge/card/CardInSet.java
Normal file
45
src/main/java/forge/card/CardInSet.java
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CardInSet class.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardInSet.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class CardInSet {
|
||||||
|
private CardRarity rarity;
|
||||||
|
private 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; }
|
||||||
|
}
|
||||||
154
src/main/java/forge/card/CardManaCost.java
Normal file
154
src/main/java/forge/card/CardManaCost.java
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CardManaCost class.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardManaCost.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
public final class CardManaCost implements Comparable<CardManaCost> {
|
||||||
|
private final List<CardManaCostShard> shards = new ArrayList<CardManaCostShard>();
|
||||||
|
private final int genericCost;
|
||||||
|
private final boolean isEmpty; // lands cost
|
||||||
|
private final String stringValue; // precalculated for toString;
|
||||||
|
|
||||||
|
private Float compareWeight = null;
|
||||||
|
|
||||||
|
|
||||||
|
// pass mana cost parser here
|
||||||
|
private CardManaCost() {
|
||||||
|
isEmpty = true;
|
||||||
|
genericCost = 0;
|
||||||
|
stringValue = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final CardManaCost empty = new CardManaCost();
|
||||||
|
|
||||||
|
public CardManaCost(final String cost) {
|
||||||
|
ParserMtgData parser = new ParserMtgData(cost);
|
||||||
|
if (!parser.hasNext()) {
|
||||||
|
throw new RuntimeException("Empty manacost passed to parser (this should have been handled before)");
|
||||||
|
}
|
||||||
|
isEmpty = false;
|
||||||
|
while (parser.hasNext()) {
|
||||||
|
CardManaCostShard shard = parser.next();
|
||||||
|
if (shard != null) { shards.add(shard); } // null is OK - that was generic mana
|
||||||
|
}
|
||||||
|
genericCost = parser.getColorlessCost(); // collect generic mana here
|
||||||
|
stringValue = getSimpleString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSimpleString() {
|
||||||
|
if (shards.isEmpty()) return Integer.toString(genericCost);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean isFirst = false;
|
||||||
|
if (genericCost > 0) { sb.append(genericCost); isFirst = false; }
|
||||||
|
for (CardManaCostShard s : shards) {
|
||||||
|
if ( !isFirst ) { sb.append(' '); }
|
||||||
|
else { isFirst = false; }
|
||||||
|
sb.append(s.toString());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCMC() {
|
||||||
|
int sum = 0;
|
||||||
|
for (CardManaCostShard s : shards) { sum += s.cmc; }
|
||||||
|
return sum + genericCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getColorProfile() {
|
||||||
|
byte result = 0;
|
||||||
|
for (CardManaCostShard s : shards) { result |= s.getColorMask(); }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CardManaCost o) { return getCompareWeight().compareTo(o.getCompareWeight()); }
|
||||||
|
private Float getCompareWeight() {
|
||||||
|
if (compareWeight == null) {
|
||||||
|
float weight = genericCost;
|
||||||
|
for (CardManaCostShard s : shards) { weight += s.cmpc; }
|
||||||
|
if (isEmpty) {
|
||||||
|
weight = -1; // for those who doesn't even have a 0 sign on card
|
||||||
|
}
|
||||||
|
compareWeight = Float.valueOf(weight);
|
||||||
|
}
|
||||||
|
return compareWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParserMtgData implements Iterator<CardManaCostShard> {
|
||||||
|
private final String cost;
|
||||||
|
|
||||||
|
private int nextBracket;
|
||||||
|
private int colorlessCost;
|
||||||
|
|
||||||
|
|
||||||
|
public ParserMtgData(final String cost) {
|
||||||
|
this.cost = cost;
|
||||||
|
// System.out.println(cost);
|
||||||
|
nextBracket = cost.indexOf('{');
|
||||||
|
colorlessCost = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColorlessCost() {
|
||||||
|
if ( hasNext() ) {
|
||||||
|
throw new RuntimeException("Colorless cost should be obtained after iteration is complete");
|
||||||
|
}
|
||||||
|
return colorlessCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() { return nextBracket != -1; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CardManaCostShard next() {
|
||||||
|
int closeBracket = cost.indexOf('}', nextBracket);
|
||||||
|
String unparsed = cost.substring(nextBracket + 1, closeBracket);
|
||||||
|
nextBracket = cost.indexOf('{', closeBracket + 1);
|
||||||
|
|
||||||
|
// System.out.println(unparsed);
|
||||||
|
if (StringUtils.isNumeric(unparsed)) {
|
||||||
|
colorlessCost += Integer.parseInt(unparsed);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int atoms = 0;
|
||||||
|
for (int iChar = 0; iChar < unparsed.length(); iChar++) {
|
||||||
|
switch (unparsed.charAt(iChar)) {
|
||||||
|
case 'W': atoms |= CardManaCostShard.Atom.WHITE; break;
|
||||||
|
case 'U': atoms |= CardManaCostShard.Atom.BLUE; break;
|
||||||
|
case 'B': atoms |= CardManaCostShard.Atom.BLACK; break;
|
||||||
|
case 'R': atoms |= CardManaCostShard.Atom.RED; break;
|
||||||
|
case 'G': atoms |= CardManaCostShard.Atom.GREEN; break;
|
||||||
|
case '2': atoms |= CardManaCostShard.Atom.OR_2_COLORLESS; break;
|
||||||
|
case 'P': atoms |= CardManaCostShard.Atom.OR_2_LIFE; break;
|
||||||
|
case 'X': atoms |= CardManaCostShard.Atom.IS_X; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CardManaCostShard.valueOf(atoms);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() { } // unsuported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
124
src/main/java/forge/card/CardManaCostShard.java
Normal file
124
src/main/java/forge/card/CardManaCostShard.java
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import forge.card.mana.ManaCost;
|
||||||
|
|
||||||
|
public class CardManaCostShard {
|
||||||
|
|
||||||
|
private final int shard;
|
||||||
|
public final int cmc;
|
||||||
|
public final float cmpc;
|
||||||
|
private final String stringValue;
|
||||||
|
protected CardManaCostShard(int value, String sValue) {
|
||||||
|
shard = value;
|
||||||
|
cmc = getCMC();
|
||||||
|
cmpc = getCmpCost();
|
||||||
|
stringValue = sValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Atom {
|
||||||
|
//int COLORLESS = 1 << 0;
|
||||||
|
int WHITE = 1 << 1;
|
||||||
|
int BLUE = 1 << 2;
|
||||||
|
int BLACK = 1 << 3;
|
||||||
|
int RED = 1 << 4;
|
||||||
|
int GREEN = 1 << 5;
|
||||||
|
|
||||||
|
int IS_X = 1 << 8;
|
||||||
|
int OR_2_COLORLESS = 1 << 9;
|
||||||
|
int OR_2_LIFE = 1 << 10;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Why boxed values are here?
|
||||||
|
* They is meant to be constant and require no further boxing if added into an arraylist or something else,
|
||||||
|
* with generic parameters that would require Object's descendant.
|
||||||
|
*
|
||||||
|
* Choosing between "let's have some boxing" and "let's have some unboxing",
|
||||||
|
* I choose the latter,
|
||||||
|
* because memory for boxed objects will be taken from heap,
|
||||||
|
* while unboxed values will lay on stack, which is faster
|
||||||
|
*/
|
||||||
|
public static final CardManaCostShard X = new CardManaCostShard(Atom.IS_X, "X");
|
||||||
|
|
||||||
|
public static final CardManaCostShard WHITE = new CardManaCostShard(Atom.WHITE, "W");
|
||||||
|
public static final CardManaCostShard BLUE = new CardManaCostShard(Atom.BLUE, "U");
|
||||||
|
public static final CardManaCostShard BLACK = new CardManaCostShard(Atom.BLACK, "B");
|
||||||
|
public static final CardManaCostShard RED = new CardManaCostShard(Atom.RED, "R");
|
||||||
|
public static final CardManaCostShard GREEN = new CardManaCostShard(Atom.GREEN, "G");
|
||||||
|
|
||||||
|
public static final CardManaCostShard PW = new CardManaCostShard(Atom.WHITE | Atom.OR_2_LIFE, "W/P");
|
||||||
|
public static final CardManaCostShard PU = new CardManaCostShard(Atom.BLUE | Atom.OR_2_LIFE, "U/P");
|
||||||
|
public static final CardManaCostShard PB = new CardManaCostShard(Atom.BLACK | Atom.OR_2_LIFE, "B/P");
|
||||||
|
public static final CardManaCostShard PR = new CardManaCostShard(Atom.RED | Atom.OR_2_LIFE, "R/P");
|
||||||
|
public static final CardManaCostShard PG = new CardManaCostShard(Atom.GREEN | Atom.OR_2_LIFE, "G/P");
|
||||||
|
|
||||||
|
public static final CardManaCostShard WU = new CardManaCostShard(Atom.WHITE | Atom.BLUE, "W/U");
|
||||||
|
public static final CardManaCostShard WB = new CardManaCostShard(Atom.WHITE | Atom.BLACK, "W/B");
|
||||||
|
public static final CardManaCostShard WR = new CardManaCostShard(Atom.WHITE | Atom.RED, "W/R");
|
||||||
|
public static final CardManaCostShard WG = new CardManaCostShard(Atom.WHITE | Atom.GREEN, "W/G");
|
||||||
|
public static final CardManaCostShard UB = new CardManaCostShard(Atom.BLUE | Atom.BLACK, "U/B");
|
||||||
|
public static final CardManaCostShard UR = new CardManaCostShard(Atom.BLUE | Atom.RED, "U/R");
|
||||||
|
public static final CardManaCostShard UG = new CardManaCostShard(Atom.BLUE | Atom.GREEN, "U/G");
|
||||||
|
public static final CardManaCostShard BR = new CardManaCostShard(Atom.BLACK | Atom.RED, "B/R");
|
||||||
|
public static final CardManaCostShard BG = new CardManaCostShard(Atom.BLACK | Atom.GREEN, "B/G");
|
||||||
|
public static final CardManaCostShard RG = new CardManaCostShard(Atom.RED | Atom.GREEN, "R/G");
|
||||||
|
|
||||||
|
public static final CardManaCostShard W2 = new CardManaCostShard(Atom.WHITE | Atom.OR_2_COLORLESS, "2/W");
|
||||||
|
public static final CardManaCostShard U2 = new CardManaCostShard(Atom.BLUE | Atom.OR_2_COLORLESS, "2/U");
|
||||||
|
public static final CardManaCostShard B2 = new CardManaCostShard(Atom.BLACK | Atom.OR_2_COLORLESS, "2/B");
|
||||||
|
public static final CardManaCostShard R2 = new CardManaCostShard(Atom.RED | Atom.OR_2_COLORLESS, "2/R");
|
||||||
|
public static final CardManaCostShard G2 = new CardManaCostShard(Atom.GREEN | Atom.OR_2_COLORLESS, "2/G");
|
||||||
|
|
||||||
|
private static final CardManaCostShard[] allPossible = new CardManaCostShard[] {
|
||||||
|
X, WHITE, BLUE, BLACK, RED, GREEN,
|
||||||
|
PW, PU, PB, PR, PG,
|
||||||
|
WU, WB, WR, WG, UB, UR, UG, BR, BG, RG,
|
||||||
|
W2, U2, B2, R2, G2
|
||||||
|
};
|
||||||
|
|
||||||
|
private int getCMC() {
|
||||||
|
if (0 != (shard & Atom.IS_X)) { return 0; }
|
||||||
|
if (0 != (shard & Atom.OR_2_COLORLESS)) { return 2; }
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Mana cost, adjusted slightly to make colored mana parts more significant.
|
||||||
|
* Should only be used for comparison purposes; using this method allows the sort:
|
||||||
|
* 2 < X 2 < 1 U < U U < UR U < X U U < X X U U
|
||||||
|
*
|
||||||
|
* @return The converted cost + 0.0005* the number of colored mana in the cost + 0.00001 *
|
||||||
|
* the number of X's in the cost
|
||||||
|
*/
|
||||||
|
private float getCmpCost() {
|
||||||
|
if (0 != (shard & Atom.IS_X)) { return 0.0001f; }
|
||||||
|
float cost = 0 != (shard & Atom.OR_2_COLORLESS) ? 2 : 1;
|
||||||
|
// yes, these numbers are magic, slightly-magic
|
||||||
|
if (0 != (shard & Atom.WHITE)) { cost += 0.0005f; }
|
||||||
|
if (0 != (shard & Atom.BLUE)) { cost += 0.0020f; }
|
||||||
|
if (0 != (shard & Atom.BLACK)) { cost += 0.0080f; }
|
||||||
|
if (0 != (shard & Atom.RED)) { cost += 0.0320f; }
|
||||||
|
if (0 != (shard & Atom.GREEN)) { cost += 0.1280f; }
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte getColorMask() {
|
||||||
|
byte result = 0;
|
||||||
|
if (0 != (shard & Atom.WHITE)) { result |= CardColor.WHITE; }
|
||||||
|
if (0 != (shard & Atom.BLUE)) { result |= CardColor.BLUE; }
|
||||||
|
if (0 != (shard & Atom.BLACK)) { result |= CardColor.BLACK; }
|
||||||
|
if (0 != (shard & Atom.RED)) { result |= CardColor.RED; }
|
||||||
|
if (0 != (shard & Atom.GREEN)) { result |= CardColor.GREEN; }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CardManaCostShard valueOf(final int atoms) {
|
||||||
|
for (int i = 0; i < allPossible.length; i++) {
|
||||||
|
if (allPossible[i].shard == atoms) { return allPossible[i]; }
|
||||||
|
}
|
||||||
|
throw new RuntimeException(String.format("Not fount: mana shard with profile = %x", atoms));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() { return stringValue; }
|
||||||
|
}
|
||||||
67
src/main/java/forge/card/CardPool.java
Normal file
67
src/main/java/forge/card/CardPool.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CardPool class.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardReference.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
public final class CardPool extends CardPoolView {
|
||||||
|
|
||||||
|
// Contructors here
|
||||||
|
public CardPool() { super(); }
|
||||||
|
public CardPool(final List<String> names) { super(); addAllCards(CardDb.instance().getCards(names)); }
|
||||||
|
|
||||||
|
// Copy ctor will create its own modifiable pool
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public CardPool(final CardPoolView from) {
|
||||||
|
super();
|
||||||
|
cards = (Hashtable<CardPrinted, Integer>) ((Hashtable<CardPrinted, Integer>) (from.cards)).clone();
|
||||||
|
}
|
||||||
|
public CardPool(final Iterable<CardPrinted> list) {
|
||||||
|
this(); addAllCards(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// get
|
||||||
|
public CardPoolView getView() { return new CardPoolView(Collections.unmodifiableMap(cards)); }
|
||||||
|
|
||||||
|
// Cards manipulation
|
||||||
|
public void add(final CardPrinted card) { add(card, 1); }
|
||||||
|
public void add(final CardPrinted card, final int amount) {
|
||||||
|
cards.put(card, count(card) + amount);
|
||||||
|
isListInSync = false;
|
||||||
|
}
|
||||||
|
public void addAllCards(final Iterable<CardPrinted> cards) {
|
||||||
|
for (CardPrinted cr : cards) { add(cr); }
|
||||||
|
isListInSync = false;
|
||||||
|
}
|
||||||
|
public void addAll(final Iterable<Entry<CardPrinted, Integer>> map) {
|
||||||
|
for (Entry<CardPrinted, Integer> e : map) { add(e.getKey(), e.getValue()); }
|
||||||
|
isListInSync = false;
|
||||||
|
}
|
||||||
|
public void addAll(final Entry<CardPrinted, Integer>[] map) {
|
||||||
|
for (Entry<CardPrinted, Integer> e : map) { add(e.getKey(), e.getValue()); }
|
||||||
|
isListInSync = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(final CardPrinted card) { remove(card, 1); }
|
||||||
|
public void remove(final CardPrinted card, final int amount) {
|
||||||
|
int count = count(card);
|
||||||
|
if (count <= amount) { cards.remove(card); }
|
||||||
|
else { cards.put(card, count - amount); }
|
||||||
|
isListInSync = false;
|
||||||
|
}
|
||||||
|
public void removeAll(final Iterable<Entry<CardPrinted, Integer>> map) {
|
||||||
|
for (Entry<CardPrinted, Integer> e : map) { remove(e.getKey(), e.getValue()); }
|
||||||
|
isListInSync = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() { cards.clear(); isListInSync = false; }
|
||||||
|
}
|
||||||
94
src/main/java/forge/card/CardPoolView.java
Normal file
94
src/main/java/forge/card/CardPoolView.java
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import forge.CardList;
|
||||||
|
|
||||||
|
import net.slightlymagic.braids.util.lambda.Lambda1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CardPoolView class.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardPoolView.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
public class CardPoolView implements Iterable<Entry<CardPrinted, Integer>> {
|
||||||
|
|
||||||
|
// Field Accessors for select/aggregate operations with filters.
|
||||||
|
public final static Lambda1<CardRules, Entry<CardPrinted, Integer>> fnToCard =
|
||||||
|
new Lambda1<CardRules, Entry<CardPrinted, Integer>>() {
|
||||||
|
@Override public CardRules apply(final Entry<CardPrinted, Integer> from) { return from.getKey().getCard(); }
|
||||||
|
};
|
||||||
|
public final static Lambda1<CardPrinted, Entry<CardPrinted, Integer>> fnToReference =
|
||||||
|
new Lambda1<CardPrinted, Entry<CardPrinted, Integer>>() {
|
||||||
|
@Override public CardPrinted apply(final Entry<CardPrinted, Integer> from) { return from.getKey(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
public final static Lambda1<Integer, Entry<CardPrinted, Integer>> fnToCount =
|
||||||
|
new Lambda1<Integer, Entry<CardPrinted, Integer>>() {
|
||||||
|
@Override public Integer apply(final Entry<CardPrinted, Integer> from) { return from.getValue(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
public CardPoolView() { cards = new Hashtable<CardPrinted, Integer>(); }
|
||||||
|
public CardPoolView(final Map<CardPrinted, Integer> inMap) { cards = inMap; }
|
||||||
|
|
||||||
|
// Data members
|
||||||
|
protected Map<CardPrinted, Integer> cards;
|
||||||
|
|
||||||
|
// same thing as above, it was copied to provide sorting (needed by table views in deck editors)
|
||||||
|
protected List<Entry<CardPrinted, Integer>> cardsListOrdered = new ArrayList<Map.Entry<CardPrinted,Integer>>();
|
||||||
|
protected boolean isListInSync = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Iterator<Entry<CardPrinted, Integer>> iterator() { return cards.entrySet().iterator(); }
|
||||||
|
|
||||||
|
// Cards manipulation
|
||||||
|
public final int count(final CardPrinted card) {
|
||||||
|
if (cards == null) { return 0; }
|
||||||
|
Integer boxed = cards.get(card);
|
||||||
|
return boxed == null ? 0 : boxed.intValue();
|
||||||
|
}
|
||||||
|
public final int countAll() {
|
||||||
|
int result = 0;
|
||||||
|
if (cards != null) { for (Integer n : cards.values()) { result += n; } }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public final int countDistinct() { return cards.size(); }
|
||||||
|
|
||||||
|
public final List<Entry<CardPrinted, Integer>> getOrderedList() {
|
||||||
|
if (!isListInSync) {
|
||||||
|
cardsListOrdered.clear();
|
||||||
|
if (cards != null) {
|
||||||
|
for (Entry<CardPrinted, Integer> e : cards.entrySet()) {
|
||||||
|
cardsListOrdered.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isListInSync = true;
|
||||||
|
}
|
||||||
|
return cardsListOrdered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final List<CardPrinted> toFlatList() {
|
||||||
|
List<CardPrinted> result = new ArrayList<CardPrinted>();
|
||||||
|
for (Entry<CardPrinted, Integer> e : this) {
|
||||||
|
for (int i = 0; i < e.getValue(); i++) { result.add(e.getKey()); }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final CardList toForgeCardList() {
|
||||||
|
CardList result = new CardList();
|
||||||
|
for (Entry<CardPrinted, Integer> e : this) {
|
||||||
|
for (int i = 0; i < e.getValue(); i++) {
|
||||||
|
result.add(e.getKey().toForgeCard());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
166
src/main/java/forge/card/CardPrinted.java
Normal file
166
src/main/java/forge/card/CardPrinted.java
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import net.slightlymagic.braids.util.lambda.Lambda1;
|
||||||
|
import net.slightlymagic.maxmtg.Predicate;
|
||||||
|
import forge.AllZone;
|
||||||
|
import forge.Card;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CardReference class.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardReference.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
public final class CardPrinted implements Comparable<CardPrinted> {
|
||||||
|
// Reference to rules
|
||||||
|
private final transient CardRules card;
|
||||||
|
|
||||||
|
// These fields are kinda PK for PrintedCard
|
||||||
|
private final String name;
|
||||||
|
private final String cardSet;
|
||||||
|
private final int artIndex;
|
||||||
|
private final boolean foiled;
|
||||||
|
|
||||||
|
// Calculated fields are below:
|
||||||
|
private final transient CardRarity rarity; // rarity is given in ctor when set is assigned
|
||||||
|
|
||||||
|
// need this to be sure that different cased names won't break the system (and create uniqie cardref entries)
|
||||||
|
private final transient String _name_lcase;
|
||||||
|
|
||||||
|
// field RO accessors
|
||||||
|
public String getName() { return name; }
|
||||||
|
public String getSet() { return cardSet; }
|
||||||
|
public int getArtIndex() { return artIndex; }
|
||||||
|
public boolean isFoil() { return foiled; }
|
||||||
|
public CardRules getCard() { return card; }
|
||||||
|
public CardRarity getRarity() { return rarity; }
|
||||||
|
|
||||||
|
|
||||||
|
// Lambda to get rules for selects from list of printed cards
|
||||||
|
public static final Lambda1<CardRules, CardPrinted> fnGetRules = new Lambda1<CardRules, CardPrinted>() {
|
||||||
|
@Override public CardRules apply(final CardPrinted from) { return from.card; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructor is private. All non-foiled instances are stored in CardDb
|
||||||
|
private CardPrinted(final CardRules c, final String set, final CardRarity rare, final int index, boolean foil) {
|
||||||
|
card = c;
|
||||||
|
name = c.getName();
|
||||||
|
cardSet = set;
|
||||||
|
artIndex = index;
|
||||||
|
foiled = foil;
|
||||||
|
rarity = rare;
|
||||||
|
_name_lcase = name.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package visibility */
|
||||||
|
static CardPrinted build(final CardRules c, final String set, final CardRarity rare, final int index) {
|
||||||
|
return new CardPrinted(c, set, rare, index, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* foiled don't need to stay in CardDb's structures, so u'r free to create */
|
||||||
|
public static CardPrinted makeFoiled(final CardPrinted c) {
|
||||||
|
return new CardPrinted(c.card, c.cardSet, c.rarity, c.artIndex, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Want this class to be a key for HashTable
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) { return true; }
|
||||||
|
if (obj == null) { return false; }
|
||||||
|
if (getClass() != obj.getClass()) { return false; }
|
||||||
|
|
||||||
|
CardPrinted other = (CardPrinted) obj;
|
||||||
|
if (!name.equals(other.name)) { return false; }
|
||||||
|
if (!cardSet.equals(other.cardSet)) { return false; }
|
||||||
|
if (other.foiled != this.foiled || other.artIndex != this.artIndex) { return false; }
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int code = _name_lcase.hashCode() * 11 + cardSet.hashCode() * 59 + artIndex * 2;
|
||||||
|
if (foiled) { return code + 1; }
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%s|%s", name, cardSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Card toForgeCard() {
|
||||||
|
Card c = AllZone.getCardFactory().getCard(name, null);
|
||||||
|
c.setCurSetCode(getSet());
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CardPrinted o) {
|
||||||
|
int nameCmp = _name_lcase.compareTo(o._name_lcase);
|
||||||
|
if (0 != nameCmp) { return nameCmp; }
|
||||||
|
// TODO: compare sets properly
|
||||||
|
return cardSet.compareTo(o.cardSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static abstract class Predicates {
|
||||||
|
public static Predicate<CardPrinted> rarity(final boolean isEqual, final CardRarity value)
|
||||||
|
{
|
||||||
|
return new PredicateRarity(value, isEqual);
|
||||||
|
}
|
||||||
|
public static Predicate<CardPrinted> printedInSets(final String[] value)
|
||||||
|
{
|
||||||
|
return new PredicateSets(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PredicateRarity extends Predicate<CardPrinted> {
|
||||||
|
private final CardRarity operand;
|
||||||
|
private final boolean shouldBeEqual;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTrue(final CardPrinted card) {
|
||||||
|
return card.rarity.equals(operand) == shouldBeEqual;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PredicateRarity(final CardRarity type, final boolean wantEqual) {
|
||||||
|
operand = type;
|
||||||
|
shouldBeEqual = wantEqual;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PredicateSets extends Predicate<CardPrinted> {
|
||||||
|
private final String[] sets;
|
||||||
|
@Override public boolean isTrue(final CardPrinted card) {
|
||||||
|
return Arrays.binarySearch(sets, card.rarity) >= 0;
|
||||||
|
}
|
||||||
|
public PredicateSets(final String[] wantSets) {
|
||||||
|
sets = wantSets.clone();
|
||||||
|
Arrays.sort(sets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class Presets {
|
||||||
|
// Think twice before using these, since rarity is a prop of printed card.
|
||||||
|
public static final Predicate<CardPrinted> isCommon = rarity(true, CardRarity.Common);
|
||||||
|
public static final Predicate<CardPrinted> isUncommon = rarity(true, CardRarity.Uncommon);
|
||||||
|
public static final Predicate<CardPrinted> isRare = rarity(true, CardRarity.Rare);
|
||||||
|
public static final Predicate<CardPrinted> isMythicRare = rarity(true, CardRarity.MythicRare);
|
||||||
|
public static final Predicate<CardPrinted> isRareOrMythic = Predicate.or(isRare, isMythicRare);
|
||||||
|
|
||||||
|
public static final Predicate<CardPrinted> isSpecial = rarity(true, CardRarity.Special);
|
||||||
|
|
||||||
|
public static final Predicate<CardPrinted> exceptLands = rarity(false, CardRarity.BasicLand);
|
||||||
|
|
||||||
|
// TODO: Update this code on each rotation (or move this list to a file)
|
||||||
|
public static final Predicate<CardPrinted> isStandard = printedInSets(
|
||||||
|
new String[] {"M12", "NPH", "MBS", "SOM", "M11", "ROE", "WWK", "ZEN"});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/main/java/forge/card/CardRarity.java
Normal file
28
src/main/java/forge/card/CardRarity.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CardRarity class.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardRarity.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum CardRarity {
|
||||||
|
BasicLand(0, "L"),
|
||||||
|
Common(1, "C"),
|
||||||
|
Uncommon(2, "U"),
|
||||||
|
Rare(3, "R"),
|
||||||
|
MythicRare(4, "M"),
|
||||||
|
Special(10, "S"); // Timeshifted
|
||||||
|
|
||||||
|
private final int rating;
|
||||||
|
private final String strValue;
|
||||||
|
private CardRarity(final int value, final String sValue) {
|
||||||
|
rating = value;
|
||||||
|
strValue = sValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() { return strValue; }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package forge.card;
|
|
||||||
|
|
||||||
import java.security.InvalidParameterException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import forge.AllZone;
|
|
||||||
import forge.Card;
|
|
||||||
import forge.SetInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>CardReference class.</p>
|
|
||||||
*
|
|
||||||
* @author Forge
|
|
||||||
* @version $Id: CardReference.java 9708 2011-08-09 19:34:12Z jendave $
|
|
||||||
*/
|
|
||||||
public final class CardReference {
|
|
||||||
private static final boolean ENABLE_CONSISTENCY_CHECK = true;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
private String cardSet;
|
|
||||||
private short pictureNumber = 0;
|
|
||||||
private boolean foiled = false;
|
|
||||||
|
|
||||||
public CardReference(final String named, final String set, final int picNum, final boolean foil) {
|
|
||||||
this(named, set, picNum);
|
|
||||||
foiled = foil;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CardReference(final String named, final String set, final int picNum) {
|
|
||||||
this(named, set);
|
|
||||||
pictureNumber = (short) picNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CardReference(final String named, final String set) {
|
|
||||||
name = named;
|
|
||||||
cardSet = set;
|
|
||||||
|
|
||||||
if (set == null || ENABLE_CONSISTENCY_CHECK) {
|
|
||||||
Card c = AllZone.getCardFactory().getCard(name, null);
|
|
||||||
if (c == null) {
|
|
||||||
String error = String.format("Invalid reference! The card named '%s' is unknown to Forge", name);
|
|
||||||
throw new InvalidParameterException(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<SetInfo> validSets = c.getSets();
|
|
||||||
boolean isSetValid = false;
|
|
||||||
if (cardSet != null) {
|
|
||||||
for (SetInfo si : validSets) {
|
|
||||||
if (si.Code.equals(set)) { isSetValid = true; break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isSetValid) {
|
|
||||||
cardSet = c.getMostRecentSet();
|
|
||||||
// String error = String.format("The card '%s' is not a part of '%s' set", name, set);
|
|
||||||
// throw new InvalidParameterException(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CardReference(final String named) { this(named, (String) null); }
|
|
||||||
|
|
||||||
public String getName() { return name; }
|
|
||||||
public String getSet() { return cardSet; }
|
|
||||||
public short getPictureIndex() { return pictureNumber; }
|
|
||||||
public boolean isFoil() { return foiled; }
|
|
||||||
|
|
||||||
// Want this class to be a key for HashTable
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) { return true; }
|
|
||||||
if (obj == null) { return false; }
|
|
||||||
if (getClass() != obj.getClass()) { return false; }
|
|
||||||
|
|
||||||
CardReference other = (CardReference) obj;
|
|
||||||
if (!name.equals(other.name)) { return false; }
|
|
||||||
if (!cardSet.equals(other.cardSet)) { return false; }
|
|
||||||
if (other.foiled != this.foiled || other.pictureNumber != this.pictureNumber) { return false; }
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int code = name.hashCode() * 11 + cardSet.hashCode() * 59 + pictureNumber * 2;
|
|
||||||
if (foiled) { return code + 1; }
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
348
src/main/java/forge/card/CardRules.java
Normal file
348
src/main/java/forge/card/CardRules.java
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.slightlymagic.maxmtg.Predicate;
|
||||||
|
import net.slightlymagic.maxmtg.Predicate.ComparableOp;
|
||||||
|
import net.slightlymagic.maxmtg.Predicate.PredicatesOp;
|
||||||
|
import net.slightlymagic.maxmtg.Predicate.StringOp;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CardOracle class.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardOracle.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
public final class CardRules {
|
||||||
|
private final String name;
|
||||||
|
private final CardType type;
|
||||||
|
private final CardManaCost cost;
|
||||||
|
private CardColor color = null; // color is subject to change yet (parse %cardname% is %color% rule)
|
||||||
|
private final String[] rules;
|
||||||
|
|
||||||
|
private int iPower = -1;
|
||||||
|
private int iToughness = -1;
|
||||||
|
private String power = null;
|
||||||
|
private String toughness = null;
|
||||||
|
|
||||||
|
private String loyalty = null;
|
||||||
|
|
||||||
|
private HashMap<String, CardInSet> setsPrinted = null;
|
||||||
|
|
||||||
|
// Ctor and builders are needed here
|
||||||
|
public String getName() { return name; }
|
||||||
|
public CardType getType() { return type; }
|
||||||
|
public CardManaCost getManaCost() { return cost; }
|
||||||
|
public CardColor getColor() { return color; }
|
||||||
|
public String[] getRules() { return rules; }
|
||||||
|
public Set<Entry<String, CardInSet>> getSetsPrinted() { return setsPrinted.entrySet(); }
|
||||||
|
|
||||||
|
public String getPower() { return power; }
|
||||||
|
public int getIntPower() { return iPower; }
|
||||||
|
public String getToughness() { return toughness; }
|
||||||
|
public int getIntToughness() { return iToughness; }
|
||||||
|
public String getLoyalty() { return loyalty; }
|
||||||
|
|
||||||
|
public String getPTorLoyalty() {
|
||||||
|
if (getType().isCreature()) { return power + "/" + toughness; }
|
||||||
|
if (getType().isPlaneswalker()) { return loyalty; }
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardRules(final String cardName, final CardType cardType, final String manacost,
|
||||||
|
final String ptLine, final String[] cardRules, final String[] setsData)
|
||||||
|
{
|
||||||
|
this.name = cardName;
|
||||||
|
this.type = cardType;
|
||||||
|
this.cost = manacost == null ? CardManaCost.empty : new CardManaCost(manacost);
|
||||||
|
this.rules = cardRules;
|
||||||
|
this.color = new CardColor(cost);
|
||||||
|
if (cardType.isCreature()) {
|
||||||
|
int slashPos = ptLine.indexOf('/');
|
||||||
|
if (slashPos == -1) {
|
||||||
|
throw new RuntimeException(String.format("Creature '%s' has bad p/t stats", cardName));
|
||||||
|
}
|
||||||
|
this.power = ptLine.substring(0, slashPos);
|
||||||
|
this.toughness = ptLine.substring(slashPos + 1, ptLine.length());
|
||||||
|
this.iPower = StringUtils.isNumeric(power) ? Integer.parseInt(power) : 0;
|
||||||
|
this.iToughness = StringUtils.isNumeric(toughness) ? Integer.parseInt(toughness) : 0;
|
||||||
|
} else if (cardType.isPlaneswalker()) {
|
||||||
|
this.loyalty = ptLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setsPrinted = new HashMap<String, CardInSet>();
|
||||||
|
for (int iSet = 0; iSet < setsData.length; iSet++) {
|
||||||
|
String setCode = setsData[iSet].substring(0, setsData[iSet].indexOf(' '));
|
||||||
|
setsPrinted.put(setCode, CardInSet.parse(setsData[iSet]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean rulesContain(final String text) {
|
||||||
|
for (String r : rules) { if (r.contains(text)) { return true; } }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public String getLatestSetPrinted() {
|
||||||
|
String lastSet = null;
|
||||||
|
// TODO: Make a true release-date based sorting
|
||||||
|
for (String cs : setsPrinted.keySet()) {
|
||||||
|
lastSet = cs;
|
||||||
|
}
|
||||||
|
return lastSet;
|
||||||
|
}
|
||||||
|
public CardInSet getSetInfo(final String setCode) {
|
||||||
|
CardInSet result = setsPrinted.get(setCode);
|
||||||
|
if (result != null) { return result; }
|
||||||
|
throw new RuntimeException(String.format("Card '%s' was never printed in set '%s'", name, setCode));
|
||||||
|
|
||||||
|
}
|
||||||
|
public CardRarity getRarityFromLatestSet() {
|
||||||
|
CardInSet cis = setsPrinted.get(getLatestSetPrinted());
|
||||||
|
return cis.getRarity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class Predicates {
|
||||||
|
|
||||||
|
// Static builder methods - they choose concrete implementation by themselves
|
||||||
|
public static Predicate<CardRules> cmc(final ComparableOp op, final int what)
|
||||||
|
{
|
||||||
|
return new LeafNumber(LeafNumber.CardField.CMC, op, what);
|
||||||
|
}
|
||||||
|
// Power
|
||||||
|
// Toughness
|
||||||
|
public static Predicate<CardRules> rules(final StringOp op, final String what)
|
||||||
|
{
|
||||||
|
return new LeafString(LeafString.CardField.RULES, op, what);
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> name(final StringOp op, final String what)
|
||||||
|
{
|
||||||
|
return new LeafString(LeafString.CardField.NAME, op, what);
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> subType(final StringOp op, final String what)
|
||||||
|
{
|
||||||
|
return new LeafString(LeafString.CardField.SUBTYPE, op, what);
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> coreType(final boolean isEqual, final String what)
|
||||||
|
{
|
||||||
|
try { return coreType(isEqual, CardCoreType.valueOf(CardCoreType.class, what)); }
|
||||||
|
catch (Exception e) { return Predicate.getFalse(CardRules.class); }
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> coreType(final boolean isEqual, final CardCoreType type)
|
||||||
|
{
|
||||||
|
return new PredicateCoreType(type, isEqual);
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> superType(final boolean isEqual, final String what)
|
||||||
|
{
|
||||||
|
try { return superType(isEqual, CardSuperType.valueOf(CardSuperType.class, what)); }
|
||||||
|
catch (Exception e) { return Predicate.getFalse(CardRules.class); }
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> superType(final boolean isEqual, final CardSuperType type)
|
||||||
|
{
|
||||||
|
return new PredicateSuperType(type, isEqual);
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> rarityInCardsLatestSet(final boolean isEqual, final CardRarity value)
|
||||||
|
{
|
||||||
|
return new PredicateLastesSetRarity(value, isEqual);
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> hasColor(final byte thatColor) {
|
||||||
|
return new LeafColor(LeafColor.ColorOperator.HasAllOf, thatColor);
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> isColor(final byte thatColor) {
|
||||||
|
return new LeafColor(LeafColor.ColorOperator.HasAnyOf, thatColor);
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> hasCntColors(final byte cntColors) {
|
||||||
|
return new LeafColor(LeafColor.ColorOperator.Equals, cntColors);
|
||||||
|
}
|
||||||
|
public static Predicate<CardRules> hasAtLeastCntColors(final byte cntColors) {
|
||||||
|
return new LeafColor(LeafColor.ColorOperator.CountColorsGreaterOrEqual, cntColors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LeafString extends Predicate<CardRules> {
|
||||||
|
public enum CardField {
|
||||||
|
RULES,
|
||||||
|
NAME,
|
||||||
|
SUBTYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String operand;
|
||||||
|
private final StringOp operator;
|
||||||
|
private final CardField field;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTrue(final CardRules card) {
|
||||||
|
boolean shouldConatin;
|
||||||
|
switch (field) {
|
||||||
|
case NAME:
|
||||||
|
return op(card.getName(), operand);
|
||||||
|
case SUBTYPE:
|
||||||
|
shouldConatin = operator == StringOp.CONTAINS || operator == StringOp.EQUALS;
|
||||||
|
return shouldConatin == card.getType().subTypeContains(operand);
|
||||||
|
case RULES:
|
||||||
|
shouldConatin = operator == StringOp.CONTAINS || operator == StringOp.EQUALS;
|
||||||
|
return shouldConatin == card.rulesContain(operand);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean op(final String op1, final String op2) {
|
||||||
|
if (operator == StringOp.CONTAINS) { return op1.contains(op2); }
|
||||||
|
if (operator == StringOp.NOT_CONTAINS) { return op1.contains(op2); }
|
||||||
|
if (operator == StringOp.EQUALS) { return op1.equals(op2); }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LeafString(final CardField field, final StringOp operator, final String operand)
|
||||||
|
{
|
||||||
|
this.field = field;
|
||||||
|
this.operand = operand;
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LeafColor extends Predicate<CardRules> {
|
||||||
|
public enum ColorOperator {
|
||||||
|
CountColors,
|
||||||
|
CountColorsGreaterOrEqual,
|
||||||
|
HasAnyOf,
|
||||||
|
HasAllOf,
|
||||||
|
Equals
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ColorOperator op;
|
||||||
|
private final byte color;
|
||||||
|
|
||||||
|
public LeafColor(final ColorOperator operator, final byte thatColor)
|
||||||
|
{
|
||||||
|
op = operator;
|
||||||
|
color = thatColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTrue(final CardRules subject) {
|
||||||
|
switch(op) {
|
||||||
|
case CountColors: return subject.getColor().countColors() == color;
|
||||||
|
case CountColorsGreaterOrEqual: return subject.getColor().countColors() >= color;
|
||||||
|
case Equals: return subject.getColor().isEqual(color);
|
||||||
|
case HasAllOf: return subject.getColor().hasAllColors(color);
|
||||||
|
case HasAnyOf: return subject.getColor().hasAnyColor(color);
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LeafNumber extends Predicate<CardRules> {
|
||||||
|
protected enum CardField {
|
||||||
|
CMC,
|
||||||
|
POWER,
|
||||||
|
TOUGHNESS,
|
||||||
|
}
|
||||||
|
|
||||||
|
private final CardField field;
|
||||||
|
private final ComparableOp operator;
|
||||||
|
private final int operand;
|
||||||
|
|
||||||
|
public LeafNumber(final CardField field, final ComparableOp op, final int what) {
|
||||||
|
this.field = field;
|
||||||
|
operand = what;
|
||||||
|
operator = op;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTrue(final CardRules card) {
|
||||||
|
int value;
|
||||||
|
switch (field) {
|
||||||
|
case CMC: return op(card.getManaCost().getCMC(), operand);
|
||||||
|
case POWER: value = card.getIntPower(); return value >= 0 ? op(value, operand) : false;
|
||||||
|
case TOUGHNESS: value = card.getIntToughness(); return value >= 0 ? op(value, operand) : false;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean op(final int op1, final int op2) {
|
||||||
|
switch (operator) {
|
||||||
|
case EQUALS: return op1 == op2;
|
||||||
|
case GREATER_THAN: return op1 > op2;
|
||||||
|
case GT_OR_EQUAL: return op1 >= op2;
|
||||||
|
case LESS_THAN: return op1 < op2;
|
||||||
|
case LT_OR_EQUAL: return op1 <= op2;
|
||||||
|
case NOT_EQUALS: return op1 != op2;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PredicateCoreType extends Predicate<CardRules> {
|
||||||
|
private final CardCoreType operand;
|
||||||
|
private final boolean shouldBeEqual;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTrue(final CardRules card) { return shouldBeEqual == card.getType().typeContains(operand); }
|
||||||
|
|
||||||
|
public PredicateCoreType(final CardCoreType type, final boolean wantEqual) {
|
||||||
|
operand = type;
|
||||||
|
shouldBeEqual = wantEqual;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PredicateSuperType extends Predicate<CardRules> {
|
||||||
|
private final CardSuperType operand;
|
||||||
|
private final boolean shouldBeEqual;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTrue(final CardRules card) {
|
||||||
|
return shouldBeEqual == card.getType().superTypeContains(operand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PredicateSuperType(final CardSuperType type, final boolean wantEqual) {
|
||||||
|
operand = type;
|
||||||
|
shouldBeEqual = wantEqual;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static class PredicateLastesSetRarity extends Predicate<CardRules> {
|
||||||
|
private final CardRarity operand;
|
||||||
|
private final boolean shouldBeEqual;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTrue(final CardRules card) {
|
||||||
|
return card.getRarityFromLatestSet().equals(operand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PredicateLastesSetRarity(final CardRarity type, final boolean wantEqual) {
|
||||||
|
operand = type;
|
||||||
|
shouldBeEqual = wantEqual;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Presets {
|
||||||
|
public static final Predicate<CardRules> isCreature = coreType(true, CardCoreType.Creature);
|
||||||
|
public static final Predicate<CardRules> isArtifact = coreType(true, CardCoreType.Artifact);
|
||||||
|
public static final Predicate<CardRules> isLand = coreType(true, CardCoreType.Land);
|
||||||
|
public static final Predicate<CardRules> isPlaneswalker = coreType(true, CardCoreType.Planeswalker);
|
||||||
|
public static final Predicate<CardRules> isInstant = coreType(true, CardCoreType.Instant);
|
||||||
|
public static final Predicate<CardRules> isSorcery = coreType(true, CardCoreType.Sorcery);
|
||||||
|
public static final Predicate<CardRules> isEnchantment = coreType(true, CardCoreType.Enchantment);
|
||||||
|
|
||||||
|
public static final Predicate<CardRules> isNonLand = coreType(false, CardCoreType.Land);
|
||||||
|
public static final Predicate<CardRules> isNonCreatureSpell = Predicate.compose(isCreature, PredicatesOp.NOR, isLand);
|
||||||
|
|
||||||
|
public static final Predicate<CardRules> isWhite = isColor(CardColor.WHITE);
|
||||||
|
public static final Predicate<CardRules> isBlue = isColor(CardColor.BLUE);
|
||||||
|
public static final Predicate<CardRules> isBlack = isColor(CardColor.BLACK);
|
||||||
|
public static final Predicate<CardRules> isRed = isColor(CardColor.RED);
|
||||||
|
public static final Predicate<CardRules> isGreen = isColor(CardColor.GREEN);
|
||||||
|
public static final Predicate<CardRules> isColorless = hasCntColors((byte) 0);
|
||||||
|
public static final Predicate<CardRules> isMulticolor = hasAtLeastCntColors((byte) 2);
|
||||||
|
|
||||||
|
// Think twice before using these, since rarity is a prop of printed card.
|
||||||
|
public static final Predicate<CardRules> isInLatestSetCommon = rarityInCardsLatestSet(true, CardRarity.Common);
|
||||||
|
public static final Predicate<CardRules> isInLatestSetUncommon = rarityInCardsLatestSet(true, CardRarity.Uncommon);
|
||||||
|
public static final Predicate<CardRules> isInLatestSetRare = rarityInCardsLatestSet(true, CardRarity.Rare);
|
||||||
|
public static final Predicate<CardRules> isInLatestSetMythicRare = rarityInCardsLatestSet(true, CardRarity.MythicRare);
|
||||||
|
public static final Predicate<CardRules> isInLatestSetSpecial = rarityInCardsLatestSet(true, CardRarity.Special);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/main/java/forge/card/CardSet.java
Normal file
47
src/main/java/forge/card/CardSet.java
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>CardSet class.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardSet.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
public final class CardSet implements Comparable<CardSet> { // immutable
|
||||||
|
private int index;
|
||||||
|
private String code;
|
||||||
|
private String code2;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public CardSet(final int index, final String name, final String code, final String code2) {
|
||||||
|
this.code = code;
|
||||||
|
this.code2 = code2;
|
||||||
|
this.index = index;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public String getCode() { return code; }
|
||||||
|
public String getCode2() { return code2; }
|
||||||
|
public int getIndex() { return index; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CardSet o) {
|
||||||
|
if (o == null) { return 1; }
|
||||||
|
return o.index - this.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return code.hashCode() * 17 + name.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) { return true; }
|
||||||
|
if (obj == null) { return false; }
|
||||||
|
if (getClass() != obj.getClass()) { return false; }
|
||||||
|
|
||||||
|
CardSet other = (CardSet) obj;
|
||||||
|
return other.name.equals(this.name) && this.code.equals(other.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/main/java/forge/card/CardSuperType.java
Normal file
6
src/main/java/forge/card/CardSuperType.java
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
public enum CardSuperType
|
||||||
|
{
|
||||||
|
Basic, Legendary, Show, Ongoing, World
|
||||||
|
}
|
||||||
113
src/main/java/forge/card/CardType.java
Normal file
113
src/main/java/forge/card/CardType.java
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Immutable Card type. Can be build only from parsing a string.</p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardType.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
|
||||||
|
public final class CardType implements Comparable<CardType> {
|
||||||
|
private List<String> subType = new ArrayList<String>();
|
||||||
|
private EnumSet<CardCoreType> coreType = EnumSet.noneOf(CardCoreType.class);
|
||||||
|
private EnumSet<CardSuperType> superType = EnumSet.noneOf(CardSuperType.class);
|
||||||
|
private String calculatedType = null; // since obj is immutable, this is calc'd once
|
||||||
|
|
||||||
|
// This will be useful for faster parses
|
||||||
|
private static HashMap<String, CardCoreType> stringToCoreType = new HashMap<String, CardCoreType>();
|
||||||
|
private static HashMap<String, CardSuperType> stringToSuperType = new HashMap<String, CardSuperType>();
|
||||||
|
static {
|
||||||
|
for (CardSuperType st : CardSuperType.values()) { stringToSuperType.put(st.name(), st); }
|
||||||
|
for (CardCoreType ct : CardCoreType.values()) { stringToCoreType.put(ct.name(), ct); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardType() { } // use static ctors!
|
||||||
|
|
||||||
|
// TODO: Debug this code
|
||||||
|
public static CardType parse(final String typeText) {
|
||||||
|
// Most types and subtypes, except "Serra<72>s Realm" and "Bolas<61>s Meditation Realm" consist of only one word
|
||||||
|
final char space = ' ';
|
||||||
|
CardType result = new CardType();
|
||||||
|
|
||||||
|
int iTypeStart = 0;
|
||||||
|
int iSpace = typeText.indexOf(space);
|
||||||
|
boolean hasMoreTypes = typeText.length() > 0;
|
||||||
|
while (hasMoreTypes) {
|
||||||
|
String type = typeText.substring(iTypeStart, iSpace == -1 ? typeText.length() : iSpace );
|
||||||
|
if (!isMultiwordType(type)) {
|
||||||
|
iTypeStart = iSpace + 1;
|
||||||
|
result.parseAndAdd(type);
|
||||||
|
}
|
||||||
|
hasMoreTypes = iSpace != -1;
|
||||||
|
iSpace = typeText.indexOf(space, iSpace + 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMultiwordType(final String type) {
|
||||||
|
final String[] multiWordTypes = {"Serra's Realm", "Bolas's Meditation Realm"};
|
||||||
|
// no need to loop for only 2 exceptions!
|
||||||
|
if (multiWordTypes[0].startsWith(type) && !multiWordTypes[0].equals(type)) { return true; }
|
||||||
|
if (multiWordTypes[1].startsWith(type) && !multiWordTypes[1].equals(type)) { return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseAndAdd(final String type) {
|
||||||
|
if ("-".equals(type)) { return; }
|
||||||
|
|
||||||
|
CardCoreType ct = stringToCoreType.get(type);
|
||||||
|
if (ct != null) { coreType.add(ct); return; }
|
||||||
|
|
||||||
|
CardSuperType st = stringToSuperType.get(type);
|
||||||
|
if (st != null) { superType.add(st); return; }
|
||||||
|
|
||||||
|
// If not recognized by super- and core- this must be subtype
|
||||||
|
subType.add(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean subTypeContains(final String operand) { return subType.contains(operand); }
|
||||||
|
public boolean typeContains(final CardCoreType operand) { return coreType.contains(operand); }
|
||||||
|
public boolean superTypeContains(final CardSuperType operand) { return superType.contains(operand); }
|
||||||
|
|
||||||
|
public boolean isCreature() { return coreType.contains(CardCoreType.Creature); }
|
||||||
|
public boolean isPlaneswalker() { return coreType.contains(CardCoreType.Planeswalker); }
|
||||||
|
public boolean isLand() { return coreType.contains(CardCoreType.Land); }
|
||||||
|
public boolean isArtifact() { return coreType.contains(CardCoreType.Artifact); }
|
||||||
|
public boolean isInstant() { return coreType.contains(CardCoreType.Instant); }
|
||||||
|
public boolean isSorcery() { return coreType.contains(CardCoreType.Sorcery); }
|
||||||
|
public boolean isEnchantment() { return coreType.contains(CardCoreType.Enchantment); }
|
||||||
|
|
||||||
|
public String getTypesBeforeDash() {
|
||||||
|
ArrayList<String> types = new ArrayList<String>();
|
||||||
|
for (CardSuperType st : superType) { types.add(st.name()); }
|
||||||
|
for (CardCoreType ct : coreType) { types.add(ct.name()); }
|
||||||
|
return StringUtils.join(types, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTypesAfterDash() {
|
||||||
|
return StringUtils.join(subType, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (null == calculatedType) { calculatedType = toStringImpl(); }
|
||||||
|
return calculatedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toStringImpl() {
|
||||||
|
if (subType.isEmpty()) { return getTypesBeforeDash(); }
|
||||||
|
else { return String.format("%s - %s", getTypesBeforeDash(), getTypesAfterDash()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CardType o) {
|
||||||
|
return toString().compareTo(o.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
68
src/main/java/forge/card/MtgDataParser.java
Normal file
68
src/main/java/forge/card/MtgDataParser.java
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.naming.OperationNotSupportedException;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
public final class MtgDataParser implements Iterator<CardRules> {
|
||||||
|
|
||||||
|
private Iterator<String> it;
|
||||||
|
public MtgDataParser(final Iterable<String> data) {
|
||||||
|
it = data.iterator();
|
||||||
|
skipSetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean weHaveNext;
|
||||||
|
private void skipSetList() {
|
||||||
|
String nextLine = it.next();
|
||||||
|
while (nextLine.length() > 0 && it.hasNext()) {
|
||||||
|
nextLine = it.next();
|
||||||
|
}
|
||||||
|
weHaveNext = it.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() { return weHaveNext; }
|
||||||
|
|
||||||
|
private static final String[] emptyArray = new String[0]; // list.toArray() needs this =(
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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;
|
||||||
|
if (manaCost.startsWith("{")) {
|
||||||
|
if (!it.hasNext()) { weHaveNext = false; return null; }
|
||||||
|
type = CardType.parse(it.next());
|
||||||
|
} else { // Land?
|
||||||
|
type = CardType.parse(manaCost);
|
||||||
|
manaCost = null;
|
||||||
|
}
|
||||||
|
String ptOrLoyalty = null;
|
||||||
|
if (type.isCreature() || type.isPlaneswalker()) {
|
||||||
|
if (!it.hasNext()) { weHaveNext = false; return null; }
|
||||||
|
ptOrLoyalty = it.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> strs = new ArrayList<String>();
|
||||||
|
if (!it.hasNext()) { weHaveNext = false; return null; }
|
||||||
|
String nextLine = it.next();
|
||||||
|
while (StringUtils.isNotBlank(nextLine) && it.hasNext()) {
|
||||||
|
strs.add(nextLine);
|
||||||
|
nextLine = it.next();
|
||||||
|
}
|
||||||
|
String[] sets = strs.remove(strs.size() - 1).split(", ");
|
||||||
|
|
||||||
|
return new CardRules(name, type, manaCost, ptOrLoyalty, strs.toArray(emptyArray), sets);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void remove() { }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -101,6 +101,8 @@ public interface NewConstants {
|
|||||||
/** Constant <code>BOOSTERDATA="boosterdata"</code>. */
|
/** Constant <code>BOOSTERDATA="boosterdata"</code>. */
|
||||||
String BOOSTERDATA = "boosterdata";
|
String BOOSTERDATA = "boosterdata";
|
||||||
|
|
||||||
|
String MTG_DATA = "mtg-data";
|
||||||
|
|
||||||
/** Constant <code>IMAGE_BASE="image/base"</code>. */
|
/** Constant <code>IMAGE_BASE="image/base"</code>. */
|
||||||
String IMAGE_BASE = "image/base";
|
String IMAGE_BASE = "image/base";
|
||||||
/** Constant <code>IMAGE_TOKEN="image/token"</code>. */
|
/** Constant <code>IMAGE_TOKEN="image/token"</code>. */
|
||||||
|
|||||||
Reference in New Issue
Block a user