Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duel commander format #6596

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions forge-core/src/main/java/forge/StaticData.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +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 @@ -429,6 +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 @@ -441,6 +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
1 change: 1 addition & 0 deletions forge-core/src/main/java/forge/card/CardRules.java
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ public boolean canBeCommander() {
if (mainPart.getOracleText().contains("can be your commander") || canBeBackground()) {
return true;
}

CardType type = mainPart.getType();
boolean creature = type.isCreature();
for (String staticAbility : mainPart.getStaticAbilities()) { // Check for Grist
Expand Down
79 changes: 39 additions & 40 deletions forge-core/src/main/java/forge/deck/DeckFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import forge.StaticData;
import forge.card.CardRules;
import forge.card.CardRulesPredicates;
import forge.card.CardType;
import forge.card.ColorSet;
import forge.card.ICardFace;
import forge.deck.generation.DeckGenPool;
import forge.deck.generation.DeckGeneratorBase.FilterCMC;
import forge.deck.generation.IDeckGenPool;
Expand Down Expand Up @@ -60,43 +58,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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats happening to the other checks...?

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,6 +79,10 @@ public void adjustCMCLevels(List<ImmutablePair<FilterCMC, Integer>> cmcLevels) {
cmcLevels.add(ImmutablePair.of(new FilterCMC(3, 3), 3));
}
},
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 @@ -117,35 +95,39 @@ public void adjustCMCLevels(List<ImmutablePair<FilterCMC, Integer>> cmcLevels) {
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() {
return this == Commander || this == Oathbreaker || this == TinyLeaders || this == Brawl;
return this == Commander || this == Oathbreaker || this == TinyLeaders || this == Brawl || this == DuelCommander;
}

public boolean hasSignatureSpell() {
Expand Down Expand Up @@ -241,7 +223,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 @@ -475,10 +457,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 @@ -517,6 +505,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 @@ -526,7 +525,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
3 changes: 2 additions & 1 deletion forge-game/src/main/java/forge/game/GameAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -1380,7 +1380,8 @@ else if (c.hasBeenDealtDeathtouchDamage() || (c.getDamage() > 0 && c.getLethal()

if ((game.getRules().hasAppliedVariant(GameType.Commander)
|| game.getRules().hasAppliedVariant(GameType.Brawl)
|| game.getRules().hasAppliedVariant(GameType.Planeswalker)) && !checkAgain) {
|| game.getRules().hasAppliedVariant(GameType.Planeswalker)
|| game.getRules().hasAppliedVariant(GameType.DuelCommander)) && !checkAgain) {
for (final Card c : p.getCardsIn(ZoneType.Graveyard).threadSafeIterable()) {
checkAgain |= stateBasedAction_Commander(c, mapParams);
}
Expand Down
31 changes: 27 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,8 @@ 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");
coreFormats.add("PreDH.txt");
Expand All @@ -375,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 @@ -412,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 @@ -444,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
3 changes: 2 additions & 1 deletion forge-game/src/main/java/forge/game/GameRules.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ public boolean hasCommander() {
return appliedVariants.contains(GameType.Commander)
|| appliedVariants.contains(GameType.Oathbreaker)
|| appliedVariants.contains(GameType.TinyLeaders)
|| appliedVariants.contains(GameType.Brawl);
|| appliedVariants.contains(GameType.Brawl)
|| appliedVariants.contains(GameType.DuelCommander);
}

public boolean useGrayText() {
Expand Down
Loading
Loading