Skip to content

Commit

Permalink
Added banned as commander support for Duel Commander and Tiny Leaders
Browse files Browse the repository at this point in the history
  • Loading branch information
misha-colbourne committed Nov 21, 2024
1 parent 3c94e14 commit e90b39d
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 91 deletions.
15 changes: 15 additions & 0 deletions forge-core/src/main/java/forge/StaticData.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ public class StaticData {
private Predicate<PaperCard> modernPredicate;
private Predicate<PaperCard> commanderPredicate;
private Predicate<PaperCard> oathbreakerPredicate;
private Predicate<PaperCard> tinyLeadersPredicate;
private Predicate<PaperCard> tinyLeadersAllowedAsCommanderPredicate;
private Predicate<PaperCard> duelCommanderPredicate;
private Predicate<PaperCard> duelCommanderAllowedAsCommanderPredicate;

private boolean filteredHandsEnabled = false;

Expand Down Expand Up @@ -430,8 +433,14 @@ public boolean allowCustomCardsInDecksConformance() {

public void setBrawlPredicate(Predicate<PaperCard> brawlPredicate) { this.brawlPredicate = brawlPredicate; }

public void setTinyLeadersPredicate(Predicate<PaperCard> tinyLeadersPredicate) { this.tinyLeadersPredicate = tinyLeadersPredicate; }

public void setTinyLeadersAllowedAsCommanderPredicate(Predicate<PaperCard> tinyLeadersAllowedAsCommanderPredicate) { this.tinyLeadersAllowedAsCommanderPredicate = tinyLeadersAllowedAsCommanderPredicate; }

public void setDuelCommanderPredicate(Predicate<PaperCard> duelCommanderPredicate) { this.duelCommanderPredicate = duelCommanderPredicate; }

public void setDuelCommanderAllowedAsCommanderPredicate(Predicate<PaperCard> duelCommanderAllowedAsCommanderPredicate) { this.duelCommanderAllowedAsCommanderPredicate = duelCommanderAllowedAsCommanderPredicate; }

public Predicate<PaperCard> getStandardPredicate() { return standardPredicate; }

public Predicate<PaperCard> getPioneerPredicate() { return pioneerPredicate; }
Expand All @@ -444,8 +453,14 @@ public boolean allowCustomCardsInDecksConformance() {

public Predicate<PaperCard> getBrawlPredicate() { return brawlPredicate; }

public Predicate<PaperCard> getTinyLeadersPredicate() { return tinyLeadersPredicate; }

public Predicate<PaperCard> getDuelCommanderPredicate() { return duelCommanderPredicate; }

public Predicate<PaperCard> getDuelCommanderAllowedAsCommanderPredicate() { return duelCommanderAllowedAsCommanderPredicate; }

public Predicate<PaperCard> getTinyLeadersAllowedAsCommanderPredicate() { return tinyLeadersAllowedAsCommanderPredicate; }

/**
* Get an alternative card print for the given card wrt. the input setReleaseDate.
* The reference release date will be used to retrieve the alternative art, according
Expand Down
119 changes: 39 additions & 80 deletions forge-core/src/main/java/forge/deck/DeckFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.apache.commons.lang3.Range;
import org.apache.commons.lang3.tuple.ImmutablePair;

import javax.smartcardio.Card;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
Expand All @@ -60,43 +61,19 @@ public String getAttractionDeckConformanceProblem(Deck deck) {
}
},
Commander ( Range.is(99), Range.between(0, 10), 1, null,
card -> StaticData.instance().getCommanderPredicate().apply(card)
card -> StaticData.instance().getCommanderPredicate().apply(card), null
),
Oathbreaker ( Range.is(58), Range.between(0, 10), 1, null,
card -> StaticData.instance().getOathbreakerPredicate().apply(card)
card -> StaticData.instance().getOathbreakerPredicate().apply(card), null
),
Pauper ( Range.is(60), Range.between(0, 10), 1),
Brawl ( Range.is(59), Range.between(0, 15), 1, null,
card -> StaticData.instance().getBrawlPredicate().apply(card)
card -> StaticData.instance().getBrawlPredicate().apply(card), null
),
TinyLeaders ( Range.is(49), Range.between(0, 10), 1, new Predicate<CardRules>() {
private final Set<String> bannedCards = ImmutableSet.of(
"Ancestral Recall", "Balance", "Black Lotus", "Black Vise", "Channel", "Chaos Orb", "Contract From Below", "Counterbalance", "Darkpact", "Demonic Attorney", "Demonic Tutor", "Earthcraft", "Edric, Spymaster of Trest", "Falling Star",
"Fastbond", "Flash", "Goblin Recruiter", "Grindstone", "Hermit Druid", "Imperial Seal", "Jeweled Bird", "Karakas", "Library of Alexandria", "Mana Crypt", "Mana Drain", "Mana Vault", "Metalworker", "Mind Twist", "Mishra's Workshop",
"Mox Emerald", "Mox Jet", "Mox Pearl", "Mox Ruby", "Mox Sapphire", "Najeela, the Blade Blossom", "Necropotence", "Shahrazad", "Skullclamp", "Sol Ring", "Strip Mine", "Survival of the Fittest", "Sword of Body and Mind", "Time Vault", "Time Walk", "Timetwister",
"Timmerian Fiends", "Tolarian Academy", "Umezawa's Jitte", "Vampiric Tutor", "Wheel of Fortune", "Yawgmoth's Will");

@Override
public boolean apply(CardRules rules) {
// Check for split cards explicitly, as using rules.getManaCost().getCMC()
// will return the sum of the costs, which is not what we want.
if (rules.getMainPart().getManaCost().getCMC() > 3) {
return false; //only cards with CMC less than 3 are allowed
}
ICardFace otherPart = rules.getOtherPart();
if (otherPart != null && otherPart.getManaCost().getCMC() > 3) {
return false; //only cards with CMC less than 3 are allowed
}
return !bannedCards.contains(rules.getName());
}
}) {
private final Set<String> bannedCommanders = ImmutableSet.of("Derevi, Empyrial Tactician", "Erayo, Soratami Ascendant", "Rofellos, Llanowar Emissary");

@Override
public boolean isLegalCommander(CardRules rules) {
return super.isLegalCommander(rules) && !bannedCommanders.contains(rules.getName());
}

TinyLeaders ( Range.is(49), Range.between(0, 10), 1, null,
card -> StaticData.instance().getTinyLeadersPredicate().apply(card),
card -> StaticData.instance().getTinyLeadersAllowedAsCommanderPredicate().apply(card)
) {
@Override
public void adjustCMCLevels(List<ImmutablePair<FilterCMC, Integer>> cmcLevels) {
cmcLevels.clear();
Expand All @@ -105,49 +82,10 @@ public void adjustCMCLevels(List<ImmutablePair<FilterCMC, Integer>> cmcLevels) {
cmcLevels.add(ImmutablePair.of(new FilterCMC(3, 3), 3));
}
},
DuelCommander ( Range.is(99), Range.is(0), 1, null,
card -> StaticData.instance().getDuelCommanderPredicate().apply(card)
) {
private final Set<String> bannedCommanders = ImmutableSet.of(
"Ajani, Nacatl Pariah",
"Akiri, Line-Slinger",
"Arahbo, Roar of the World",
"Asmoranomardicadaistinaculdacar",
"Baral, Chief of Compliance",
"Breya, Etherium Shaper",
"Derevi, Empyrial Tactician",
"Dihada, Binder of Wills",
"Edgar Markov",
"Edric, Spymaster of Trest",
"Emry, Lurker of the Loch",
"Eris, Roar of the Storm",
"Esior, Wardwing Familiar",
"Geist of Saint Traft",
"Inalla, Archmage Ritualist",
"Krark, the Thumbless",
"Minsc & Boo, Timeless Heroes",
"Nadu, Winged Wisdom",
"Najeela, the Blade-Blossom",
"Old Stickfingers",
"Oloro, Ageless Ascetic",
"Omnath, Locus of Creation",
"Prime Speaker Vannifar",
"Raffine, Scheming Seer",
"Rofellos, Llanowar Emissary",
"Shorikai, Genesis Engine",
"Tamiyo, Inquisitive Student",
"Tasigur, the Golden Fang",
"Urza, Lord High Artificer",
"Vial Smasher the Fierce",
"Winota, Joiner of Forces",
"Yuriko, the Tiger's Shadow",
"Zurgo Bellstriker");

@Override
public boolean isLegalCommander(CardRules rules) {
return super.isLegalCommander(rules) && !bannedCommanders.contains(rules.getName());
}
},
DuelCommander ( Range.is(99), Range.between(0, 10), 1, null,
card -> StaticData.instance().getDuelCommanderPredicate().apply(card),
card -> StaticData.instance().getDuelCommanderAllowedAsCommanderPredicate().apply(card)
),
PlanarConquest ( Range.between(40, Integer.MAX_VALUE), Range.is(0), 1),
Adventure ( Range.between(40, Integer.MAX_VALUE), Range.between(0, 15), 4),
Vanguard ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
Expand All @@ -160,31 +98,35 @@ public boolean isLegalCommander(CardRules rules) {
private final int maxCardCopies;
private final Predicate<CardRules> cardPoolFilter;
private final Predicate<PaperCard> paperCardPoolFilter;
private final Predicate<PaperCard> paperCardCommanderFilter;
private final static String ADVPROCLAMATION = "Advantageous Proclamation";
// private final static String SOVREALM = "Sovereign's Realm";

DeckFormat(Range<Integer> mainRange0, Range<Integer> sideRange0, int maxCardCopies0, Predicate<CardRules> cardPoolFilter0, Predicate<PaperCard> paperCardPoolFilter0) {
DeckFormat(Range<Integer> mainRange0, Range<Integer> sideRange0, int maxCardCopies0, Predicate<CardRules> cardPoolFilter0, Predicate<PaperCard> paperCardPoolFilter0, Predicate<PaperCard> paperCardCommanderFilter0) {
mainRange = mainRange0;
sideRange = sideRange0;
maxCardCopies = maxCardCopies0;
cardPoolFilter = cardPoolFilter0;
paperCardPoolFilter = paperCardPoolFilter0;
paperCardCommanderFilter = paperCardCommanderFilter0;
}

DeckFormat(Range<Integer> mainRange0, Range<Integer> sideRange0, int maxCardCopies0, Predicate<CardRules> cardPoolFilter0) {
mainRange = mainRange0;
sideRange = sideRange0;
maxCardCopies = maxCardCopies0;
paperCardPoolFilter = null;
cardPoolFilter = cardPoolFilter0;
paperCardPoolFilter = null;
paperCardCommanderFilter = null;
}

DeckFormat(Range<Integer> mainRange0, Range<Integer> sideRange0, int maxCardCopies0) {
mainRange = mainRange0;
sideRange = sideRange0;
maxCardCopies = maxCardCopies0;
paperCardPoolFilter = null;
cardPoolFilter = null;
paperCardPoolFilter = null;
paperCardCommanderFilter = null;
}

public boolean hasCommander() {
Expand Down Expand Up @@ -284,7 +226,7 @@ public String getDeckConformanceProblem(Deck deck) {
}

for (PaperCard pc : commanders) {
if (!isLegalCommander(pc.getRules())) {
if (!isLegalCommander(pc)) {
return "has an illegal commander";
}
cmdCI |= pc.getRules().getColorIdentity().getColor();
Expand Down Expand Up @@ -518,10 +460,16 @@ public boolean isLegalCard(PaperCard pc) {
return cardPoolFilter.apply(pc.getRules());
}

public boolean isLegalCommander(CardRules rules) {
public boolean isLegalCommander(PaperCard card) {
CardRules rules = card.getRules();

if (cardPoolFilter != null && !cardPoolFilter.apply(rules)) {
return false;
}
if (paperCardCommanderFilter != null && !paperCardCommanderFilter.apply(card)) {
return false;
}

if (this.equals(DeckFormat.Oathbreaker)) {
return rules.canBeOathbreaker();
}
Expand Down Expand Up @@ -560,6 +508,17 @@ public Predicate<Deck> hasLegalCardsPredicate(boolean enforceDeckLegality) {
}
}
}

for (final PaperCard commander : deck.getCommanders()) {
if (!isLegalCommander(commander)) {
System.err.println(
"Excluding deck: '" + deck.toString() +
"' Reason: '" + commander.getName() + "' is not a legal commander."
);
return false;
}
}

return true;
};
}
Expand All @@ -569,7 +528,7 @@ public Predicate<PaperCard> isLegalCardPredicate() {
}

public Predicate<PaperCard> isLegalCommanderPredicate() {
return card -> isLegalCommander(card.getRules());
return this::isLegalCommander;
}

public Predicate<PaperCard> isLegalCardForCommanderPredicate(List<PaperCard> commanders) {
Expand Down
30 changes: 26 additions & 4 deletions forge-game/src/main/java/forge/game/GameFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public enum FormatSubType {
protected final List<String> allowedSetCodes; // this is mutable to support quest mode set unlocks
protected final List<CardRarity> allowedRarities;
protected final List<String> bannedCardNames;
protected final List<String> bannedAsCommanderCardNames;
protected final List<String> restrictedCardNames;
protected final List<String> additionalCardNames; // for cards that are legal but not reprinted in any of the allowed Sets
protected boolean restrictedLegendary = false;
Expand All @@ -84,22 +85,24 @@ public enum FormatSubType {

protected final transient List<String> allowedSetCodes_ro;
protected final transient List<String> bannedCardNames_ro;
protected final transient List<String> bannedAsCommanderCardNames_ro;
protected final transient List<String> restrictedCardNames_ro;
protected final transient List<String> additionalCardNames_ro;

protected final transient Predicate<PaperCard> filterRules;
protected final transient Predicate<PaperCard> filterPrinted;
protected final transient Predicate<PaperCard> filterAllowedAsCommander;

private final int index;

public GameFormat(final String fName, final Iterable<String> sets, final List<String> bannedCards) {
this(fName, parseDate(DEFAULTDATE), sets, bannedCards, null, false, null, null, 0, FormatType.CUSTOM, FormatSubType.CUSTOM);
this(fName, parseDate(DEFAULTDATE), sets, bannedCards, null, null, false, null, null, 0, FormatType.CUSTOM, FormatSubType.CUSTOM);
}

public static final GameFormat NoFormat = new GameFormat("(none)", parseDate(DEFAULTDATE) , null, null, null, false
public static final GameFormat NoFormat = new GameFormat("(none)", parseDate(DEFAULTDATE) , null, null, null, null, false
, null, null, Integer.MAX_VALUE, FormatType.CUSTOM, FormatSubType.CUSTOM);

public GameFormat(final String fName, final Date effectiveDate, final Iterable<String> sets, final List<String> bannedCards,
public GameFormat(final String fName, final Date effectiveDate, final Iterable<String> sets, final List<String> bannedCards, final List<String> bannedAsCommander,
final List<String> restrictedCards, Boolean restrictedLegendary, final List<String> additionalCards,
final List<CardRarity> rarities, int compareIdx, FormatType formatType, FormatSubType formatSubType) {
this.index = compareIdx;
Expand All @@ -124,18 +127,21 @@ public GameFormat(final String fName, final Date effectiveDate, final Iterable<S
}

bannedCardNames = bannedCards == null ? new ArrayList<>() : Lists.newArrayList(bannedCards);
bannedAsCommanderCardNames = bannedAsCommander == null ? new ArrayList<>() : Lists.newArrayList(bannedAsCommander);
restrictedCardNames = restrictedCards == null ? new ArrayList<>() : Lists.newArrayList(restrictedCards);
allowedRarities = rarities == null ? new ArrayList<>() : rarities;
this.restrictedLegendary = restrictedLegendary;
additionalCardNames = additionalCards == null ? new ArrayList<>() : Lists.newArrayList(additionalCards);

this.allowedSetCodes_ro = Collections.unmodifiableList(allowedSetCodes);
this.bannedCardNames_ro = Collections.unmodifiableList(bannedCardNames);
this.bannedAsCommanderCardNames_ro = Collections.unmodifiableList(bannedAsCommanderCardNames);
this.restrictedCardNames_ro = Collections.unmodifiableList(restrictedCardNames);
this.additionalCardNames_ro = Collections.unmodifiableList(additionalCardNames);

this.filterRules = this.buildFilterRules();
this.filterPrinted = this.buildFilterPrinted();
this.filterAllowedAsCommander = Predicates.not(IPaperCard.Predicates.names(this.getBannedAsCommanderCardNames()));
}
protected Predicate<PaperCard> buildFilter(boolean printed) {
Predicate<PaperCard> p = Predicates.not(IPaperCard.Predicates.names(this.getBannedCardNames()));
Expand Down Expand Up @@ -204,6 +210,10 @@ public List<String> getBannedCardNames() {
return this.bannedCardNames_ro;
}

public List<String> getBannedAsCommanderCardNames() {
return this.bannedAsCommanderCardNames_ro;
}

public List<String> getRestrictedCards() {
return restrictedCardNames_ro;
}
Expand Down Expand Up @@ -247,6 +257,10 @@ public Predicate<PaperCard> getFilterPrinted() {
return this.filterPrinted;
}

public Predicate<PaperCard> getFilterAllowedAsCommander() {
return this.filterAllowedAsCommander;
}

public boolean isSetLegal(final String setCode) {
return this.getAllowedSetCodes().isEmpty() || this.getAllowedSetCodes().contains(setCode);
}
Expand Down Expand Up @@ -357,6 +371,7 @@ public static class Reader extends StorageReaderRecursiveFolderWithUserFolder<Ga
coreFormats.add("Extended.txt");
coreFormats.add("Brawl.txt");
coreFormats.add("Oathbreaker.txt");
coreFormats.add("TinyLeaders.txt");
coreFormats.add("DuelCommander.txt");
coreFormats.add("Premodern.txt");
coreFormats.add("Pauper.txt");
Expand All @@ -376,6 +391,7 @@ protected GameFormat read(File file) {
final Map<String, List<String>> contents = FileSection.parseSections(FileUtil.readFile(file));
List<String> sets = null; // default: all sets allowed
List<String> bannedCards = null; // default: nothing banned
List<String> bannedAsCommanders = null; // default: nothing banned
List<String> restrictedCards = null; // default: nothing restricted
Boolean restrictedLegendary = false;
List<String> additionalCards = null; // default: nothing additional
Expand Down Expand Up @@ -413,10 +429,16 @@ protected GameFormat read(File file) {
if ( null != strSets ) {
sets = Arrays.asList(strSets.split(", "));
}

String strCars = section.get("banned");
if ( strCars != null ) {
bannedCards = Arrays.asList(strCars.split("; "));
}

strCars = section.get("bannedAsCommander");
if ( strCars != null ) {
bannedAsCommanders = Arrays.asList(strCars.split("; "));
}

strCars = section.get("restricted");
if ( strCars != null ) {
Expand Down Expand Up @@ -445,7 +467,7 @@ protected GameFormat read(File file) {
}
}

GameFormat result = new GameFormat(title, date, sets, bannedCards, restrictedCards, restrictedLegendary, additionalCards, rarities, idx, formatType,formatsubType);
GameFormat result = new GameFormat(title, date, sets, bannedCards, bannedAsCommanders, restrictedCards, restrictedLegendary, additionalCards, rarities, idx, formatType,formatsubType);
naturallyOrdered.add(result);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ protected void addCommanderItems(final FDropDownMenu menu, final PaperCard card,
}
boolean isLegalCommander;
String captionSuffix = Forge.getLocalizer().getMessage("lblCommander");
isLegalCommander = DeckFormat.Commander.isLegalCommander(card.getRules());
isLegalCommander = DeckFormat.Commander.isLegalCommander(card);
if (isLegalCommander && !parentScreen.getCommanderPage().cardManager.getPool().contains(card)) {
addItem(menu, "Set", "as " + captionSuffix, parentScreen.getCommanderPage().getIcon(), isAddMenu, isAddSource, new Callback<Integer>() {
@Override
Expand Down
Loading

0 comments on commit e90b39d

Please sign in to comment.