moved cardFace and rules reader to core

This commit is contained in:
Maxmtg
2013-11-19 20:38:51 +00:00
parent 851094c523
commit 65199c7a81
9 changed files with 285 additions and 324 deletions

View File

@@ -0,0 +1,117 @@
package forge.card;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import forge.card.mana.ManaCost;
//
// DO NOT AUTOFORMAT / CHECKSTYLE THIS FILE
//
/**
* Represents a single side or part of a magic card with its original characteristics.
* <br><br>
* <i>Do not use reference to class except for card parsing.<br>Always use reference to interface type outside of package.</i>
*/
final class CardFace implements ICardFace {
private final static List<String> emptyList = Collections.unmodifiableList(new ArrayList<String>());
private final static Map<String, String> emptyMap = Collections.unmodifiableMap(new TreeMap<String, String>());
private final String name;
private CardType type = null;
private ManaCost manaCost = ManaCost.NO_COST;
private ColorSet color = null;
private String oracleText = null;
private int iPower = -1;
private int iToughness = -1;
private String power = null;
private String toughness = null;
private int initialLoyalty = -1;
private String nonAbilityText = null;
private List<String> keywords = null;
private List<String> abilities = null;
private List<String> staticAbilities = null;
private List<String> triggers = null;
private List<String> replacements = null;
private Map<String, String> variables = null;
// these implement ICardCharacteristics
@Override public String getOracleText() { return oracleText; }
@Override public int getIntPower() { return iPower; }
@Override public int getIntToughness() { return iToughness; }
@Override public String getPower() { return power; }
@Override public String getToughness() { return toughness; }
@Override public int getInitialLoyalty() { return initialLoyalty; }
@Override public String getName() { return this.name; }
@Override public CardType getType() { return this.type; }
@Override public ManaCost getManaCost() { return this.manaCost; }
@Override public ColorSet getColor() { return this.color; }
// these are raw and unparsed used for Card creation
@Override public Iterable<String> getKeywords() { return keywords; }
@Override public Iterable<String> getAbilities() { return abilities; }
@Override public Iterable<String> getStaticAbilities() { return staticAbilities; }
@Override public Iterable<String> getTriggers() { return triggers; }
@Override public Iterable<String> getReplacements() { return replacements; }
@Override public String getNonAbilityText() { return nonAbilityText; }
@Override public Iterable<Entry<String, String>> getVariables() { return variables.entrySet(); }
public CardFace(String name0) {
this.name = name0;
if ( StringUtils.isBlank(name0) )
throw new RuntimeException("Card name is empty");
}
// Here come setters to allow parser supply values
public void setType(CardType type0) { this.type = type0; }
public void setManaCost(ManaCost manaCost0) { this.manaCost = manaCost0; }
public void setColor(ColorSet color0) { this.color = color0; }
public void setOracleText(String text) { this.oracleText = text; }
public void setInitialLoyalty(int value) { this.initialLoyalty = value; }
public void setPtText(String value) {
final int slashPos = value.indexOf('/');
if (slashPos == -1) {
throw new RuntimeException(String.format("Creature '%s' has bad p/t stats", this.getName()));
}
this.power = value.substring(0, slashPos);
this.toughness = value.substring(slashPos + 1);
this.iPower = StringUtils.isNumeric(this.power) ? Integer.parseInt(this.power) : 0;
this.iToughness = StringUtils.isNumeric(this.toughness) ? Integer.parseInt(this.toughness) : 0;
}
// Raw fields used for Card creation
public void setNonAbilityText(String value) { this.nonAbilityText = value; }
public void addKeyword(String value) { if (null == this.keywords) { this.keywords = new ArrayList<String>(); } this.keywords.add(value); }
public void addAbility(String value) { if (null == this.abilities) { this.abilities = new ArrayList<String>(); } this.abilities.add(value);}
public void addTrigger(String value) { if (null == this.triggers) { this.triggers = new ArrayList<String>(); } this.triggers.add(value);}
public void addStaticAbility(String value) { if (null == this.staticAbilities) { this.staticAbilities = new ArrayList<String>(); } this.staticAbilities.add(value);}
public void addReplacementEffect(String value) { if (null == this.replacements) { this.replacements = new ArrayList<String>(); } this.replacements.add(value);}
public void addSVar(String key, String value) { if (null == this.variables) { this.variables = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER); } this.variables.put(key, value); }
public void assignMissingFields() { // Most scripts do not specify color explicitly
if ( null == oracleText ) { System.err.println(name + " has no Oracle text."); oracleText = ""; }
if ( manaCost == null && color == null ) System.err.println(name + " has neither ManaCost nor Color");
if ( color == null ) color = ColorSet.fromManaCost(manaCost);
if ( keywords == null ) keywords = emptyList;
if ( abilities == null ) abilities = emptyList;
if ( staticAbilities == null ) staticAbilities = emptyList;
if ( triggers == null ) triggers = emptyList;
if ( replacements == null ) replacements = emptyList;
if ( variables == null ) variables = emptyMap;
if ( null == nonAbilityText ) nonAbilityText = "";
}
}

View File

@@ -18,7 +18,13 @@
package forge.card;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.commons.lang3.StringUtils;
import forge.card.mana.IParserManaCost;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostShard;
/**
* A collection of methods containing full
@@ -37,7 +43,7 @@ public final class CardRules implements ICardCharacteristics {
private ColorSet colorIdentity = null;
public CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
private CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
splitType = altMode;
mainPart = faces[0];
otherPart = faces[1];
@@ -219,4 +225,265 @@ public final class CardRules implements ICardCharacteristics {
return colorIdentity;
}
/** Instantiates class, reads a card. For batch operations better create you own reader instance. */
public static CardRules fromScript(Iterable<String> script) {
Reader crr = new Reader();
for(String line : script) {
crr.parseLine(line);
}
return crr.getCard();
}
// Reads cardname.txt
public static class Reader {
// fields to build
private CardFace[] faces = new CardFace[] { null, null };
private String[] pictureUrl = new String[] { null, null };
private int curFace = 0;
private CardSplitType altMode = CardSplitType.None;
private String handLife = null;
// fields to build CardAiHints
private boolean removedFromAIDecks = false;
private boolean removedFromRandomDecks = false;
private DeckHints hints = null;
private DeckHints needs = null;
/**
* Reset all fields to parse next card (to avoid allocating new CardRulesReader N times)
*/
public final void reset() {
this.curFace = 0;
this.faces[0] = null;
this.faces[1] = null;
this.pictureUrl[0] = null;
this.pictureUrl[1] = null;
this.handLife = null;
this.altMode = CardSplitType.None;
this.removedFromAIDecks = false;
this.removedFromRandomDecks = false;
this.needs = null;
this.hints = null;
}
/**
* Gets the card.
*
* @return the card
*/
public final CardRules getCard() {
CardAiHints cah = new CardAiHints(removedFromAIDecks, removedFromRandomDecks, hints, needs );
faces[0].assignMissingFields();
if (null != faces[1]) faces[1].assignMissingFields();
final CardRules result = new CardRules(faces, altMode, cah);
result.setDlUrls(pictureUrl);
if (StringUtils.isNotBlank(handLife))
result.setVanguardProperties(handLife);
return result;
}
public final CardRules readCard(final Iterable<String> script) {
this.reset();
for (String line : script) {
if (line.isEmpty() || line.charAt(0) == '#') {
continue;
}
this.parseLine(line);
}
return this.getCard();
}
/**
* Parses the line.
*
* @param line
* the line
*/
public final void parseLine(final String line) {
int colonPos = line.indexOf(':');
String key = colonPos > 0 ? line.substring(0, colonPos) : line;
String value = colonPos > 0 ? line.substring(1+colonPos).trim() : null;
switch(key.charAt(0)) {
case 'A':
if ("A".equals(key))
this.faces[curFace].addAbility(value);
else if ("AlternateMode".equals(key)) {
//System.out.println(faces[curFace].getName());
this.altMode = CardSplitType.smartValueOf(value);
} else if ("ALTERNATE".equals(key)) {
this.curFace = 1;
}
break;
case 'C':
if ("Colors".equals(key)) {
// This is forge.card.CardColor not forge.CardColor.
// Why do we have two classes with the same name?
ColorSet newCol = ColorSet.fromNames(value.split(","));
this.faces[this.curFace].setColor(newCol);
}
break;
case 'D':
if ("DeckHints".equals(key)) {
hints = new DeckHints(value);
} else if ("DeckNeeds".equals(key)) {
needs = new DeckHints(value);
}
break;
case 'H':
if ("HandLifeModifier".equals(key)) {
handLife = value;
}
break;
case 'K':
if ("K".equals(key)) {
this.faces[this.curFace].addKeyword(value);
}
break;
case 'L':
if ("Loyalty".equals(key)) {
this.faces[this.curFace].setInitialLoyalty(Integer.valueOf(value));
}
break;
case 'M':
if ("ManaCost".equals(key)) {
this.faces[this.curFace].setManaCost("no cost".equals(value) ? ManaCost.NO_COST
: new ManaCost(new ManaCostParser(value)));
}
break;
case 'N':
if ("Name".equals(key)) {
this.faces[this.curFace] = new CardFace(value);
}
break;
case 'O':
if ("Oracle".equals(key)) {
this.faces[this.curFace].setOracleText(value);
}
break;
case 'P':
if ("PT".equals(key)) {
this.faces[this.curFace].setPtText(value);
}
break;
case 'R':
if ("R".equals(key)) {
this.faces[this.curFace].addReplacementEffect(value);
}
break;
case 'S':
if ("S".equals(key)) {
this.faces[this.curFace].addStaticAbility(value);
} else if ( "SVar".equals(key) ) {
if ( null == value ) throw new IllegalArgumentException("SVar has no variable name");
colonPos = value.indexOf(':');
String variable = colonPos > 0 ? value.substring(0, colonPos) : value;
value = colonPos > 0 ? value.substring(1+colonPos) : null;
if ( "RemAIDeck".equals(variable) ) {
this.removedFromAIDecks = "True".equalsIgnoreCase(value);
} else if ( "RemRandomDeck".equals(variable) ) {
this.removedFromRandomDecks = "True".equalsIgnoreCase(value);
} else if ( "Picture".equals(variable) ) {
this.pictureUrl[this.curFace] = value;
} else if ( "Rarity".equals(variable) ) {
// discard that, they should supply it in SetInfo
} else
this.faces[curFace].addSVar(variable, value);
} else if ("SetInfo".equals(key)) {
// deprecated
}
break;
case 'T':
if ("T".equals(key)) {
this.faces[this.curFace].addTrigger(value);
} else if ("Types".equals(key)) {
this.faces[this.curFace].setType(CardType.parse(value));
} else if ("Text".equals(key) && !"no text".equals(value) && StringUtils.isNotBlank(value)) {
this.faces[this.curFace].setNonAbilityText(value);
}
break;
}
}
/**
* The Class ParserCardnameTxtManaCost.
*/
private static class ManaCostParser implements IParserManaCost {
private final StringTokenizer st;
private int colorlessCost;
public ManaCostParser(final String cost) {
st = new StringTokenizer(cost, " ");
this.colorlessCost = 0;
}
@Override
public final int getTotalColorlessCost() {
if (this.hasNext()) {
throw new RuntimeException("Colorless cost should be obtained after iteration is complete");
}
return this.colorlessCost;
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#hasNext()
*/
@Override
public final boolean hasNext() {
return st.hasMoreTokens();
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#next()
*/
@Override
public final ManaCostShard next() {
final String unparsed = st.nextToken();
// System.out.println(unparsed);
try {
int iVal = Integer.parseInt(unparsed);
this.colorlessCost += iVal;
return null;
}
catch (NumberFormatException nex) { }
return ManaCostShard.parseNonGeneric(unparsed);
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#remove()
*/
@Override
public void remove() {
} // unsuported
}
}
}

View File

@@ -34,7 +34,7 @@ import forge.card.CardRules;
*/
public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard {
// Reference to rules
private final transient CardRules card;
private final transient CardRules rules;
// These fields are kinda PK for PrintedCard
public final String name;
@@ -72,7 +72,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
@Override
public CardRules getRules() {
return this.card;
return this.rules;
}
@Override
@@ -100,7 +100,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
public static final Function<PaperCard, CardRules> FN_GET_RULES = new Function<PaperCard, CardRules>() {
@Override
public CardRules apply(final PaperCard from) {
return from.card;
return from.rules;
}
};
public static final Function<PaperCard, String> FN_GET_NAME = new Function<PaperCard, String>() {
@@ -117,7 +117,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index, final boolean foil) {
if ( edition0 == null || c == null || rare == null )
throw new IllegalArgumentException("Cannot create card without rules, edition or rarity");
this.card = c;
this.rules = c;
this.name = c.getName();
this.edition = edition0;
this.artIndex = index;