diff --git a/forge-core/src/main/java/forge/StaticData.java b/forge-core/src/main/java/forge/StaticData.java index 068e1956d6f..81c737e60e8 100644 --- a/forge-core/src/main/java/forge/StaticData.java +++ b/forge-core/src/main/java/forge/StaticData.java @@ -43,6 +43,10 @@ public class StaticData { private Predicate modernPredicate; private Predicate commanderPredicate; private Predicate oathbreakerPredicate; + private Predicate tinyLeadersPredicate; + private Predicate tinyLeadersAllowedAsCommanderPredicate; + private Predicate duelCommanderPredicate; + private Predicate duelCommanderAllowedAsCommanderPredicate; private boolean filteredHandsEnabled = false; @@ -429,6 +433,14 @@ public boolean allowCustomCardsInDecksConformance() { public void setBrawlPredicate(Predicate brawlPredicate) { this.brawlPredicate = brawlPredicate; } + public void setTinyLeadersPredicate(Predicate tinyLeadersPredicate) { this.tinyLeadersPredicate = tinyLeadersPredicate; } + + public void setTinyLeadersAllowedAsCommanderPredicate(Predicate tinyLeadersAllowedAsCommanderPredicate) { this.tinyLeadersAllowedAsCommanderPredicate = tinyLeadersAllowedAsCommanderPredicate; } + + public void setDuelCommanderPredicate(Predicate duelCommanderPredicate) { this.duelCommanderPredicate = duelCommanderPredicate; } + + public void setDuelCommanderAllowedAsCommanderPredicate(Predicate duelCommanderAllowedAsCommanderPredicate) { this.duelCommanderAllowedAsCommanderPredicate = duelCommanderAllowedAsCommanderPredicate; } + public Predicate getStandardPredicate() { return standardPredicate; } public Predicate getPioneerPredicate() { return pioneerPredicate; } @@ -441,6 +453,14 @@ public boolean allowCustomCardsInDecksConformance() { public Predicate getBrawlPredicate() { return brawlPredicate; } + public Predicate getTinyLeadersPredicate() { return tinyLeadersPredicate; } + + public Predicate getDuelCommanderPredicate() { return duelCommanderPredicate; } + + public Predicate getDuelCommanderAllowedAsCommanderPredicate() { return duelCommanderAllowedAsCommanderPredicate; } + + public Predicate 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 diff --git a/forge-core/src/main/java/forge/card/CardRules.java b/forge-core/src/main/java/forge/card/CardRules.java index 3811eeb3ded..eb8c7e64a56 100644 --- a/forge-core/src/main/java/forge/card/CardRules.java +++ b/forge-core/src/main/java/forge/card/CardRules.java @@ -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 diff --git a/forge-core/src/main/java/forge/deck/DeckFormat.java b/forge-core/src/main/java/forge/deck/DeckFormat.java index 7abbfefc159..c711c06db6c 100644 --- a/forge-core/src/main/java/forge/deck/DeckFormat.java +++ b/forge-core/src/main/java/forge/deck/DeckFormat.java @@ -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; @@ -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() { - private final Set 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 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> cmcLevels) { cmcLevels.clear(); @@ -105,6 +79,10 @@ public void adjustCMCLevels(List> 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), @@ -117,35 +95,39 @@ public void adjustCMCLevels(List> cmcLevels) { private final int maxCardCopies; private final Predicate cardPoolFilter; private final Predicate paperCardPoolFilter; + private final Predicate paperCardCommanderFilter; private final static String ADVPROCLAMATION = "Advantageous Proclamation"; // private final static String SOVREALM = "Sovereign's Realm"; - DeckFormat(Range mainRange0, Range sideRange0, int maxCardCopies0, Predicate cardPoolFilter0, Predicate paperCardPoolFilter0) { + DeckFormat(Range mainRange0, Range sideRange0, int maxCardCopies0, Predicate cardPoolFilter0, Predicate paperCardPoolFilter0, Predicate paperCardCommanderFilter0) { mainRange = mainRange0; sideRange = sideRange0; maxCardCopies = maxCardCopies0; cardPoolFilter = cardPoolFilter0; paperCardPoolFilter = paperCardPoolFilter0; + paperCardCommanderFilter = paperCardCommanderFilter0; } DeckFormat(Range mainRange0, Range sideRange0, int maxCardCopies0, Predicate cardPoolFilter0) { mainRange = mainRange0; sideRange = sideRange0; maxCardCopies = maxCardCopies0; - paperCardPoolFilter = null; cardPoolFilter = cardPoolFilter0; + paperCardPoolFilter = null; + paperCardCommanderFilter = null; } DeckFormat(Range mainRange0, Range 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() { @@ -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(); @@ -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(); } @@ -517,6 +505,17 @@ public Predicate 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; }; } @@ -526,7 +525,7 @@ public Predicate isLegalCardPredicate() { } public Predicate isLegalCommanderPredicate() { - return card -> isLegalCommander(card.getRules()); + return this::isLegalCommander; } public Predicate isLegalCardForCommanderPredicate(List commanders) { diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index c2969e4ca72..1e0a1971392 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -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); } diff --git a/forge-game/src/main/java/forge/game/GameFormat.java b/forge-game/src/main/java/forge/game/GameFormat.java index bde08906dbc..12d6e90c0a0 100644 --- a/forge-game/src/main/java/forge/game/GameFormat.java +++ b/forge-game/src/main/java/forge/game/GameFormat.java @@ -75,6 +75,7 @@ public enum FormatSubType { protected final List allowedSetCodes; // this is mutable to support quest mode set unlocks protected final List allowedRarities; protected final List bannedCardNames; + protected final List bannedAsCommanderCardNames; protected final List restrictedCardNames; protected final List additionalCardNames; // for cards that are legal but not reprinted in any of the allowed Sets protected boolean restrictedLegendary = false; @@ -84,22 +85,24 @@ public enum FormatSubType { protected final transient List allowedSetCodes_ro; protected final transient List bannedCardNames_ro; + protected final transient List bannedAsCommanderCardNames_ro; protected final transient List restrictedCardNames_ro; protected final transient List additionalCardNames_ro; protected final transient Predicate filterRules; protected final transient Predicate filterPrinted; + protected final transient Predicate filterAllowedAsCommander; private final int index; public GameFormat(final String fName, final Iterable sets, final List 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 sets, final List bannedCards, + public GameFormat(final String fName, final Date effectiveDate, final Iterable sets, final List bannedCards, final List bannedAsCommander, final List restrictedCards, Boolean restrictedLegendary, final List additionalCards, final List rarities, int compareIdx, FormatType formatType, FormatSubType formatSubType) { this.index = compareIdx; @@ -124,6 +127,7 @@ public GameFormat(final String fName, final Date effectiveDate, final Iterable() : 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; @@ -131,11 +135,13 @@ public GameFormat(final String fName, final Date effectiveDate, final Iterable buildFilter(boolean printed) { Predicate p = Predicates.not(IPaperCard.Predicates.names(this.getBannedCardNames())); @@ -204,6 +210,10 @@ public List getBannedCardNames() { return this.bannedCardNames_ro; } + public List getBannedAsCommanderCardNames() { + return this.bannedAsCommanderCardNames_ro; + } + public List getRestrictedCards() { return restrictedCardNames_ro; } @@ -247,6 +257,10 @@ public Predicate getFilterPrinted() { return this.filterPrinted; } + public Predicate getFilterAllowedAsCommander() { + return this.filterAllowedAsCommander; + } + public boolean isSetLegal(final String setCode) { return this.getAllowedSetCodes().isEmpty() || this.getAllowedSetCodes().contains(setCode); } @@ -357,6 +371,8 @@ public static class Reader extends StorageReaderRecursiveFolderWithUserFolder> contents = FileSection.parseSections(FileUtil.readFile(file)); List sets = null; // default: all sets allowed List bannedCards = null; // default: nothing banned + List bannedAsCommanders = null; // default: nothing banned List restrictedCards = null; // default: nothing restricted Boolean restrictedLegendary = false; List additionalCards = null; // default: nothing additional @@ -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 ) { @@ -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; } diff --git a/forge-game/src/main/java/forge/game/GameRules.java b/forge-game/src/main/java/forge/game/GameRules.java index 48905fec905..2472ce1d6fb 100644 --- a/forge-game/src/main/java/forge/game/GameRules.java +++ b/forge-game/src/main/java/forge/game/GameRules.java @@ -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() { diff --git a/forge-game/src/main/java/forge/game/GameType.java b/forge-game/src/main/java/forge/game/GameType.java index 87aebe39cc7..9d9abacd1a0 100644 --- a/forge-game/src/main/java/forge/game/GameType.java +++ b/forge-game/src/main/java/forge/game/GameType.java @@ -36,6 +36,7 @@ public enum GameType { Oathbreaker (DeckFormat.Oathbreaker, false, false, false, "lblOathbreaker", "lblOathbreakerDesc"), TinyLeaders (DeckFormat.TinyLeaders, false, false, false, "lblTinyLeaders", "lblTinyLeadersDesc"), Brawl (DeckFormat.Brawl, false, false, false, "lblBrawl", "lblBrawlDesc"), + DuelCommander (DeckFormat.DuelCommander, false, false, false, "lblDuelCommander", "lblDuelCommanderDesc"), Planeswalker (DeckFormat.PlanarConquest, false, false, true, "lblPlaneswalker", "lblPlaneswalkerDesc"), Planechase (DeckFormat.Planechase, false, false, true, "lblPlanechase", "lblPlanechaseDesc"), Archenemy (DeckFormat.Archenemy, false, false, true, "lblArchenemy", "lblArchenemyDesc"), diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 75bd3d72c02..3d9756dc0c6 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -701,11 +701,12 @@ public final int addDamageAfterPrevention(final int amount, final Card source, f addPoisonCounters(poisonCounters, source.getController(), counterTable); } - //Oathbreaker, Tiny Leaders, and Brawl ignore commander damage rule + //Oathbreaker, Tiny Leaders, Duel Commander and Brawl ignore commander damage rule if (source.isCommander() && isCombat && !this.getGame().getRules().hasAppliedVariant(GameType.Oathbreaker) && !this.getGame().getRules().hasAppliedVariant(GameType.TinyLeaders) - && !this.getGame().getRules().hasAppliedVariant(GameType.Brawl)) { + && !this.getGame().getRules().hasAppliedVariant(GameType.Brawl) + && !this.getGame().getRules().hasAppliedVariant(GameType.DuelCommander)) { // In case that commander is merged permanent, get the real commander card final Card realCommander = source.getRealCommander(); addCommanderDamage(realCommander, amount); diff --git a/forge-game/src/main/java/forge/game/player/RegisteredPlayer.java b/forge-game/src/main/java/forge/game/player/RegisteredPlayer.java index 8967d91df3c..d25a26dfee9 100644 --- a/forge-game/src/main/java/forge/game/player/RegisteredPlayer.java +++ b/forge-game/src/main/java/forge/game/player/RegisteredPlayer.java @@ -175,6 +175,9 @@ public static RegisteredPlayer forVariants(final int playerCount, start.commanders = deck.getCommanders(); start.setStartingLife(start.getStartingLife() + 10); } + if (appliedVariants.contains(GameType.DuelCommander)) { + start.commanders = deck.getCommanders(); + } if (appliedVariants.contains(GameType.Planechase)) { start.planes = planes; } diff --git a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java index 1ae5b9de9c4..8ab3dcce47c 100644 --- a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java +++ b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java @@ -143,6 +143,9 @@ private void updateCustom() { case TinyLeaders: updateDecks(DeckProxy.getAllTinyLeadersDecks(), ItemManagerConfig.COMMANDER_DECKS); break; + case DuelCommander: + updateDecks(DeckProxy.getAllDuelCommanderDecks(), ItemManagerConfig.COMMANDER_DECKS); + break; default: updateDecks(DeckProxy.getAllConstructedDecks(), ItemManagerConfig.CONSTRUCTED_DECKS); break; diff --git a/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java b/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java index 607b77a0b49..bcbabc9222b 100644 --- a/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java +++ b/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java @@ -52,6 +52,7 @@ public enum EDocID { EDITOR_BRAWL (VBrawlDecks.SINGLETON_INSTANCE), EDITOR_TINY_LEADERS (VTinyLeadersDecks.SINGLETON_INSTANCE), EDITOR_OATHBREAKER (VOathbreakerDecks.SINGLETON_INSTANCE), + EDITOR_DUEL_COMMANDER (VDuelCommanderDecks.SINGLETON_INSTANCE), EDITOR_LOG(VEditorLog.SINGLETON_INSTANCE), WORKSHOP_CATALOG (VWorkshopCatalog.SINGLETON_INSTANCE), diff --git a/forge-gui-desktop/src/main/java/forge/gui/framework/FScreen.java b/forge-gui-desktop/src/main/java/forge/gui/framework/FScreen.java index 97000772173..04e3f7a7163 100644 --- a/forge-gui-desktop/src/main/java/forge/gui/framework/FScreen.java +++ b/forge-gui-desktop/src/main/java/forge/gui/framework/FScreen.java @@ -98,6 +98,15 @@ public class FScreen { "lblCloseEditor", ForgeConstants.EDITOR_LAYOUT_FILE, false); + public static final FScreen DECK_EDITOR_DUEL_COMMANDER = new FScreen( + VDeckEditorUI.SINGLETON_INSTANCE, + CDeckEditorUI.SINGLETON_INSTANCE, + "lblDuelCommanderDeckEditor", + FSkin.getImage(FSkinProp.IMG_PACK), + true, + "lblCloseEditor", + ForgeConstants.EDITOR_LAYOUT_FILE, + false); public static final FScreen DECK_EDITOR_PLANECHASE = new FScreen( VDeckEditorUI.SINGLETON_INSTANCE, CDeckEditorUI.SINGLETON_INSTANCE, diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/DeckManager.java b/forge-gui-desktop/src/main/java/forge/itemmanager/DeckManager.java index 707986841ad..cd976b56f0d 100644 --- a/forge-gui-desktop/src/main/java/forge/itemmanager/DeckManager.java +++ b/forge-gui-desktop/src/main/java/forge/itemmanager/DeckManager.java @@ -335,6 +335,11 @@ public void editDeck(final DeckProxy deck) { DeckPreferences.setTinyLeadersDeck((deck != null) ? deck.toString() : ""); editorCtrl = new CEditorConstructed(getCDetailPicture(), this.gameType); break; + case DuelCommander: + screen = FScreen.DECK_EDITOR_CONSTRUCTED; // re-use "Deck Editor", rather than creating a new top level tab + DeckPreferences.setDuelCommanderDeck((deck != null) ? deck.toString() : ""); + editorCtrl = new CEditorConstructed(getCDetailPicture(), this.gameType); + break; case Sealed: screen = FScreen.DECK_EDITOR_SEALED; editorCtrl = new CEditorLimited(FModel.getDecks().getSealed(), screen, getCDetailPicture()); @@ -386,6 +391,7 @@ public boolean deleteDeck(final DeckProxy deck) { case Commander: case Oathbreaker: case TinyLeaders: + case DuelCommander: case Constructed: case Draft: case Sealed: diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/CDeckEditorUI.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/CDeckEditorUI.java index c1069f04bd4..0621c31ec37 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/CDeckEditorUI.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/CDeckEditorUI.java @@ -64,6 +64,7 @@ public enum CDeckEditorUI implements ICDoc { private final VOathbreakerDecks vOathbreakerDecks; private final VBrawlDecks vBrawlDecks; private final VTinyLeadersDecks vTinyLeadersDecks; + private final VDuelCommanderDecks vDuelCommanderDecks; private final VEditorLog vEditorLog; CDeckEditorUI() { @@ -79,6 +80,8 @@ public enum CDeckEditorUI implements ICDoc { this.vBrawlDecks.setCDetailPicture(cDetailPicture); this.vTinyLeadersDecks = VTinyLeadersDecks.SINGLETON_INSTANCE; this.vTinyLeadersDecks.setCDetailPicture(cDetailPicture); + this.vDuelCommanderDecks = VDuelCommanderDecks.SINGLETON_INSTANCE; + this.vDuelCommanderDecks.setCDetailPicture(cDetailPicture); this.vEditorLog = VEditorLog.SINGLETON_INSTANCE; } diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/SEditorIO.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/SEditorIO.java index d68c38d8ff8..b748e620cfb 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/SEditorIO.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/SEditorIO.java @@ -73,6 +73,10 @@ else if (FOptionPane.showConfirmDialog(Localizer.getInstance().getMessage("lblTh COathbreakerDecks.SINGLETON_INSTANCE.refresh(); VOathbreakerDecks.SINGLETON_INSTANCE.getLstDecks().setSelectedString(deckStr); break; + case DuelCommander: + CDuelCommanderDecks.SINGLETON_INSTANCE.refresh(); + VDuelCommanderDecks.SINGLETON_INSTANCE.getLstDecks().setSelectedString(deckStr); + break; default: CAllDecks.SINGLETON_INSTANCE.refresh(); //pull new deck into deck list and select it VAllDecks.SINGLETON_INSTANCE.getLstDecks().setSelectedString(deckStr); diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CDuelCommanderDecks.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CDuelCommanderDecks.java new file mode 100644 index 00000000000..bdb851e1b2f --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CDuelCommanderDecks.java @@ -0,0 +1,43 @@ +package forge.screens.deckeditor.controllers; + +import forge.deck.DeckProxy; +import forge.gui.framework.ICDoc; +import forge.screens.deckeditor.views.VDuelCommanderDecks; + +/** + * Controls the "Duel Commander Decks" panel in the deck editor UI. + * + *

(C at beginning of class name denotes a control class.) + * + */ +public enum CDuelCommanderDecks implements ICDoc { + SINGLETON_INSTANCE; + + private final VDuelCommanderDecks view = VDuelCommanderDecks.SINGLETON_INSTANCE; + + //========== Overridden methods + + @Override + public void register() { + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#initialize() + */ + @Override + public void initialize() { + refresh(); + } + + public void refresh() { + CAllDecks.refreshDeckManager(view.getLstDecks(), DeckProxy.getAllDuelCommanderDecks()); + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#update() + */ + @Override + public void update() { + CAllDecks.updateDeckManager(view.getLstDecks()); + } +} diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorCommander.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorCommander.java index cce01b0f987..a9961747920 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorCommander.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorCommander.java @@ -73,12 +73,12 @@ public final class CEditorCommander extends CDeckEditor { */ @SuppressWarnings("serial") public CEditorCommander(final CDetailPicture cDetailPicture, GameType gameType0) { - super(gameType0 == GameType.TinyLeaders ? FScreen.DECK_EDITOR_TINY_LEADERS : gameType0 == GameType.Brawl ? FScreen.DECK_EDITOR_BRAWL : - gameType0 == GameType.Oathbreaker ? FScreen.DECK_EDITOR_OATHBREAKER : FScreen.DECK_EDITOR_COMMANDER, cDetailPicture, gameType0); + super(getFScreen(gameType0), cDetailPicture, gameType0); allSections.add(DeckSection.Main); allSections.add(DeckSection.Sideboard); allSections.add(DeckSection.Commander); + CardDb commonCards = FModel.getMagicDb().getCommonCards(); if (gameType == GameType.Brawl){ GameFormat format = FModel.getFormats().get("Brawl"); @@ -115,6 +115,9 @@ public CEditorCommander(final CDetailPicture cDetailPicture, GameType gameType0) case Oathbreaker: this.controller = new DeckController<>(decks.getOathbreaker(), this, newCreator); break; + case DuelCommander: + this.controller = new DeckController<>(decks.getDuelCommander(), this, newCreator); + break; default: this.controller = new DeckController<>(decks.getCommander(), this, newCreator); break; @@ -123,6 +126,23 @@ public CEditorCommander(final CDetailPicture cDetailPicture, GameType gameType0) getBtnAddBasicLands().setCommand((UiCommand) () -> CEditorConstructed.addBasicLands(CEditorCommander.this)); } + private static FScreen getFScreen(GameType gameType0) { + FScreen fScreen; + switch (gameType0) { + case TinyLeaders: + fScreen = FScreen.DECK_EDITOR_TINY_LEADERS; + case Brawl: + fScreen = FScreen.DECK_EDITOR_BRAWL; + case Oathbreaker: + fScreen = FScreen.DECK_EDITOR_OATHBREAKER; + case DuelCommander: + fScreen = FScreen.DECK_EDITOR_DUEL_COMMANDER; + default: + fScreen = FScreen.DECK_EDITOR_COMMANDER; + } + return fScreen; + } + //=========== Overridden from ACEditorBase @Override diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java index f3be724aab0..2e270194cbc 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java @@ -95,6 +95,7 @@ public CEditorConstructed(final CDetailPicture cDetailPicture0, final GameType g break; case Commander: + case DuelCommander: allSections.add(DeckSection.Commander); commanderPool = FModel.getCommanderPool(); @@ -161,6 +162,9 @@ public CEditorConstructed(final CDetailPicture cDetailPicture0, final GameType g case TinyLeaders: this.controller = new DeckController<>(FModel.getDecks().getTinyLeaders(), this, newCreator); break; + case DuelCommander: + this.controller = new DeckController<>(FModel.getDecks().getDuelCommander(), this, newCreator); + break; default: } @@ -182,6 +186,7 @@ protected CardLimit getCardLimit() { case Oathbreaker: case TinyLeaders: case Brawl: + case DuelCommander: return CardLimit.Singleton; default: } @@ -491,6 +496,7 @@ public void setEditorMode(DeckSection sectionMode) { case Oathbreaker: case TinyLeaders: case Brawl: + case DuelCommander: switch(sectionMode) { case Main: this.getCatalogManager().setup(ItemManagerConfig.CARD_CATALOG); diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorDraftingProcess.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorDraftingProcess.java index cae36cd0508..2e94a4f01ea 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorDraftingProcess.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorDraftingProcess.java @@ -60,6 +60,7 @@ public class CEditorDraftingProcess extends ACEditorBase i private DragCell oathbreakerDecksParent = null; private DragCell brawlDecksParent = null; private DragCell tinyLeadersDecksParent = null; + private DragCell duelCommanderDecksParent = null; private DragCell deckGenParent = null; private DragCell draftLogParent = null; private boolean saved = false; @@ -367,6 +368,7 @@ public void update() { oathbreakerDecksParent = removeTab(VOathbreakerDecks.SINGLETON_INSTANCE); brawlDecksParent = removeTab(VBrawlDecks.SINGLETON_INSTANCE); tinyLeadersDecksParent = removeTab(VTinyLeadersDecks.SINGLETON_INSTANCE); + duelCommanderDecksParent = removeTab(VDuelCommanderDecks.SINGLETON_INSTANCE); // set catalog table to single-selection only mode getCatalogManager().setAllowMultipleSelections(false); @@ -422,6 +424,9 @@ public void resetUIChanges() { if (tinyLeadersDecksParent != null) { tinyLeadersDecksParent.addDoc(VTinyLeadersDecks.SINGLETON_INSTANCE); } + if (duelCommanderDecksParent != null) { + duelCommanderDecksParent.addDoc(VDuelCommanderDecks.SINGLETON_INSTANCE); + } if (draftLogParent != null) { draftLogParent.addDoc(VEditorLog.SINGLETON_INSTANCE); } diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorLimited.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorLimited.java index 81cdbb05aa3..ad627da0d3e 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorLimited.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorLimited.java @@ -40,13 +40,7 @@ import forge.model.FModel; import forge.screens.deckeditor.AddBasicLandsDialog; import forge.screens.deckeditor.SEditorIO; -import forge.screens.deckeditor.views.VAllDecks; -import forge.screens.deckeditor.views.VBrawlDecks; -import forge.screens.deckeditor.views.VCommanderDecks; -import forge.screens.deckeditor.views.VCurrentDeck; -import forge.screens.deckeditor.views.VDeckgen; -import forge.screens.deckeditor.views.VOathbreakerDecks; -import forge.screens.deckeditor.views.VTinyLeadersDecks; +import forge.screens.deckeditor.views.*; import forge.screens.home.sanctioned.CSubmenuDraft; import forge.screens.home.sanctioned.CSubmenuSealed; import forge.screens.match.controllers.CDetailPicture; @@ -69,6 +63,7 @@ public final class CEditorLimited extends CDeckEditor { private DragCell oathbreakerDecksParent = null; private DragCell brawlDecksParent = null; private DragCell tinyLeadersDecksParent = null; + private DragCell duelCommanderDecksParent = null; private DragCell deckGenParent = null; private final List allSections = new ArrayList<>(); @@ -243,6 +238,7 @@ public void update() { oathbreakerDecksParent = removeTab(VOathbreakerDecks.SINGLETON_INSTANCE); brawlDecksParent = removeTab(VBrawlDecks.SINGLETON_INSTANCE); tinyLeadersDecksParent = removeTab(VTinyLeadersDecks.SINGLETON_INSTANCE); + duelCommanderDecksParent = removeTab(VDuelCommanderDecks.SINGLETON_INSTANCE); } /* (non-Javadoc) @@ -277,6 +273,9 @@ public void resetUIChanges() { if (brawlDecksParent!= null) { brawlDecksParent.addDoc(VBrawlDecks.SINGLETON_INSTANCE); } + if (duelCommanderDecksParent!= null) { + duelCommanderDecksParent.addDoc(VDuelCommanderDecks.SINGLETON_INSTANCE); + } if (tinyLeadersDecksParent != null) { tinyLeadersDecksParent.addDoc(VTinyLeadersDecks.SINGLETON_INSTANCE); } diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuestDraftingProcess.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuestDraftingProcess.java index f24973ffd77..e463a598ba9 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuestDraftingProcess.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuestDraftingProcess.java @@ -62,6 +62,7 @@ public void setDraftQuest(CSubmenuQuestDraft draftQuest0) { private DragCell oathbreakerDecksParent = null; private DragCell brawlDecksParent = null; private DragCell tinyLeadersDecksParent = null; + private DragCell duelCommanderDecksParent = null; private DragCell deckGenParent = null; private boolean saved = false; @@ -285,6 +286,7 @@ public void update() { oathbreakerDecksParent = removeTab(VOathbreakerDecks.SINGLETON_INSTANCE); brawlDecksParent = removeTab(VBrawlDecks.SINGLETON_INSTANCE); tinyLeadersDecksParent = removeTab(VTinyLeadersDecks.SINGLETON_INSTANCE); + duelCommanderDecksParent = removeTab(VDuelCommanderDecks.SINGLETON_INSTANCE); // set catalog table to single-selection only mode getCatalogManager().setAllowMultipleSelections(false); @@ -343,6 +345,9 @@ public void resetUIChanges() { if (tinyLeadersDecksParent != null) { tinyLeadersDecksParent.addDoc(VTinyLeadersDecks.SINGLETON_INSTANCE); } + if (duelCommanderDecksParent != null) { + duelCommanderDecksParent.addDoc(VDuelCommanderDecks.SINGLETON_INSTANCE); + } // set catalog table back to free-selection mode getCatalogManager().setAllowMultipleSelections(true); diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/views/VDuelCommanderDecks.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/views/VDuelCommanderDecks.java new file mode 100644 index 00000000000..6d0ea0b7981 --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/views/VDuelCommanderDecks.java @@ -0,0 +1,101 @@ +package forge.screens.deckeditor.views; + +import forge.deck.io.DeckPreferences; +import forge.game.GameType; +import forge.gui.framework.DragCell; +import forge.gui.framework.DragTab; +import forge.gui.framework.EDocID; +import forge.gui.framework.IVDoc; +import forge.itemmanager.DeckManager; +import forge.itemmanager.ItemManagerContainer; +import forge.screens.deckeditor.controllers.CDuelCommanderDecks; +import forge.screens.match.controllers.CDetailPicture; +import forge.util.Localizer; +import net.miginfocom.swing.MigLayout; + +import javax.swing.*; + +/** + * Assembles Swing components of all deck viewer in deck editor. + * + *

(V at beginning of class name denotes a view class.) + */ +public enum VDuelCommanderDecks implements IVDoc { + /** */ + SINGLETON_INSTANCE; + + // Fields used with interface IVDoc + private DragCell parentCell; + final Localizer localizer = Localizer.getInstance(); + private final DragTab tab = new DragTab(localizer.getMessage("lblDuelCommander")); + + private DeckManager lstDecks; + + //========== Overridden methods + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getDocumentID() + */ + @Override + public EDocID getDocumentID() { + return EDocID.EDITOR_DUEL_COMMANDER; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getTabLabel() + */ + @Override + public DragTab getTabLabel() { + return tab; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getLayoutControl() + */ + @Override + public CDuelCommanderDecks getLayoutControl() { + return CDuelCommanderDecks.SINGLETON_INSTANCE; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#setParentCell(forge.gui.framework.DragCell) + */ + @Override + public void setParentCell(DragCell cell0) { + this.parentCell = cell0; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getParentCell() + */ + @Override + public DragCell getParentCell() { + return this.parentCell; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#populate() + */ + @Override + public void populate() { + CDuelCommanderDecks.SINGLETON_INSTANCE.refresh(); //ensure decks refreshed in case any deleted or added since last loaded + String preferredDeck = DeckPreferences.getDuelCommanderDeck(); + + JPanel parentBody = parentCell.getBody(); + parentBody.setLayout(new MigLayout("insets 5, gap 0, wrap, hidemode 3")); + parentBody.add(new ItemManagerContainer(lstDecks), "push, grow"); + + VAllDecks.editPreferredDeck(lstDecks, preferredDeck); + } + + //========== Retrieval methods + /** @return {@link JPanel} */ + public DeckManager getLstDecks() { + return lstDecks; + } + + public void setCDetailPicture(final CDetailPicture cDetailPicture) { + this.lstDecks = new DeckManager(GameType.DuelCommander, cDetailPicture); + this.lstDecks.setCaption(localizer.getMessage("lblDuelCommanderDecks")); + } +} diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java b/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java index fb519d0f19b..4899db541ba 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java @@ -398,7 +398,8 @@ private void updateVariantControlsVisibility() { final boolean isOathbreaker = lobby.hasVariant(GameType.Oathbreaker); final boolean isTinyLeaders = lobby.hasVariant(GameType.TinyLeaders); final boolean isBrawl = lobby.hasVariant(GameType.Brawl); - final boolean isCommanderApplied = mayEdit && (lobby.hasVariant(GameType.Commander) || isOathbreaker || isTinyLeaders || isBrawl); + final boolean isDuelCommander = lobby.hasVariant(GameType.DuelCommander); + final boolean isCommanderApplied = mayEdit && (lobby.hasVariant(GameType.Commander) || isOathbreaker || isTinyLeaders || isBrawl || isDuelCommander); final boolean isPlanechaseApplied = mayEdit && lobby.hasVariant(GameType.Planechase); final boolean isVanguardApplied = mayEdit && lobby.hasVariant(GameType.Vanguard); final boolean isArchenemyApplied = mayEdit && lobby.hasVariant(GameType.Archenemy); @@ -556,6 +557,7 @@ private void addHandlersToVariantsControls() { lobby.hasVariant(GameType.Oathbreaker) ? GameType.Oathbreaker : lobby.hasVariant(GameType.TinyLeaders) ? GameType.TinyLeaders : lobby.hasVariant(GameType.Brawl) ? GameType.Brawl : + lobby.hasVariant(GameType.DuelCommander) ? GameType.DuelCommander : GameType.Commander); cmdDeckSelectorBtn.requestFocusInWindow(); lobby.changePlayerFocus(index); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java index 8bdeae8f401..0d5cf48e8dc 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java @@ -90,13 +90,14 @@ public class VLobby implements ILobbyView { private final VariantCheckBox vntOathbreaker = new VariantCheckBox(GameType.Oathbreaker); private final VariantCheckBox vntTinyLeaders = new VariantCheckBox(GameType.TinyLeaders); private final VariantCheckBox vntBrawl = new VariantCheckBox(GameType.Brawl); + private final VariantCheckBox vntDuelCommander = new VariantCheckBox(GameType.DuelCommander); private final VariantCheckBox vntPlanechase = new VariantCheckBox(GameType.Planechase); private final VariantCheckBox vntArchenemy = new VariantCheckBox(GameType.Archenemy); private final VariantCheckBox vntArchenemyRumble = new VariantCheckBox(GameType.ArchenemyRumble); private final ImmutableList vntBoxesLocal = - ImmutableList.of(vntVanguard, vntMomirBasic, vntMoJhoSto, vntCommander, vntOathbreaker, vntBrawl, vntTinyLeaders, vntPlanechase, vntArchenemy, vntArchenemyRumble); + ImmutableList.of(vntVanguard, vntMomirBasic, vntMoJhoSto, vntCommander, vntOathbreaker, vntBrawl, vntTinyLeaders, vntDuelCommander, vntPlanechase, vntArchenemy, vntArchenemyRumble); private final ImmutableList vntBoxesNetwork = - ImmutableList.of(vntVanguard, vntMomirBasic, vntMoJhoSto, vntCommander, vntOathbreaker, vntBrawl, vntTinyLeaders /*, vntPlanechase, vntArchenemy, vntArchenemyRumble */); + ImmutableList.of(vntVanguard, vntMomirBasic, vntMoJhoSto, vntCommander, vntOathbreaker, vntBrawl, vntTinyLeaders, vntDuelCommander /*, vntPlanechase, vntArchenemy, vntArchenemyRumble */); // Player frame elements private final JPanel playersFrame = new JPanel(new MigLayout("insets 0, gap 0 5, wrap, hidemode 3")); @@ -582,6 +583,7 @@ private void populateDeckPanel(final GameType forGameType) { case Oathbreaker: case TinyLeaders: case Brawl: + case DuelCommander: decksFrame.add(getDeckChooser(playerWithFocus), "grow, push"); break; case Planechase: @@ -794,6 +796,11 @@ private FDeckChooser createDeckChooser(final GameType type, final int iSlot, fin deckType = iSlot == 0 ? DeckType.BRAWL_DECK : DeckType.CUSTOM_DECK; prefKey = FPref.BRAWL_DECK_STATES[iSlot]; break; + case DuelCommander: + forCommander = true; + deckType = iSlot == 0 ? DeckType.DUEL_COMMANDER_DECK : DeckType.CUSTOM_DECK; + prefKey = FPref.DUEL_COMMANDER_DECK_STATES[iSlot]; + break; default: forCommander = false; deckType = iSlot == 0 ? DeckType.PRECONSTRUCTED_DECK : DeckType.COLOR_DECK; diff --git a/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java b/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java index 1c0fa85e556..f3c9ef668d3 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java +++ b/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java @@ -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() { @Override diff --git a/forge-gui-mobile/src/forge/deck/FDeckChooser.java b/forge-gui-mobile/src/forge/deck/FDeckChooser.java index d7f6cc3f0e3..ebe981c4165 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckChooser.java +++ b/forge-gui-mobile/src/forge/deck/FDeckChooser.java @@ -211,6 +211,7 @@ else if (selectedDeckType == DeckType.PAUPER_CARDGEN_DECK){ case Oathbreaker: case TinyLeaders: case Brawl: + case DuelCommander: case Gauntlet: initialize(null, DeckType.CUSTOM_DECK); break; @@ -274,6 +275,9 @@ public void onActivate() { case Brawl: lstDecks.setSelectedString(DeckPreferences.getBrawlDeck()); break; + case DuelCommander: + lstDecks.setSelectedString(DeckPreferences.getDuelCommanderDeck()); + break; case Archenemy: lstDecks.setSelectedString(DeckPreferences.getSchemeDeck()); break; @@ -294,6 +298,9 @@ public void onActivate() { case BRAWL_DECK: lstDecks.setSelectedString(DeckPreferences.getBrawlDeck()); break; + case DUEL_COMMANDER_DECK: + lstDecks.setSelectedString(DeckPreferences.getDuelCommanderDeck()); + break; case SCHEME_DECK: lstDecks.setSelectedString(DeckPreferences.getSchemeDeck()); break; @@ -376,6 +383,7 @@ private void createNewDeck() { case OATHBREAKER_DECK: case TINY_LEADERS_DECK: case BRAWL_DECK: + case DUEL_COMMANDER_DECK: case SCHEME_DECK: case PLANAR_DECK: case DRAFT_DECK: @@ -405,6 +413,7 @@ private void editSelectedDeck() { case OATHBREAKER_DECK: case TINY_LEADERS_DECK: case BRAWL_DECK: + case DUEL_COMMANDER_DECK: case SCHEME_DECK: case PLANAR_DECK: case DRAFT_DECK: @@ -446,6 +455,9 @@ public void run(Boolean result) { case Oathbreaker: storage = FModel.getDecks().getOathbreaker(); break; + case DuelCommander: + storage = FModel.getDecks().getDuelCommander(); + break; default: storage = FModel.getDecks().getConstructed(); break; @@ -472,6 +484,8 @@ private EditorType getEditorType() { return EditorType.TinyLeaders; case BRAWL_DECK: return EditorType.Brawl; + case DUEL_COMMANDER_DECK: + return EditorType.DuelCommander; case SCHEME_DECK: return EditorType.Archenemy; case PLANAR_DECK: @@ -491,6 +505,8 @@ private EditorType getEditorType() { return EditorType.TinyLeaders; case Brawl: return EditorType.Brawl; + case DuelCommander: + return EditorType.DuelCommander; case Archenemy: return EditorType.Archenemy; case Planechase: @@ -578,6 +594,7 @@ public void initialize(FPref savedStateSetting, DeckType defaultDeckType) { case Oathbreaker: case TinyLeaders: case Brawl: + case DuelCommander: cmbDeckTypes.addItem(DeckType.CUSTOM_DECK); cmbDeckTypes.addItem(DeckType.PRECON_COMMANDER_DECK); cmbDeckTypes.addItem(DeckType.RANDOM_DECK); @@ -593,6 +610,7 @@ public void initialize(FPref savedStateSetting, DeckType defaultDeckType) { cmbDeckTypes.addItem(DeckType.OATHBREAKER_DECK); cmbDeckTypes.addItem(DeckType.TINY_LEADERS_DECK); cmbDeckTypes.addItem(DeckType.BRAWL_DECK); + cmbDeckTypes.addItem(DeckType.DUEL_COMMANDER_DECK); cmbDeckTypes.addItem(DeckType.SCHEME_DECK); cmbDeckTypes.addItem(DeckType.PLANAR_DECK); cmbDeckTypes.addItem(DeckType.DRAFT_DECK); @@ -872,6 +890,10 @@ private void refreshDecksList(DeckType deckType, boolean forceRefresh, FEvent ev pool = DeckProxy.getAllBrawlDecks(); config = ItemManagerConfig.COMMANDER_DECKS; break; + case DuelCommander: + pool = DeckProxy.getAllDuelCommanderDecks(); + config = ItemManagerConfig.COMMANDER_DECKS; + break; case Archenemy: pool = DeckProxy.getAllSchemeDecks(); config = ItemManagerConfig.SCHEME_DECKS; @@ -910,6 +932,10 @@ private void refreshDecksList(DeckType deckType, boolean forceRefresh, FEvent ev pool = DeckProxy.getAllBrawlDecks(); config = ItemManagerConfig.COMMANDER_DECKS; break; + case DUEL_COMMANDER_DECK: + pool = DeckProxy.getAllDuelCommanderDecks(); + config = ItemManagerConfig.COMMANDER_DECKS; + break; case RANDOM_COMMANDER_DECK: pool = CommanderDeckGenerator.getCommanderDecks(lstDecks.getGameType().getDeckFormat(),isAi, false); config = ItemManagerConfig.STRING_ONLY; @@ -1408,11 +1434,17 @@ private void testSelectedDeck() { } if (selectedDeckType == DeckType.BRAWL_DECK) { - //cannot create gauntlet for tiny leaders decks, so just start single match + //cannot create gauntlet for brawl decks, so just start single match testVariantDeck(userDeck, GameType.Brawl); return; } + if (selectedDeckType == DeckType.DUEL_COMMANDER_DECK) { + //cannot create gauntlet for duel commander decks, so just start single match + testVariantDeck(userDeck, GameType.DuelCommander); + return; + } + GuiChoose.getInteger(Forge.getLocalizer().getMessage("lblHowManyOpponents"), 1, 50, new Callback() { @Override public void run(final Integer numOpponents) { diff --git a/forge-gui-mobile/src/forge/deck/FDeckEditor.java b/forge-gui-mobile/src/forge/deck/FDeckEditor.java index 8578e757cd5..5b757282874 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckEditor.java +++ b/forge-gui-mobile/src/forge/deck/FDeckEditor.java @@ -58,6 +58,7 @@ public enum EditorType { Oathbreaker(new DeckController<>(FModel.getDecks().getOathbreaker(), (Supplier) Deck::new), null), TinyLeaders(new DeckController<>(FModel.getDecks().getTinyLeaders(), (Supplier) Deck::new), DeckFormat.TinyLeaders.isLegalCardPredicate()), Brawl(new DeckController<>(FModel.getDecks().getBrawl(), (Supplier) Deck::new), DeckFormat.Brawl.isLegalCardPredicate()), + DuelCommander(new DeckController<>(FModel.getDecks().getDuelCommander(), (Supplier) Deck::new), DeckFormat.DuelCommander.isLegalCardPredicate()), Archenemy(new DeckController<>(FModel.getDecks().getScheme(), (Supplier) Deck::new), null), Planechase(new DeckController<>(FModel.getDecks().getPlane(), (Supplier) Deck::new), null), Quest(new DeckController<>(null, (Supplier) Deck::new), null), //delay setting root folder until quest loaded @@ -72,7 +73,7 @@ public enum EditorType { EnumSet.of(Draft, Sealed, Winston, QuestDraft, Quest, QuestCommander, PlanarConquest) ); private static final Set COMMANDER_TYPES = Collections.unmodifiableSet( - EnumSet.of(Commander, Oathbreaker, TinyLeaders, Brawl, QuestCommander) + EnumSet.of(Commander, Oathbreaker, TinyLeaders, Brawl, DuelCommander, QuestCommander) ); private final DeckController controller; private final Predicate cardFilter; @@ -147,6 +148,7 @@ private static DeckEditorPage[] getPages(EditorType editorType) { case Commander: case TinyLeaders: case Brawl: + case DuelCommander: return isLandscape ? new DeckEditorPage[] { new CatalogPage(ItemManagerConfig.CARD_CATALOG), new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.COMMANDER_SECTION), @@ -610,6 +612,7 @@ private CardLimit getCardLimit() { case Oathbreaker: case TinyLeaders: case Brawl: + case DuelCommander: case PlanarConquest: return CardLimit.Singleton; } @@ -878,6 +881,7 @@ public static boolean allowsReplacement(final EditorType editorType){ case Oathbreaker: case TinyLeaders: case Brawl: + case DuelCommander: return true; default: { @@ -1262,8 +1266,10 @@ protected boolean canBeCommander(final PaperCard card) { return card.getRules().canBeOathbreaker(); case PlanarConquest: return false; //don't set commander this way in Planar Conquest + case DuelCommander: + return DeckFormat.DuelCommander.isLegalCommander(card); default: - return DeckFormat.Commander.isLegalCommander(card.getRules()); + return DeckFormat.Commander.isLegalCommander(card); } } @@ -1480,6 +1486,7 @@ public void refresh() { case Oathbreaker: case TinyLeaders: case Brawl: + case DuelCommander: final List commanders = currentDeck.getCommanders(); if (commanders.isEmpty()) { //if no commander set for deck, only show valid commanders @@ -1500,6 +1507,10 @@ public void refresh() { additionalFilter = DeckFormat.Brawl.isLegalCommanderPredicate(); cardManager.setCaption(Forge.getLocalizer().getMessage("lblCommanders")); break; + case DuelCommander: + additionalFilter = DeckFormat.DuelCommander.isLegalCommanderPredicate(); + cardManager.setCaption(Forge.getLocalizer().getMessage("lblCommanders")); + break; default: // Do nothing } @@ -1518,6 +1529,9 @@ public void refresh() { case Brawl: additionalFilter = DeckFormat.Brawl.isLegalCardForCommanderPredicate(commanders); break; + case DuelCommander: + additionalFilter = DeckFormat.DuelCommander.isLegalCardForCommanderPredicate(commanders); + break; default: // Do nothing } @@ -2245,6 +2259,9 @@ public void save() { case Brawl: DeckPreferences.setBrawlDeck(deckStr); break; + case DuelCommander: + DeckPreferences.setDuelCommanderDeck(deckStr); + break; case Archenemy: DeckPreferences.setSchemeDeck(deckStr); break; diff --git a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java index e7db61405a1..b354e0fce26 100644 --- a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java +++ b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java @@ -147,6 +147,7 @@ public LobbyScreen(String headerCaption, FPopupMenu menu, GameLobby lobby0) { cbVariants.addItem(GameType.Oathbreaker); cbVariants.addItem(GameType.TinyLeaders); cbVariants.addItem(GameType.Brawl); + cbVariants.addItem(GameType.DuelCommander); cbVariants.addItem(GameType.Planechase); cbVariants.addItem(GameType.Archenemy); cbVariants.addItem(GameType.ArchenemyRumble); @@ -183,14 +184,14 @@ else if (cbVariants.getSelectedIndex() == cbVariants.getItemCount() - 1) { updatePlayersFromPrefs(); FThreads.invokeInBackgroundThread(() -> { - playerPanels.get(0).initialize(FPref.CONSTRUCTED_P1_DECK_STATE, FPref.COMMANDER_P1_DECK_STATE, FPref.OATHBREAKER_P1_DECK_STATE, FPref.TINY_LEADER_P1_DECK_STATE, FPref.BRAWL_P1_DECK_STATE, DeckType.PRECONSTRUCTED_DECK); - playerPanels.get(1).initialize(FPref.CONSTRUCTED_P2_DECK_STATE, FPref.COMMANDER_P2_DECK_STATE, FPref.OATHBREAKER_P2_DECK_STATE, FPref.TINY_LEADER_P2_DECK_STATE, FPref.BRAWL_P2_DECK_STATE, DeckType.COLOR_DECK); + playerPanels.get(0).initialize(FPref.CONSTRUCTED_P1_DECK_STATE, FPref.COMMANDER_P1_DECK_STATE, FPref.OATHBREAKER_P1_DECK_STATE, FPref.TINY_LEADER_P1_DECK_STATE, FPref.BRAWL_P1_DECK_STATE, FPref.DUEL_COMMANDER_P1_DECK_STATE, DeckType.PRECONSTRUCTED_DECK); + playerPanels.get(1).initialize(FPref.CONSTRUCTED_P2_DECK_STATE, FPref.COMMANDER_P2_DECK_STATE, FPref.OATHBREAKER_P2_DECK_STATE, FPref.TINY_LEADER_P2_DECK_STATE, FPref.BRAWL_P2_DECK_STATE, FPref.DUEL_COMMANDER_P2_DECK_STATE, DeckType.COLOR_DECK); try { if (getNumPlayers() > 2) { - playerPanels.get(2).initialize(FPref.CONSTRUCTED_P3_DECK_STATE, FPref.COMMANDER_P3_DECK_STATE, FPref.OATHBREAKER_P3_DECK_STATE, FPref.TINY_LEADER_P3_DECK_STATE, FPref.BRAWL_P3_DECK_STATE, DeckType.COLOR_DECK); + playerPanels.get(2).initialize(FPref.CONSTRUCTED_P3_DECK_STATE, FPref.COMMANDER_P3_DECK_STATE, FPref.OATHBREAKER_P3_DECK_STATE, FPref.TINY_LEADER_P3_DECK_STATE, FPref.BRAWL_P3_DECK_STATE, FPref.DUEL_COMMANDER_P3_DECK_STATE, DeckType.COLOR_DECK); } if (getNumPlayers() > 3) { - playerPanels.get(3).initialize(FPref.CONSTRUCTED_P4_DECK_STATE, FPref.COMMANDER_P4_DECK_STATE, FPref.OATHBREAKER_P3_DECK_STATE, FPref.TINY_LEADER_P4_DECK_STATE, FPref.BRAWL_P4_DECK_STATE, DeckType.COLOR_DECK); + playerPanels.get(3).initialize(FPref.CONSTRUCTED_P4_DECK_STATE, FPref.COMMANDER_P4_DECK_STATE, FPref.OATHBREAKER_P3_DECK_STATE, FPref.TINY_LEADER_P4_DECK_STATE, FPref.BRAWL_P4_DECK_STATE, FPref.DUEL_COMMANDER_P4_DECK_STATE, DeckType.COLOR_DECK); } } catch (Exception e) {} /*playerPanels.get(4).initialize(FPref.CONSTRUCTED_P5_DECK_STATE, DeckType.COLOR_DECK); @@ -455,6 +456,7 @@ private MultiVariantSelect() { lstVariants.addItem(new Variant(GameType.Oathbreaker)); lstVariants.addItem(new Variant(GameType.TinyLeaders)); lstVariants.addItem(new Variant(GameType.Brawl)); + lstVariants.addItem(new Variant(GameType.DuelCommander)); lstVariants.addItem(new Variant(GameType.Planechase)); lstVariants.addItem(new Variant(GameType.Archenemy)); lstVariants.addItem(new Variant(GameType.ArchenemyRumble)); @@ -586,6 +588,16 @@ public final void update(final int slot, final LobbySlotType type) { default: break; } + final FDeckChooser duelCommanderDeckChooser = playerPanels.get(slot).getDuelCommanderDeckChooser(); + selectedDeckType = duelCommanderDeckChooser.getSelectedDeckType(); + switch (selectedDeckType){ + case RANDOM_CARDGEN_COMMANDER_DECK: + case RANDOM_COMMANDER_DECK: + duelCommanderDeckChooser.refreshDeckListForAI(); + break; + default: + break; + } } @Override @@ -614,9 +626,9 @@ public void update(final boolean fullUpdate) { else { panel = new PlayerPanel(this, allowNetworking, i, slot, lobby.mayEdit(i), lobby.hasControl()); if (i == 2) { - panel.initialize(FPref.CONSTRUCTED_P3_DECK_STATE, FPref.COMMANDER_P3_DECK_STATE, FPref.OATHBREAKER_P3_DECK_STATE, FPref.TINY_LEADER_P3_DECK_STATE, FPref.BRAWL_P3_DECK_STATE, DeckType.COLOR_DECK); + panel.initialize(FPref.CONSTRUCTED_P3_DECK_STATE, FPref.COMMANDER_P3_DECK_STATE, FPref.OATHBREAKER_P3_DECK_STATE, FPref.TINY_LEADER_P3_DECK_STATE, FPref.BRAWL_P3_DECK_STATE, FPref.DUEL_COMMANDER_P3_DECK_STATE, DeckType.COLOR_DECK); } else if (i == 3) { - panel.initialize(FPref.CONSTRUCTED_P4_DECK_STATE, FPref.COMMANDER_P4_DECK_STATE, FPref.OATHBREAKER_P4_DECK_STATE, FPref.TINY_LEADER_P4_DECK_STATE, FPref.BRAWL_P4_DECK_STATE, DeckType.COLOR_DECK); + panel.initialize(FPref.CONSTRUCTED_P4_DECK_STATE, FPref.COMMANDER_P4_DECK_STATE, FPref.OATHBREAKER_P4_DECK_STATE, FPref.TINY_LEADER_P4_DECK_STATE, FPref.BRAWL_P4_DECK_STATE, FPref.DUEL_COMMANDER_P4_DECK_STATE, DeckType.COLOR_DECK); } playerPanels.add(panel); playersScroll.add(panel); @@ -725,7 +737,16 @@ else if (hasVariant(GameType.Brawl)) { deckName = Forge.getLocalizer().getMessage("lblBrawlDeck") + ": " + playerPanel.getBrawlDeckChooser().getDeck().getName(); } - }else { + } + else if (hasVariant(GameType.DuelCommander)) { + deck = playerPanel.getDuelCommanderDeck(); + if (deck != null) { + playerPanel.getDuelCommanderDeckChooser().saveState(); + deckName = Forge.getLocalizer().getMessage("lblDuelCommanderDeck") + ": " + + playerPanel.getDuelCommanderDeckChooser().getDeck().getName(); + } + } + else { deck = playerPanel.getDeck(); if (deck != null) { playerPanel.getDeckChooser().saveState(); diff --git a/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java b/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java index b05df5549a8..7e4b6fce34c 100644 --- a/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java +++ b/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java @@ -76,10 +76,11 @@ public class PlayerPanel extends FContainer { private final FLabel btnOathbreakDeck = new FLabel.ButtonBuilder().text(Forge.getLocalizer().getMessage("lblOathbreakerDeckRandomGenerated")).build(); private final FLabel btnTinyLeadersDeck = new FLabel.ButtonBuilder().text(Forge.getLocalizer().getMessage("lblTinyLeadersDeckRandomGenerated")).build(); private final FLabel btnBrawlDeck = new FLabel.ButtonBuilder().text(Forge.getLocalizer().getMessage("lblBrawlDeckRandomGenerated")).build(); + private final FLabel btnDuelCommanderDeck = new FLabel.ButtonBuilder().text(Forge.getLocalizer().getMessage("lblDuelCommanderDeckRandomGenerated")).build(); private final FLabel btnPlanarDeck = new FLabel.ButtonBuilder().text(Forge.getLocalizer().getMessage("lblPlanarDeckRandomGenerated")).build(); private final FLabel btnVanguardAvatar = new FLabel.ButtonBuilder().text(Forge.getLocalizer().getMessage("lblVanguardAvatarRandom")).build(); - private final FDeckChooser deckChooser, lstSchemeDecks, lstCommanderDecks, lstOathbreakerDecks, lstTinyLeadersDecks, lstBrawlDecks, lstPlanarDecks; + private final FDeckChooser deckChooser, lstSchemeDecks, lstCommanderDecks, lstOathbreakerDecks, lstTinyLeadersDecks, lstBrawlDecks, lstDuelCommanderDecks, lstPlanarDecks; private final FVanguardChooser lstVanguardAvatars; public PlayerPanel(final LobbyScreen screen0, final boolean allowNetworking0, final int index0, final LobbySlot slot, final boolean mayEdit0, final boolean mayControl0) { @@ -180,6 +181,21 @@ public void handleEvent(FEvent e) { } } }); + lstDuelCommanderDecks = new FDeckChooser(GameType.DuelCommander, isAi, new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + if( ((DeckManager)e.getSource()).getSelectedItem() != null) { + btnDuelCommanderDeck.setText(Forge.getLocalizer().getMessage("lblDuelCommanderDeck") + + ":" + (Forge.isLandscapeMode() ? " " : "\n") + ((DeckManager) e.getSource()).getSelectedItem().getName()); + lstDuelCommanderDecks.saveState(); + if (allowNetworking && btnDuelCommanderDeck.isEnabled() && humanAiSwitch.isToggled()) { + screen.updateMyDeck(index); + } + }else{ + btnDuelCommanderDeck.setText(Forge.getLocalizer().getMessage("lblDuelCommanderDeck")); + } + } + }); lstSchemeDecks = new FDeckChooser(GameType.Archenemy, isAi, e -> { if( ((DeckManager)e.getSource()).getSelectedItem() != null){ btnSchemeDeck.setText(Forge.getLocalizer().getMessage("lblSchemeDeck") @@ -261,6 +277,11 @@ public void handleEvent(FEvent e) { lstBrawlDecks.setHeaderCaption(Forge.getLocalizer().getMessage("lblSelectBrawlDeckFor").replace("%s", txtPlayerName.getText())); Forge.openScreen(lstBrawlDecks); }); + add(btnDuelCommanderDeck); + btnDuelCommanderDeck.setCommand(e -> { + lstDuelCommanderDecks.setHeaderCaption(Forge.getLocalizer().getMessage("lblSelectDuelCommanderDeckFor").replace("%s", txtPlayerName.getText())); + Forge.openScreen(lstDuelCommanderDecks); + }); add(btnSchemeDeck); btnSchemeDeck.setCommand(e -> { lstSchemeDecks.setHeaderCaption(Forge.getLocalizer().getMessage("lblSelectSchemeDeckFor").replace("%s", txtPlayerName.getText())); @@ -286,7 +307,7 @@ public void handleEvent(FEvent e) { setMayControl(mayControl0); } - public void initialize(FPref savedStateSetting, FPref savedStateSettingCommander, FPref savedStateSettingOathbreaker, FPref savedStateSettingTinyLeader, FPref savedStateSettingBrawl, DeckType defaultDeckType) { + public void initialize(FPref savedStateSetting, FPref savedStateSettingCommander, FPref savedStateSettingOathbreaker, FPref savedStateSettingTinyLeader, FPref savedStateSettingBrawl, FPref savedStateSettingDuelCommander, DeckType defaultDeckType) { //order by last variant.. Set gameTypes = FModel.getPreferences().getGameType(FPref.UI_APPLIED_VARIANTS); if (gameTypes.contains(GameType.Commander)) { @@ -294,24 +315,35 @@ public void initialize(FPref savedStateSetting, FPref savedStateSettingCommander lstOathbreakerDecks.initialize(savedStateSettingOathbreaker, DeckType.OATHBREAKER_DECK); lstTinyLeadersDecks.initialize(savedStateSettingTinyLeader, DeckType.TINY_LEADERS_DECK); lstBrawlDecks.initialize(savedStateSettingBrawl, DeckType.BRAWL_DECK); + lstDuelCommanderDecks.initialize(savedStateSettingDuelCommander, DeckType.DUEL_COMMANDER_DECK); deckChooser.initialize(savedStateSetting, defaultDeckType); } else if (gameTypes.contains(GameType.Oathbreaker)) { lstOathbreakerDecks.initialize(savedStateSettingOathbreaker, DeckType.OATHBREAKER_DECK); lstCommanderDecks.initialize(savedStateSettingCommander, DeckType.COMMANDER_DECK); lstTinyLeadersDecks.initialize(savedStateSettingTinyLeader, DeckType.TINY_LEADERS_DECK); lstBrawlDecks.initialize(savedStateSettingBrawl, DeckType.BRAWL_DECK); + lstDuelCommanderDecks.initialize(savedStateSettingDuelCommander, DeckType.DUEL_COMMANDER_DECK); deckChooser.initialize(savedStateSetting, defaultDeckType); } else if (gameTypes.contains(GameType.TinyLeaders)) { lstTinyLeadersDecks.initialize(savedStateSettingTinyLeader, DeckType.TINY_LEADERS_DECK); lstOathbreakerDecks.initialize(savedStateSettingOathbreaker, DeckType.OATHBREAKER_DECK); lstCommanderDecks.initialize(savedStateSettingCommander, DeckType.COMMANDER_DECK); lstBrawlDecks.initialize(savedStateSettingBrawl, DeckType.BRAWL_DECK); + lstDuelCommanderDecks.initialize(savedStateSettingDuelCommander, DeckType.DUEL_COMMANDER_DECK); deckChooser.initialize(savedStateSetting, defaultDeckType); } else if (gameTypes.contains(GameType.Brawl)) { lstBrawlDecks.initialize(savedStateSettingBrawl, DeckType.BRAWL_DECK); lstTinyLeadersDecks.initialize(savedStateSettingTinyLeader, DeckType.TINY_LEADERS_DECK); lstOathbreakerDecks.initialize(savedStateSettingOathbreaker, DeckType.OATHBREAKER_DECK); lstCommanderDecks.initialize(savedStateSettingCommander, DeckType.COMMANDER_DECK); + lstDuelCommanderDecks.initialize(savedStateSettingDuelCommander, DeckType.DUEL_COMMANDER_DECK); + deckChooser.initialize(savedStateSetting, defaultDeckType); + } else if (gameTypes.contains(GameType.DuelCommander)) { + lstBrawlDecks.initialize(savedStateSettingBrawl, DeckType.BRAWL_DECK); + lstTinyLeadersDecks.initialize(savedStateSettingTinyLeader, DeckType.TINY_LEADERS_DECK); + lstOathbreakerDecks.initialize(savedStateSettingOathbreaker, DeckType.OATHBREAKER_DECK); + lstCommanderDecks.initialize(savedStateSettingCommander, DeckType.COMMANDER_DECK); + lstDuelCommanderDecks.initialize(savedStateSettingDuelCommander, DeckType.DUEL_COMMANDER_DECK); deckChooser.initialize(savedStateSetting, defaultDeckType); } else { deckChooser.initialize(savedStateSetting, defaultDeckType); @@ -319,6 +351,7 @@ public void initialize(FPref savedStateSetting, FPref savedStateSettingCommander lstOathbreakerDecks.initialize(savedStateSettingOathbreaker, DeckType.OATHBREAKER_DECK); lstTinyLeadersDecks.initialize(savedStateSettingTinyLeader, DeckType.TINY_LEADERS_DECK); lstBrawlDecks.initialize(savedStateSettingBrawl, DeckType.BRAWL_DECK); + lstDuelCommanderDecks.initialize(savedStateSettingDuelCommander, DeckType.DUEL_COMMANDER_DECK); } lstPlanarDecks.initialize(null, DeckType.RANDOM_DECK); lstSchemeDecks.initialize(null, DeckType.RANDOM_DECK); @@ -412,6 +445,10 @@ else if (btnBrawlDeck.isVisible()) { btnBrawlDeck.setBounds(x, y, w, fieldHeight); y += dy; } + else if (btnDuelCommanderDeck.isVisible()) { + btnDuelCommanderDeck.setBounds(x, y, w, fieldHeight); + y += dy; + } else if (btnDeck.isVisible()) { btnDeck.setBounds(x, y, w, fieldHeight); y += dy; @@ -435,7 +472,7 @@ public float getPreferredHeight() { if(Forge.isLandscapeMode()) rows--; } - if (btnCommanderDeck.isVisible() || btnOathbreakDeck.isVisible() || btnTinyLeadersDeck.isVisible() || btnBrawlDeck.isVisible()) { + if (btnCommanderDeck.isVisible() || btnOathbreakDeck.isVisible() || btnTinyLeadersDeck.isVisible() || btnBrawlDeck.isVisible() || btnDuelCommanderDeck.isVisible()) { if(Forge.isLandscapeMode()) rows++; } @@ -504,6 +541,7 @@ private void onIsAiChanged(boolean isAi) { lstCommanderDecks.setIsAi(isAi); lstTinyLeadersDecks.setIsAi(isAi); lstBrawlDecks.setIsAi(isAi); + lstDuelCommanderDecks.setIsAi(isAi); lstPlanarDecks.setIsAi(isAi); lstSchemeDecks.setIsAi(isAi); lstVanguardAvatars.setIsAi(isAi); @@ -588,6 +626,9 @@ public void setDeckSelectorButtonText(String text) { if (btnBrawlDeck.isVisible()) btnBrawlDeck.setText(text); + + if (btnDuelCommanderDeck.isVisible()) + btnDuelCommanderDeck.setText(text); } public void setVanguarAvatarName(String text) { @@ -613,6 +654,7 @@ public void updateVariantControlsVisibility() { boolean isOathbreakerApplied = false; boolean isTinyLeadersApplied = false; boolean isBrawlApplied = false; + boolean isDuelCommanderApplied = false; boolean isPlanechaseApplied = false; boolean isVanguardApplied = false; boolean isArchenemyApplied = false; @@ -651,6 +693,11 @@ public void updateVariantControlsVisibility() { isDeckBuildingAllowed = false; //Tiny Leaders deck replaces basic deck, so hide that replacedbasicdeck = true; break; + case DuelCommander: + isDuelCommanderApplied = true; + isDeckBuildingAllowed = false; + replacedbasicdeck = true; + break; case Planechase: isPlanechaseApplied = true; break; @@ -697,6 +744,12 @@ public void updateVariantControlsVisibility() { } else { btnBrawlDeck.setVisible(false); } + if (isDuelCommanderApplied) { + btnDuelCommanderDeck.setVisible(true); + btnDuelCommanderDeck.setEnabled(mayEdit); + } else { + btnDuelCommanderDeck.setVisible(false); + } if (archenemyVisiblity) { btnSchemeDeck.setVisible(true); btnSchemeDeck.setEnabled(mayEdit); @@ -730,6 +783,7 @@ public void updateVariantControlsVisibility() { btnOathbreakDeck.setVisible(isOathbreakerApplied && mayEdit); btnTinyLeadersDeck.setVisible(isTinyLeadersApplied && mayEdit); btnBrawlDeck.setVisible(isBrawlApplied && mayEdit); + btnDuelCommanderDeck.setVisible(isDuelCommanderApplied && mayEdit); btnSchemeDeck.setVisible(archenemyVisiblity && mayEdit); @@ -986,6 +1040,7 @@ public void setMayEdit(boolean mayEdit0) { btnOathbreakDeck.setEnabled(mayEdit); btnTinyLeadersDeck.setEnabled(mayEdit); btnBrawlDeck.setEnabled(mayEdit); + btnDuelCommanderDeck.setEnabled(mayEdit); btnSchemeDeck.setEnabled(mayEdit); btnPlanarDeck.setEnabled(mayEdit); cbArchenemyTeam.setEnabled(mayEdit); @@ -1028,6 +1083,10 @@ public FDeckChooser getBrawlDeckChooser() { return lstBrawlDecks; } + public FDeckChooser getDuelCommanderDeckChooser() { + return lstDuelCommanderDecks; + } + public Deck getDeck() { return deckChooser.getDeck(); } @@ -1042,9 +1101,9 @@ public Deck getTinyLeadersDeck() { return lstTinyLeadersDecks.getDeck(); } - public Deck getBrawlDeck() { - return lstBrawlDecks.getDeck(); - } + public Deck getBrawlDeck() { return lstBrawlDecks.getDeck(); } + + public Deck getDuelCommanderDeck() { return lstDuelCommanderDecks.getDeck(); } public Deck getSchemeDeck() { return lstSchemeDecks.getDeck(); diff --git a/forge-gui/res/defaults/editor.xml b/forge-gui/res/defaults/editor.xml index 558ea9e46a9..24b70008603 100644 --- a/forge-gui/res/defaults/editor.xml +++ b/forge-gui/res/defaults/editor.xml @@ -18,6 +18,7 @@ EDITOR_OATHBREAKER EDITOR_BRAWL EDITOR_TINY_LEADERS + EDITOR_DUEL_COMMANDER EDITOR_DECKGEN diff --git a/forge-gui/res/formats/Casual/DuelCommander.txt b/forge-gui/res/formats/Casual/DuelCommander.txt new file mode 100644 index 00000000000..e3f20293709 --- /dev/null +++ b/forge-gui/res/formats/Casual/DuelCommander.txt @@ -0,0 +1,7 @@ +[format] +Name:Duel Commander +Type:Casual +Subtype:Commander +Order:143 +Banned:Ancestral Recall; Ancient Tomb; Back to Basics; Bazaar of Baghdad; Black Lotus; Capture of Jingzhou; Cavern of Souls; Channel; Chrome Mox; Comet, Stellar Pup; Deadly Rollick; Deflecting Swat; Dig Through Time; Emrakul, the Aeons Torn; Entomb; Fastbond; Field of the Dead; Fierce Guardianship; Flawless Maneuver; Food Chain; Gaea’s Cradle; Genesis Storm; Gifts Ungiven; Grim Monolith; Hermit Druid; Hogaak, Arisen Necropolis; Humility; Imperial Seal; Jeweled Lotus; Karakas; Library of Alexandria; Lion's Eye Diamond; Lotus Petal; Loyal Retainers; Lutri, The Spellchaser; Maddening Hex; Mana Crypt; Mana Drain; Mana Vault; Mishra’s Workshop; Mox Amber; Mox Diamond; Mox Emerald; Mox Jet; Mox Opal; Mox Pearl; Mox Ruby; Mox Sapphire; Mystical Tutor; Natural Order; Necrotic Ooze; Oath of Druids; Price of Progress; Protean Hulk; Ragavan, Nimble Pilferer; Rain of Filth; Scapeshift; Sensei’s Divining Top; Serra's Sanctum; Sol Ring; Strip Mine; Temporal Manipulation; Thassa’s Oracle; The One Ring; The Tabernacle at Pendrell Vale; Timetwister; Time Vault; Time Walk; Time Warp; Tinker; Tolarian Academy; Trazyn The Infinite; Treasure Cruise; Uro, Titan of Nature's Wrath; Vampiric Tutor; Wasteland +BannedAsCommander: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 diff --git a/forge-gui/res/formats/Casual/TinyLeaders.txt b/forge-gui/res/formats/Casual/TinyLeaders.txt new file mode 100644 index 00000000000..b71f63b30a9 --- /dev/null +++ b/forge-gui/res/formats/Casual/TinyLeaders.txt @@ -0,0 +1,7 @@ +[format] +Name:Tiny Leaders +Type:Casual +Subtype:Commander +Order:144 +Banned: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 +BannedAsCommander:Derevi, Empyrial Tactician; Erayo, Soratami Ascendant; Rofellos, Llanowar Emissary diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index b611eb84be0..408038d4055 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -27,6 +27,7 @@ lblCommanderDeckEditor=Commander Deck Editor lblOathbreakerDeckEditor=Oathbreaker Deck Editor lblTinyLeadersDeckEditor=Tiny Leaders Deck Editor lblBrawlDeckEditor=Brawl Deck Editor +lblDuelCommanderDeckEditor=Duel Commander Deck Editor lblDraftDeckEditor=Draft Deck Editor lblSealedDeckEditor=Sealed Deck Editor lblTokenViewer=Token Viewer @@ -485,6 +486,8 @@ lblTinyLeaders=Tiny Leaders lblTinyLeadersDesc=Each player has a legendary \"General\" card which can be cast at any time and determines deck colors. Each card must have mana value less than 4. lblBrawl=Brawl lblBrawlDesc=Each player has a legendary \"General\" card which can be cast at any time and determines deck colors. Only cards legal in Standard may be used. +lblDuelCommander=Duel Commander +lblDuelCommanderDesc=Each player has a legendary \"General\" card which can be cast at any time and determines deck colors. 1v1 form of Commander, 20 life instead of 40. lblPlaneswalker=Planeswalker lblPlaneswalkerDesc=Each player has a Planeswalker card which can be cast at any time. lblPlanechase=Planechase @@ -624,6 +627,7 @@ lblRandomCommanderCard-basedDecks=Random Commander Card-based Decks lblOathbreakerDecks=Oathbreaker Decks lblTinyLeadersDecks=Tiny Leaders Decks lblBrawlDecks=Brawl Decks +lblDuelCommanderDecks=Duel Commander Decks lblSchemeDecks=Scheme Decks lblPlanarDecks=Planar Decks lblPreconstructedDecks=Preconstructed Decks @@ -1171,6 +1175,7 @@ lblCommanderDeckRandomGenerated=Commander Deck: Random Generated Deck lblOathbreakerDeckRandomGenerated=Oathbreaker Deck: Random Generated Deck lblTinyLeadersDeckRandomGenerated=Tiny Leaders Deck: Random Generated Deck lblBrawlDeckRandomGenerated=Brawl Deck: Random Generated Deck +lblDuelCommanderDeckRandomGenerated=Duel Commander Deck: Random Generated Deck lblPlanarDeckRandomGenerated=Planar Deck: Random Generated Deck lblVanguardAvatarRandom=Vanguard Avatar: Random lblNotReady=Not Ready @@ -1179,11 +1184,13 @@ lblDevMode=Dev Mode lblOathbreakerDeck=Oathbreaker Deck lblTinyLeadersDeck=Tiny Leaders Deck lblBrawlDeck=Brawl Deck +lblDuelCommanderDeck=Duel Commander Deck lblSelectDeckFor=Select Deck for %s lblSelectCommanderDeckFor=Select Commander Deck for %s lblSelectOathbreakerDeckFor=Select Oathbreaker Deck for %s lblSelectTinyLeadersDeckFor=Select Tiny Leaders Deck for %s lblSelectBrawlDeckFor=Select Brawl Deck for %s +lblSelectDuelCommanderDeckFor=Select Duel Commander Deck for %s lblSelectSchemeDeckFor=Select Scheme Deck for %s lblSelectPlanarDeckFor=Select Planar Deck for %s lblSelectVanguardFor=Select Vanguard for %s diff --git a/forge-gui/src/main/java/forge/deck/DeckProxy.java b/forge-gui/src/main/java/forge/deck/DeckProxy.java index 38813463449..89aa13dd517 100644 --- a/forge-gui/src/main/java/forge/deck/DeckProxy.java +++ b/forge-gui/src/main/java/forge/deck/DeckProxy.java @@ -493,6 +493,20 @@ public static Iterable getAllBrawlDecks(Predicate filter) { return result; } + public static Iterable getAllDuelCommanderDecks() { + return getAllDuelCommanderDecks(null); + } + public static Iterable getAllDuelCommanderDecks(Predicate filter) { + final List result = new ArrayList<>(); + if (filter == null) { + filter = DeckFormat.DuelCommander.hasLegalCardsPredicate(FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)); + } else { + filter = Predicates.and(DeckFormat.DuelCommander.hasLegalCardsPredicate(FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)), filter); + } + addDecksRecursivelly("Duel Commander", GameType.DuelCommander, result, "", FModel.getDecks().getDuelCommander(), filter); + return result; + } + public static Iterable getAllSchemeDecks() { return getAllSchemeDecks(null); } diff --git a/forge-gui/src/main/java/forge/deck/DeckType.java b/forge-gui/src/main/java/forge/deck/DeckType.java index 2f20910ea2d..54c56790b12 100644 --- a/forge-gui/src/main/java/forge/deck/DeckType.java +++ b/forge-gui/src/main/java/forge/deck/DeckType.java @@ -12,6 +12,7 @@ public enum DeckType { OATHBREAKER_DECK("lblOathbreakerDecks"), TINY_LEADERS_DECK("lblTinyLeadersDecks"), BRAWL_DECK("lblBrawlDecks"), + DUEL_COMMANDER_DECK("lblDuelCommanderDecks"), SCHEME_DECK("lblSchemeDecks"), PLANAR_DECK("lblPlanarDecks"), DRAFT_DECK("lblDraftDecks"), diff --git a/forge-gui/src/main/java/forge/deck/NetDeckCategory.java b/forge-gui/src/main/java/forge/deck/NetDeckCategory.java index d455ec4442b..2c128b0d99b 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckCategory.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckCategory.java @@ -16,7 +16,7 @@ public class NetDeckCategory extends StorageBase { public static final String PREFIX = "NET_DECK_"; - private static Map constructed, commander, brawl, oathbreaker, tinyleaders; + private static Map constructed, commander, brawl, oathbreaker, tinyleaders, duelCommander; private static Map loadCategories(String filename) { Map categories = new TreeMap<>(); @@ -71,6 +71,12 @@ public static NetDeckCategory selectAndLoad(GameType gameType, String name) { } categories = tinyleaders; break; + case DuelCommander: + if (duelCommander == null) { + duelCommander = loadCategories(ForgeConstants.NET_DECKS_DUEL_COMMANDER_LIST_FILE); + } + categories = duelCommander; + break; default: return null; } diff --git a/forge-gui/src/main/java/forge/deck/RandomDeckGenerator.java b/forge-gui/src/main/java/forge/deck/RandomDeckGenerator.java index d4bad4ac7b9..8e6ced7ba75 100644 --- a/forge-gui/src/main/java/forge/deck/RandomDeckGenerator.java +++ b/forge-gui/src/main/java/forge/deck/RandomDeckGenerator.java @@ -85,6 +85,8 @@ private Deck getGeneratedDeck() { return DeckgenUtil.generateCommanderDeck(isAi, GameType.TinyLeaders); case Brawl: return DeckgenUtil.generateCommanderDeck(isAi, GameType.Brawl); + case DuelCommander: + return DeckgenUtil.generateCommanderDeck(isAi, GameType.DuelCommander); case Archenemy: return DeckgenUtil.generateSchemeDeck(); case Planechase: @@ -160,6 +162,9 @@ private Deck getUserDeck() { case Brawl: decks = DeckProxy.getAllBrawlDecks(DeckFormat.Brawl.isLegalDeckPredicate()); break; + case DuelCommander: + decks = DeckProxy.getAllDuelCommanderDecks(DeckFormat.DuelCommander.isLegalDeckPredicate()); + break; case Archenemy: decks = DeckProxy.getAllSchemeDecks(DeckFormat.Archenemy.isLegalDeckPredicate()); break; diff --git a/forge-gui/src/main/java/forge/deck/io/DeckPreferences.java b/forge-gui/src/main/java/forge/deck/io/DeckPreferences.java index 64da675dc60..b81ac5a8a0f 100644 --- a/forge-gui/src/main/java/forge/deck/io/DeckPreferences.java +++ b/forge-gui/src/main/java/forge/deck/io/DeckPreferences.java @@ -23,7 +23,7 @@ */ public class DeckPreferences { private static String selectedDeckType = "", currentDeck = "", draftDeck = "", sealedDeck = "", commanderDeck = "", - oathbreakerDeck = "", tinyLeadersDeck = "", brawlDeck = "", planarDeck = "", schemeDeck = ""; + oathbreakerDeck = "", tinyLeadersDeck = "", brawlDeck = "", duelCommanderDeck = "", planarDeck = "", schemeDeck = ""; private static Map allPrefs = new HashMap<>(); public static DeckType getSelectedDeckType() { @@ -90,15 +90,22 @@ public static void setTinyLeadersDeck(String tinyLeadersDeck0) { save(); } - public static String getBrawlDeck() { - return brawlDeck; - } + public static String getBrawlDeck() { return brawlDeck; } public static void setBrawlDeck(String brawlDeck0) { if (brawlDeck.equals(brawlDeck0)) { return; } brawlDeck = brawlDeck0; save(); } + public static String getDuelCommanderDeck() { + return duelCommanderDeck; + } + public static void setDuelCommanderDeck(String duelCommanderDeck0) { + if (duelCommanderDeck.equals(duelCommanderDeck0)) { return; } + duelCommanderDeck = duelCommanderDeck0; + save(); + } + public static String getPlanarDeck() { return planarDeck; } @@ -139,6 +146,7 @@ public static void load() { oathbreakerDeck = root.getAttribute("oathbreakerDeck"); brawlDeck = root.getAttribute("brawlDeck"); tinyLeadersDeck = root.getAttribute("tinyLeadersDeck"); + duelCommanderDeck = root.getAttribute("duelCommanderDeck"); planarDeck = root.getAttribute("planarDeck"); schemeDeck = root.getAttribute("schemeDeck"); @@ -172,6 +180,7 @@ private static void save() { root.setAttribute("oathbreakerDeck", oathbreakerDeck); root.setAttribute("brawlDeck", brawlDeck); root.setAttribute("tinyLeadersDeck", tinyLeadersDeck); + root.setAttribute("duelCommanderDeck", duelCommanderDeck); root.setAttribute("planarDeck", planarDeck); root.setAttribute("schemeDeck", schemeDeck); document.appendChild(root); diff --git a/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java b/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java index 340a9633b46..41e7b4e57a1 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java @@ -240,6 +240,7 @@ public void applyVariant(final GameType variant) { data.appliedVariants.remove(GameType.Oathbreaker); data.appliedVariants.remove(GameType.TinyLeaders); data.appliedVariants.remove(GameType.Brawl); + data.appliedVariants.remove(GameType.DuelCommander); data.appliedVariants.remove(GameType.MomirBasic); data.appliedVariants.remove(GameType.MoJhoSto); break; @@ -247,6 +248,7 @@ public void applyVariant(final GameType variant) { data.appliedVariants.remove(GameType.Commander); data.appliedVariants.remove(GameType.TinyLeaders); data.appliedVariants.remove(GameType.Brawl); + data.appliedVariants.remove(GameType.DuelCommander); data.appliedVariants.remove(GameType.MomirBasic); data.appliedVariants.remove(GameType.MoJhoSto); break; @@ -254,6 +256,7 @@ public void applyVariant(final GameType variant) { data.appliedVariants.remove(GameType.Commander); data.appliedVariants.remove(GameType.Oathbreaker); data.appliedVariants.remove(GameType.Brawl); + data.appliedVariants.remove(GameType.DuelCommander); data.appliedVariants.remove(GameType.MomirBasic); data.appliedVariants.remove(GameType.MoJhoSto); break; @@ -261,9 +264,18 @@ public void applyVariant(final GameType variant) { data.appliedVariants.remove(GameType.Commander); data.appliedVariants.remove(GameType.Oathbreaker); data.appliedVariants.remove(GameType.TinyLeaders); + data.appliedVariants.remove(GameType.DuelCommander); data.appliedVariants.remove(GameType.MomirBasic); data.appliedVariants.remove(GameType.MoJhoSto); break; + case DuelCommander: + data.appliedVariants.remove(GameType.Commander); + data.appliedVariants.remove(GameType.Oathbreaker); + data.appliedVariants.remove(GameType.TinyLeaders); + data.appliedVariants.remove(GameType.Brawl); + data.appliedVariants.remove(GameType.Vanguard); + data.appliedVariants.remove(GameType.MomirBasic); + break; case Vanguard: data.appliedVariants.remove(GameType.MomirBasic); data.appliedVariants.remove(GameType.MoJhoSto); @@ -273,6 +285,7 @@ public void applyVariant(final GameType variant) { data.appliedVariants.remove(GameType.Oathbreaker); data.appliedVariants.remove(GameType.TinyLeaders); data.appliedVariants.remove(GameType.Brawl); + data.appliedVariants.remove(GameType.DuelCommander); data.appliedVariants.remove(GameType.Vanguard); data.appliedVariants.remove(GameType.MoJhoSto); break; @@ -281,6 +294,7 @@ public void applyVariant(final GameType variant) { data.appliedVariants.remove(GameType.Oathbreaker); data.appliedVariants.remove(GameType.TinyLeaders); data.appliedVariants.remove(GameType.Brawl); + data.appliedVariants.remove(GameType.DuelCommander); data.appliedVariants.remove(GameType.Vanguard); data.appliedVariants.remove(GameType.MomirBasic); break; @@ -305,6 +319,8 @@ public void removeVariant(final GameType variant) { currentGameType = GameType.TinyLeaders; } else if (hasVariant(GameType.Brawl)) { currentGameType = GameType.Brawl; + } else if (hasVariant(GameType.DuelCommander)) { + currentGameType = GameType.DuelCommander; } else { currentGameType = GameType.Constructed; } @@ -368,7 +384,7 @@ public Runnable startGame() { SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblPleaseSpecifyPlayerDeck", slot.getName())); return null; } - if (hasVariant(GameType.Commander) || hasVariant(GameType.Oathbreaker) || hasVariant(GameType.TinyLeaders) || hasVariant(GameType.Brawl)) { + if (hasVariant(GameType.Commander) || hasVariant(GameType.Oathbreaker) || hasVariant(GameType.TinyLeaders) || hasVariant(GameType.Brawl) || hasVariant(GameType.DuelCommander)) { if (!slot.getDeck().has(DeckSection.Commander)) { SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblPlayerDoesntHaveCommander", slot.getName())); return null; @@ -383,11 +399,13 @@ public Runnable startGame() { boolean isOathbreakerMatch = false; boolean isTinyLeadersMatch = false; boolean isBrawlMatch = false; + boolean isDuelCommanderMatch = false; if (!variantTypes.isEmpty()) { isOathbreakerMatch = variantTypes.contains(GameType.Oathbreaker); isTinyLeadersMatch = variantTypes.contains(GameType.TinyLeaders); isBrawlMatch = variantTypes.contains(GameType.Brawl); - isCommanderMatch = isBrawlMatch || isTinyLeadersMatch || isOathbreakerMatch || variantTypes.contains(GameType.Commander); + isDuelCommanderMatch = variantTypes.contains(GameType.DuelCommander); + isCommanderMatch = isBrawlMatch || isTinyLeadersMatch || isOathbreakerMatch || isDuelCommanderMatch ||variantTypes.contains(GameType.Commander); if (!isCommanderMatch) { for (final GameType variant : variantTypes) { if (variant.isAutoGenerated()) { @@ -449,7 +467,8 @@ public Runnable startGame() { isOathbreakerMatch ? GameType.Oathbreaker : isTinyLeadersMatch ? GameType.TinyLeaders : isBrawlMatch ? GameType.Brawl : - GameType.Commander; + isDuelCommanderMatch? GameType.DuelCommander : + GameType.Commander; if (checkLegality) { final String errMsg = commanderGameType.getDeckFormat().getDeckConformanceProblem(deck); if (errMsg != null) { @@ -524,6 +543,14 @@ else if (autoGenerateVariant != null) { player.setStartingLife(25); } } + + //override starting life for 1v1 Duel Commander + if (hasVariant(GameType.DuelCommander) && activeSlots.size() == 2){ + for (RegisteredPlayer player : players){ + player.setStartingLife(20); + } + } + playerToSlot.put(rp, slot); } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java index 7e24021c3d4..05d391aa92d 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java @@ -17,12 +17,6 @@ */ package forge.gamemodes.quest; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; - import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/data/GameFormatQuest.java b/forge-gui/src/main/java/forge/gamemodes/quest/data/GameFormatQuest.java index 54030ccd2c8..98c507b8bbd 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/data/GameFormatQuest.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/data/GameFormatQuest.java @@ -105,8 +105,8 @@ public GameFormatQuest(final String newName, final List setsToAllow, fin * @param setRotation */ public GameFormatQuest(final GameFormat toCopy, boolean allowSetUnlocks, ISetRotation setRotation) { - super(toCopy.getName(), toCopy.getEffectiveDate(), toCopy.getAllowedSetCodes(), toCopy.getBannedCardNames(), toCopy.getRestrictedCards(), - toCopy.isRestrictedLegendary(),toCopy.getAdditionalCards(), toCopy.getAllowedRarities(), + super(toCopy.getName(), toCopy.getEffectiveDate(), toCopy.getAllowedSetCodes(), toCopy.getBannedCardNames(), toCopy.getBannedAsCommanderCardNames(), + toCopy.getRestrictedCards(), toCopy.isRestrictedLegendary(),toCopy.getAdditionalCards(), toCopy.getAllowedRarities(), toCopy.getIndex(), FormatType.CUSTOM, FormatSubType.CUSTOM); allowUnlocks = allowSetUnlocks; this.setRotation = setRotation; diff --git a/forge-gui/src/main/java/forge/localinstance/achievements/ConstructedAchievements.java b/forge-gui/src/main/java/forge/localinstance/achievements/ConstructedAchievements.java index ff99e7cf459..687ce50dcd5 100644 --- a/forge-gui/src/main/java/forge/localinstance/achievements/ConstructedAchievements.java +++ b/forge-gui/src/main/java/forge/localinstance/achievements/ConstructedAchievements.java @@ -18,6 +18,7 @@ protected void addAchievements() { add(new VariantWins(GameType.Oathbreaker, 25, 50, 100)); add(new VariantWins(GameType.TinyLeaders, 25, 50, 100)); add(new VariantWins(GameType.Brawl, 25, 50, 100)); + add(new VariantWins(GameType.DuelCommander, 25, 50, 100)); add(new VariantWins(GameType.Planechase, 25, 50, 100)); add(new VariantWins(GameType.Archenemy, 25, 50, 100)); add(new Poisoned(15, 25, 40)); diff --git a/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java b/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java index 91b275ec3a5..bfe69d69738 100644 --- a/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java +++ b/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java @@ -61,6 +61,7 @@ public final class ForgeConstants { public static final String NET_DECKS_BRAWL_LIST_FILE = LISTS_DIR + "net-decks-brawl.txt"; public static final String NET_DECKS_OATHBREAKER_LIST_FILE = LISTS_DIR + "net-decks-oathbreaker.txt"; public static final String NET_DECKS_TINYLEADERS_LIST_FILE = LISTS_DIR + "net-decks-tinyleaders.txt"; + public static final String NET_DECKS_DUEL_COMMANDER_LIST_FILE = LISTS_DIR + "net-decks-duelcommander.txt"; public static final String BORDERLESS_CARD_LIST_FILE = LISTS_DIR + "borderlessCardList.txt"; public static final String CLASSIC_MODULE_CARD_TO_CROP_FILE = LISTS_DIR + "classicModuleCardtoCrop.txt"; public static final String SKINS_LIST_FILE = LISTS_DIR + "skinsList.txt"; @@ -258,12 +259,13 @@ public final class ForgeConstants { public static final String DECK_COMMANDER_DIR = DECK_BASE_DIR + "commander" + PATH_SEPARATOR; public static final String COMMANDER_PRECON_DIR = QUEST_DIR + "commanderprecons" + PATH_SEPARATOR; public static final String DECK_OATHBREAKER_DIR = DECK_BASE_DIR + "oathbreaker" + PATH_SEPARATOR; + public static final String DECK_TINY_LEADERS_DIR= DECK_BASE_DIR + "tiny_leaders" + PATH_SEPARATOR; + public static final String DECK_BRAWL_DIR = DECK_BASE_DIR + "brawl" + PATH_SEPARATOR; + public static final String DECK_DUEL_COMMANDER_DIR = DECK_BASE_DIR + "duel_commander" + PATH_SEPARATOR; public static final String DECK_NET_DIR = DECK_BASE_DIR + "net" + PATH_SEPARATOR; public static final String DECK_NET_ARCHIVE_DIR = DECK_BASE_DIR + "archive" + PATH_SEPARATOR; public static final String QUEST_SAVE_DIR = USER_QUEST_DIR + "saves" + PATH_SEPARATOR; public static final String CONQUEST_SAVE_DIR = USER_CONQUEST_DIR + "saves" + PATH_SEPARATOR; - public static final String DECK_TINY_LEADERS_DIR= DECK_BASE_DIR + "tiny_leaders" + PATH_SEPARATOR; - public static final String DECK_BRAWL_DIR = DECK_BASE_DIR + "brawl" + PATH_SEPARATOR; public static final String MAIN_PREFS_FILE = USER_PREFS_DIR + "forge.preferences"; public static final String CARD_PREFS_FILE = USER_PREFS_DIR + "card.preferences"; public static final String DECK_PREFS_FILE = USER_PREFS_DIR + "deck.preferences"; diff --git a/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java index 7898f206ff4..49f072d2afe 100644 --- a/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java +++ b/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java @@ -66,7 +66,16 @@ public enum FPref { BRAWL_P5_DECK_STATE(""), BRAWL_P6_DECK_STATE(""), BRAWL_P7_DECK_STATE(""), + BRAWL_P8_DECK_STATE(""), + DUEL_COMMANDER_P1_DECK_STATE(""), + DUEL_COMMANDER_P2_DECK_STATE(""), + DUEL_COMMANDER_P3_DECK_STATE(""), + DUEL_COMMANDER_P4_DECK_STATE(""), + DUEL_COMMANDER_P5_DECK_STATE(""), + DUEL_COMMANDER_P6_DECK_STATE(""), + DUEL_COMMANDER_P7_DECK_STATE(""), + DUEL_COMMANDER_P8_DECK_STATE(""), UI_LANDSCAPE_MODE ("false"), UI_MATCHES_PER_GAME("3"), UI_APPLIED_VARIANTS(""), @@ -324,6 +333,12 @@ public String getDefault() { BRAWL_P5_DECK_STATE, BRAWL_P6_DECK_STATE, BRAWL_P7_DECK_STATE, BRAWL_P8_DECK_STATE }; + public static FPref[] DUEL_COMMANDER_DECK_STATES = { + DUEL_COMMANDER_P1_DECK_STATE, DUEL_COMMANDER_P2_DECK_STATE, + DUEL_COMMANDER_P3_DECK_STATE, DUEL_COMMANDER_P4_DECK_STATE, + DUEL_COMMANDER_P5_DECK_STATE, DUEL_COMMANDER_P6_DECK_STATE, + DUEL_COMMANDER_P7_DECK_STATE, DUEL_COMMANDER_P8_DECK_STATE }; + } /** Instantiates a ForgePreferences object. */ diff --git a/forge-gui/src/main/java/forge/localinstance/properties/PreferencesStore.java b/forge-gui/src/main/java/forge/localinstance/properties/PreferencesStore.java index 8fc414e91d6..df347bffbde 100644 --- a/forge-gui/src/main/java/forge/localinstance/properties/PreferencesStore.java +++ b/forge-gui/src/main/java/forge/localinstance/properties/PreferencesStore.java @@ -167,6 +167,8 @@ else if (gameType.equals("Tiny Leaders")) result.add(GameType.TinyLeaders); else if (gameType.equals("Brawl")) result.add(GameType.Brawl); + else if (gameType.equals("Duel Commander")) + result.add(GameType.DuelCommander); else if (gameType.equals("Planechase")) result.add(GameType.Planechase); else if (gameType.equals("Archenemy")) diff --git a/forge-gui/src/main/java/forge/model/CardCollections.java b/forge-gui/src/main/java/forge/model/CardCollections.java index 07af6fb9548..38945258fa3 100644 --- a/forge-gui/src/main/java/forge/model/CardCollections.java +++ b/forge-gui/src/main/java/forge/model/CardCollections.java @@ -45,6 +45,7 @@ public class CardCollections { private IStorage oathbreaker; private IStorage tinyLeaders; private IStorage brawl; + private IStorage duelCommander; private IStorage genetic; private IStorage customStarter; @@ -148,6 +149,14 @@ public IStorage getBrawl() { return brawl; } + public IStorage getDuelCommander() { + if (duelCommander == null) { + duelCommander = new StorageImmediatelySerialized<>("Duel Commander decks", + new DeckStorage(new File(ForgeConstants.DECK_DUEL_COMMANDER_DIR), ForgeConstants.DECK_BASE_DIR)); + } + return duelCommander; + } + public final IStorage getGeneticAIDecks() { if (genetic == null) { genetic = new StorageImmediatelySerialized<>("Genetic AI decks", diff --git a/forge-gui/src/main/java/forge/model/FModel.java b/forge-gui/src/main/java/forge/model/FModel.java index 02d352fb8ef..abb60574e74 100644 --- a/forge-gui/src/main/java/forge/model/FModel.java +++ b/forge-gui/src/main/java/forge/model/FModel.java @@ -213,6 +213,10 @@ public void report(final int current, final int total) { magicDb.setCommanderPredicate(formats.get("Commander").getFilterRules()); magicDb.setOathbreakerPredicate(formats.get("Oathbreaker").getFilterRules()); magicDb.setBrawlPredicate(formats.get("Brawl").getFilterRules()); + magicDb.setTinyLeadersPredicate(formats.get("Tiny Leaders").getFilterRules()); + magicDb.setTinyLeadersAllowedAsCommanderPredicate(formats.get("Tiny Leaders").getFilterAllowedAsCommander()); + magicDb.setDuelCommanderPredicate(formats.get("Duel Commander").getFilterRules()); + magicDb.setDuelCommanderAllowedAsCommanderPredicate(formats.get("Duel Commander").getFilterAllowedAsCommander()); magicDb.setFilteredHandsEnabled(preferences.getPrefBoolean(FPref.FILTERED_HANDS)); try { diff --git a/forge-lda/src/main/java/forge/lda/LDAModelGenetrator.java b/forge-lda/src/main/java/forge/lda/LDAModelGenetrator.java index 8a83ae5725e..dcb81740e30 100644 --- a/forge-lda/src/main/java/forge/lda/LDAModelGenetrator.java +++ b/forge-lda/src/main/java/forge/lda/LDAModelGenetrator.java @@ -305,9 +305,7 @@ public static HashMap>> initializeComma } //filter to just legal commanders - List legends = Lists.newArrayList(Iterables.filter(cardList,Predicates.compose( - DeckFormat.Commander::isLegalCommander, PaperCard::getRules - ))); + List legends = Lists.newArrayList(Iterables.filter(cardList, DeckFormat.Commander::isLegalCommander)); //generate lookups for legends to link commander names to matrix rows for (int i=0; i