Files
forge/src/main/java/forge/card/CardRules.java

410 lines
18 KiB
Java

package forge.card;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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 Map<String, CardInSet> setsPrinted = null;
private boolean isRemovedFromAIDecks = false;
private boolean isRemovedFromRandomDecks = false;
// 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 boolean getRemAIDecks() { return isRemovedFromAIDecks; }
public boolean getRemRandomDecks() { return isRemovedFromRandomDecks; }
public String getPTorLoyalty() {
if (getType().isCreature()) { return power + "/" + toughness; }
if (getType().isPlaneswalker()) { return loyalty; }
return "";
}
public CardRules(final String cardName, final CardType cardType, final CardManaCost manacost,
final String ptLine, final String[] cardRules, final Map<String, CardInSet> setsData,
final boolean removedFromRandomDecks, final boolean removedFromAIDecks)
{
this.name = cardName;
this.type = cardType;
this.cost = manacost;
this.rules = cardRules;
this.color = new CardColor(cost);
this.isRemovedFromAIDecks = removedFromAIDecks;
this.isRemovedFromRandomDecks = removedFromRandomDecks;
//System.out.println(cardName);
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;
}
if (setsData.isEmpty()) {
setsData.put("???", new CardInSet(CardRarity.Unknown, 1));
}
setsPrinted = setsData;
}
public boolean rulesContain(final String text) {
if (rules == null) { return false; }
for (String r : rules) { if (StringUtils.containsIgnoreCase(r, 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 String getAiStatus() {
return isRemovedFromAIDecks ? (isRemovedFromRandomDecks ? "AI ?" : "AI") : (isRemovedFromRandomDecks ? "?" : "");
}
public Integer getAiStatusComparable() {
if (isRemovedFromAIDecks && isRemovedFromRandomDecks) { return Integer.valueOf(3); }
else if (isRemovedFromAIDecks) { return Integer.valueOf(4); }
else if (isRemovedFromRandomDecks) { return Integer.valueOf(2); }
else { return Integer.valueOf(1); }
}
/**
* Filtering conditions specific for CardRules class, defined here along with some presets.
*/
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> joinedType(final StringOp op, final String what) {
return new LeafString(LeafString.CardField.JOINED_TYPE, op, what);
}
public static Predicate<CardRules> wasPrintedInSet(final String setCode) {
return new PredicateExitsInSet(setCode);
}
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,
JOINED_TYPE
}
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);
case JOINED_TYPE:
return op(card.getType().toString(), operand);
default:
return false;
}
}
private boolean op(final String op1, final String op2) {
switch (operator) {
case CONTAINS: return StringUtils.containsIgnoreCase(op1, op2);
case NOT_CONTAINS: return !StringUtils.containsIgnoreCase(op1, op2);
case EQUALS: return op1.equalsIgnoreCase(op2);
default: 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) == shouldBeEqual;
}
public PredicateLastesSetRarity(final CardRarity type, final boolean wantEqual) {
operand = type;
shouldBeEqual = wantEqual;
}
}
private static class PredicateExitsInSet extends Predicate<CardRules> {
private final String setCode;
public PredicateExitsInSet(final String setsCode) {
setCode = setsCode;
}
@Override
public boolean isTrue(final CardRules subject) {
return subject.setsPrinted.containsKey(setCode);
}
}
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);
public static final List<Predicate<CardRules>> colors = new ArrayList<Predicate<CardRules>>();
static {
colors.add(isWhite);
colors.add(isBlue);
colors.add(isBlack);
colors.add(isRed);
colors.add(isGreen);
colors.add(isColorless);
}
public static final Predicate<CardRules> constantTrue = Predicate.getTrue(CardRules.class);
// 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);
}
}
}