diff --git a/forge-core/src/main/java/forge/deck/generation/DeckGenerator4Color.java b/forge-core/src/main/java/forge/deck/generation/DeckGenerator4Color.java
new file mode 100644
index 00000000000..1a0e0b36c94
--- /dev/null
+++ b/forge-core/src/main/java/forge/deck/generation/DeckGenerator4Color.java
@@ -0,0 +1,134 @@
+/*
+ * Forge: Play Magic: the Gathering.
+ * Copyright (C) 2011 Forge Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package forge.deck.generation;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Lists;
+import forge.card.ColorSet;
+import forge.card.MagicColor;
+import forge.deck.CardPool;
+import forge.deck.DeckFormat;
+import forge.item.PaperCard;
+import forge.util.MyRandom;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
+import java.util.List;
+
+/**
+ *
+ * Generate3ColorDeck class.
+ *
+ *
+ * @author Forge
+ * @version $Id$
+ */
+public class DeckGenerator4Color extends DeckGeneratorBase {
+ @Override
+ protected final float getLandPercentage() {
+ return 0.44f;
+ }
+ @Override
+ protected final float getCreaturePercentage() {
+ return 0.33f;
+ }
+ @Override
+ protected final float getSpellPercentage() {
+ return 0.23f;
+ }
+
+ @SuppressWarnings("unchecked")
+ final List> cmcLevels = Lists.newArrayList(
+ ImmutablePair.of(new FilterCMC(0, 2), 12),
+ ImmutablePair.of(new FilterCMC(3, 5), 9),
+ ImmutablePair.of(new FilterCMC(6, 20), 3)
+ );
+
+ public DeckGenerator4Color(IDeckGenPool pool0, DeckFormat format0, Predicate formatFilter0, final String clr1, final String clr2, final String clr3, final String clr4) {
+ super(pool0, format0, formatFilter0);
+ initialize(format0,clr1,clr2,clr3,clr4);
+ }
+
+ public DeckGenerator4Color(IDeckGenPool pool0, DeckFormat format0, final String clr1, final String clr2, final String clr3, final String clr4) {
+ super(pool0, format0);
+ initialize(format0,clr1,clr2,clr3,clr4);
+ }
+
+ private void initialize(DeckFormat format0, final String clr1, final String clr2, final String clr3, final String clr4){
+ format0.adjustCMCLevels(cmcLevels);
+
+ int c1 = MagicColor.fromName(clr1);
+ int c2 = MagicColor.fromName(clr2);
+ int c3 = MagicColor.fromName(clr3);
+ int c4 = MagicColor.fromName(clr4);
+
+ int rc = 0;
+ int combo = c1 | c2 | c3 | c4;
+
+ ColorSet param = ColorSet.fromMask(combo);
+ switch(param.countColors()) {
+ case 3:
+ colors = param;
+ return;
+
+ case 0:
+ int color1 = r.nextInt(5);
+ int color2 = (color1 + 1 + r.nextInt(4)) % 5;
+ colors = ColorSet.fromMask(MagicColor.WHITE << color1 | MagicColor.WHITE << color2).inverse();
+ return;
+
+ case 1:
+ do {
+ rc = MagicColor.WHITE << MyRandom.getRandom().nextInt(5);
+ } while ( rc == combo );
+ combo |= rc;
+
+ //$FALL-THROUGH$
+ case 2:
+ do {
+ rc = MagicColor.WHITE << MyRandom.getRandom().nextInt(5);
+ } while ( (rc & combo) != 0 );
+ combo |= rc;
+ break;
+ }
+ colors = ColorSet.fromMask(combo);
+ }
+
+ @Override
+ public final CardPool getDeck(final int size, final boolean forAi) {
+ addCreaturesAndSpells(size, cmcLevels, forAi);
+
+ // Add lands
+ int numLands = Math.round(size * getLandPercentage());
+ adjustDeckSize(size - numLands);
+ trace.append("numLands:").append(numLands).append("\n");
+
+ // Add dual lands
+ List duals = getDualLandList();
+ for (String s : duals) {
+ this.cardCounts.put(s, 0);
+ }
+
+ int dblsAdded = addSomeStr((numLands / 4), duals);
+ numLands -= dblsAdded;
+
+ addBasicLand(numLands);
+ adjustDeckSize(size);
+ trace.append("DeckSize:").append(tDeck.countAll()).append("\n");
+ return tDeck;
+ }
+}
diff --git a/forge-gui-desktop/src/main/java/forge/deckchooser/DecksComboBox.java b/forge-gui-desktop/src/main/java/forge/deckchooser/DecksComboBox.java
index 82385e0bdc4..3d40e0efac1 100644
--- a/forge-gui-desktop/src/main/java/forge/deckchooser/DecksComboBox.java
+++ b/forge-gui-desktop/src/main/java/forge/deckchooser/DecksComboBox.java
@@ -25,8 +25,12 @@ public class DecksComboBox extends FComboBoxWrapper {
addActionListener(getDeckTypeComboListener());
}
- public void refresh(final DeckType deckType) {
- setModel(new DefaultComboBoxModel(DeckType.ConstructedOptions));
+ public void refresh(final DeckType deckType, final boolean isForCommander) {
+ if(isForCommander){
+ setModel(new DefaultComboBoxModel(DeckType.CommanderOptions));
+ }else {
+ setModel(new DefaultComboBoxModel(DeckType.ConstructedOptions));
+ }
setSelectedItem(deckType);
}
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 42fa4a971c7..e54f444b9bf 100644
--- a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java
+++ b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java
@@ -43,6 +43,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
private ItemManagerContainer lstDecksContainer;
private NetDeckCategory netDeckCategory;
private boolean refreshingDeckType;
+ private boolean isForCommander;
private final DeckManager lstDecks;
private final FLabel btnViewDeck = new FLabel.ButtonBuilder().text("View Deck").fontSize(14).build();
@@ -56,7 +57,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
//Show dialog to select a deck
public static Deck promptForDeck(final CDetailPicture cDetailPicture, final String title, final DeckType defaultDeckType, final boolean forAi) {
FThreads.assertExecutedByEdt(true);
- final FDeckChooser chooser = new FDeckChooser(cDetailPicture, forAi);
+ final FDeckChooser chooser = new FDeckChooser(cDetailPicture, forAi, GameType.Constructed, false);
chooser.initialize(defaultDeckType);
chooser.populate();
final Dimension parentSize = JOptionPane.getRootFrame().getSize();
@@ -78,10 +79,11 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
return null;
}
- public FDeckChooser(final CDetailPicture cDetailPicture, final boolean forAi) {
- lstDecks = new DeckManager(GameType.Constructed, cDetailPicture);
+ public FDeckChooser(final CDetailPicture cDetailPicture, final boolean forAi, GameType gameType, boolean forCommander) {
+ lstDecks = new DeckManager(gameType, cDetailPicture);
setOpaque(false);
isAi = forAi;
+ isForCommander = forCommander;
final UiCommand cmdViewDeck = new UiCommand() {
@Override public void run() {
if (selectedDeckType != DeckType.COLOR_DECK && selectedDeckType != DeckType.THEME_DECK) {
@@ -129,7 +131,14 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
}
private void updateCustom() {
- updateDecks(DeckProxy.getAllConstructedDecks(), ItemManagerConfig.CONSTRUCTED_DECKS);
+ DeckFormat deckFormat = lstDecks.getGameType().getDeckFormat();
+ if(deckFormat.equals(DeckFormat.Commander)){
+ updateDecks(DeckProxy.getAllCommanderDecks(), ItemManagerConfig.COMMANDER_DECKS);
+ }else if(deckFormat.equals(DeckFormat.TinyLeaders)){
+ updateDecks(DeckProxy.getAllTinyLeadersDecks(), ItemManagerConfig.COMMANDER_DECKS);
+ }else {
+ updateDecks(DeckProxy.getAllConstructedDecks(), ItemManagerConfig.CONSTRUCTED_DECKS);
+ }
}
private void updateColors(Predicate formatFilter) {
@@ -168,6 +177,49 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
lstDecks.setSelectedIndices(new Integer[]{0});
}
+ private void updateRandomCommander() {
+ if((!lstDecks.getGameType().getDeckFormat().equals(DeckFormat.Commander)&&
+ !(lstDecks.getGameType().getDeckFormat().equals(DeckFormat.TinyLeaders)))){
+ return;
+ }
+ lstDecks.setAllowMultipleSelections(false);
+
+ lstDecks.setPool(CommanderDeckGenerator.getCommanderDecks(lstDecks.getGameType().getDeckFormat(), isAi, false));
+ lstDecks.setup(ItemManagerConfig.STRING_ONLY);
+
+ btnRandom.setText("Random");
+ btnRandom.setCommand(new UiCommand() {
+ @Override
+ public void run() {
+ DeckgenUtil.randomSelect(lstDecks);
+ }
+ });
+
+ // default selection = basic two color deck
+ lstDecks.setSelectedIndices(new Integer[]{0});
+ }
+
+ private void updateRandomCardGenCommander() {
+ if((!lstDecks.getGameType().getDeckFormat().equals(DeckFormat.Commander)&&
+ !(lstDecks.getGameType().getDeckFormat().equals(DeckFormat.TinyLeaders)))){
+ return;
+ }
+ lstDecks.setAllowMultipleSelections(false);
+ lstDecks.setPool(CommanderDeckGenerator.getCommanderDecks(lstDecks.getGameType().getDeckFormat(), isAi, true));
+ lstDecks.setup(ItemManagerConfig.STRING_ONLY);
+
+ btnRandom.setText("Random");
+ btnRandom.setCommand(new UiCommand() {
+ @Override
+ public void run() {
+ DeckgenUtil.randomSelect(lstDecks);
+ }
+ });
+
+ // default selection = basic two color deck
+ lstDecks.setSelectedIndices(new Integer[]{0});
+ }
+
private void updateThemes() {
updateDecks(DeckProxy.getAllThemeDecks(), ItemManagerConfig.STRING_ONLY);
}
@@ -192,9 +244,6 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
}
public Deck getDeck() {
- /*if(selectedDeckType.equals(DeckType.STANDARD_CARDGEN_DECK)){
- return DeckgenUtil.buildCardGenDeck(lstDecks.getSelectedItem().getName(),Predicate formatFilter);
- }*/
final DeckProxy proxy = lstDecks.getSelectedItem();
if (proxy == null) {
return null;
@@ -250,7 +299,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
@Override
public void deckTypeSelected(final DecksComboBoxEvent ev) {
- if (ev.getDeckType() == DeckType.NET_DECK && !refreshingDeckType) {
+ if ((ev.getDeckType() == DeckType.NET_DECK || ev.getDeckType() == DeckType.NET_COMMANDER_DECK) && !refreshingDeckType) {
FThreads.invokeInBackgroundThread(new Runnable() { //needed for loading net decks
@Override
public void run() {
@@ -268,7 +317,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
}
netDeckCategory = category;
- refreshDecksList(DeckType.NET_DECK, true, ev);
+ refreshDecksList(ev.getDeckType(), true, ev);
}
});
}
@@ -285,7 +334,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
if (ev == null) {
refreshingDeckType = true;
- decksComboBox.refresh(deckType);
+ decksComboBox.refresh(deckType, isForCommander);
refreshingDeckType = false;
}
lstDecks.setCaption(deckType.toString());
@@ -294,6 +343,9 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
case CUSTOM_DECK:
updateCustom();
break;
+ case COMMANDER_DECK:
+ updateCustom();
+ break;
case COLOR_DECK:
updateColors(null);
break;
@@ -304,10 +356,22 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
updateColors(FModel.getFormats().getModern().getFilterPrinted());
break;
case STANDARD_CARDGEN_DECK:
- updateMatrix(FModel.getFormats().getStandard());
+ if(FModel.isdeckGenMatrixLoaded()) {
+ updateMatrix(FModel.getFormats().getStandard());
+ }
break;
case MODERN_CARDGEN_DECK:
- updateMatrix(FModel.getFormats().getModern());
+ if(FModel.isdeckGenMatrixLoaded()) {
+ updateMatrix(FModel.getFormats().getModern());
+ }
+ break;
+ case RANDOM_COMMANDER_DECK:
+ updateRandomCommander();
+ break;
+ case RANDOM_CARDGEN_COMMANDER_DECK:
+ if(FModel.isdeckGenMatrixLoaded()) {
+ updateRandomCardGenCommander();
+ }
break;
case THEME_DECK:
updateThemes();
@@ -324,6 +388,9 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
case NET_DECK:
updateNetDecks();
break;
+ case NET_COMMANDER_DECK:
+ updateNetDecks();
+ break;
default:
break; //other deck types not currently supported here
}
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java
index 20b8b04390d..d76f52543eb 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java
@@ -8,6 +8,7 @@ import javax.swing.SwingUtilities;
import com.google.common.collect.Iterables;
+import forge.deck.CommanderDeckGenerator;
import forge.deck.DeckProxy;
import forge.deck.DeckType;
import forge.deckchooser.DecksComboBoxEvent;
@@ -23,6 +24,7 @@ public class CLobby {
private final VLobby view;
public CLobby(final VLobby view) {
this.view = view;
+ this.view.setForCommander(true);
}
private void addDecks(final Iterable commanderDecks, FList deckList, String... initialItems) {
@@ -50,14 +52,10 @@ public class CLobby {
public void update() {
SwingUtilities.invokeLater(new Runnable() {
@Override public final void run() {
- final Iterable commanderDecks = DeckProxy.getAllCommanderDecks();
- final Iterable tinyLeadersDecks = DeckProxy.getAllTinyLeadersDecks();
final Iterable schemeDecks = DeckProxy.getAllSchemeDecks();
final Iterable planarDecks = DeckProxy.getAllPlanarDecks();
for (int i = 0; i < VLobby.MAX_PLAYERS; i++) {
- addDecks(commanderDecks, view.getCommanderDeckLists().get(i));
- addDecks(tinyLeadersDecks, view.getTinyLeadersDeckLists().get(i));
addDecks(schemeDecks, view.getSchemeDeckLists().get(i),
"Use deck's scheme section (random if unavailable)");
addDecks(planarDecks, view.getPlanarDeckLists().get(i),
@@ -76,7 +74,23 @@ public class CLobby {
final FDeckChooser fdc = view.getDeckChooser(iSlot);
fdc.initialize(FPref.CONSTRUCTED_DECK_STATES[iSlot], defaultDeckTypeForSlot(iSlot));
fdc.populate();
- fdc.getDecksComboBox().addListener(new IDecksComboBoxListener() {
+ /*fdc.getDecksComboBox().addListener(new IDecksComboBoxListener() {
+ @Override public final void deckTypeSelected(final DecksComboBoxEvent ev) {
+ view.focusOnAvatar();
+ }
+ });*/
+ final FDeckChooser fdccom = view.getCommanderDeckChooser(iSlot);
+ fdccom.initialize(FPref.COMMANDER_DECK_STATES[iSlot], defaultDeckTypeForCommanderSlot(iSlot));
+ fdccom.populate();
+ fdccom.getDecksComboBox().addListener(new IDecksComboBoxListener() {
+ @Override public final void deckTypeSelected(final DecksComboBoxEvent ev) {
+ view.focusOnAvatar();
+ }
+ });
+ final FDeckChooser fdtlcom = view.getTinyLeaderDeckChooser(iSlot);
+ fdtlcom.initialize(FPref.TINY_LEADER_DECK_STATES[iSlot], defaultDeckTypeForTinyLeaderSlot(iSlot));
+ fdtlcom.populate();
+ fdtlcom.getDecksComboBox().addListener(new IDecksComboBoxListener() {
@Override public final void deckTypeSelected(final DecksComboBoxEvent ev) {
view.focusOnAvatar();
}
@@ -109,4 +123,12 @@ public class CLobby {
private static DeckType defaultDeckTypeForSlot(final int iSlot) {
return iSlot == 0 ? DeckType.PRECONSTRUCTED_DECK : DeckType.COLOR_DECK;
}
+
+ private static DeckType defaultDeckTypeForCommanderSlot(final int iSlot) {
+ return iSlot == 0 ? DeckType.COMMANDER_DECK : DeckType.RANDOM_CARDGEN_COMMANDER_DECK;
+ }
+
+ private static DeckType defaultDeckTypeForTinyLeaderSlot(final int iSlot) {
+ return iSlot == 0 ? DeckType.TINY_LEADERS_DECKS : DeckType.RANDOM_CARDGEN_COMMANDER_DECK;
+ }
}
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 a3b3907c63e..9e8fc2f93d9 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
@@ -116,6 +116,8 @@ public class VLobby implements ILobbyView {
private final List> tinyLeadersDeckLists = new ArrayList>();
private final List tinyLeadersDeckPanels = new ArrayList(MAX_PLAYERS);
+ private final List commanderDeckChoosers = Lists.newArrayListWithCapacity(MAX_PLAYERS);
+ private final List tinyLeadersDeckChoosers = Lists.newArrayListWithCapacity(MAX_PLAYERS);
private final List> schemeDeckLists = new ArrayList>();
private final List schemeDeckPanels = new ArrayList(MAX_PLAYERS);
@@ -133,6 +135,16 @@ public class VLobby implements ILobbyView {
private final Vector humanListData = new Vector();
private final Vector aiListData = new Vector();
+ public boolean isForCommander() {
+ return isForCommander;
+ }
+
+ public void setForCommander(boolean forCommander) {
+ isForCommander = forCommander;
+ }
+
+ private boolean isForCommander = false;
+
// CTR
public VLobby(final GameLobby lobby) {
this.lobby = lobby;
@@ -202,6 +214,10 @@ public class VLobby implements ILobbyView {
for (int iPlayer = 0; iPlayer < activePlayersNum; iPlayer++) {
final FDeckChooser fdc = getDeckChooser(iPlayer);
fdc.restoreSavedState();
+ final FDeckChooser fdcom = getCommanderDeckChooser(iPlayer);
+ fdcom.restoreSavedState();
+ final FDeckChooser fdtl = getTinyLeaderDeckChooser(iPlayer);
+ fdtl.restoreSavedState();
}
}
@@ -226,6 +242,7 @@ public class VLobby implements ILobbyView {
// visible panels
final LobbySlot slot = lobby.getSlot(i);
final FDeckChooser deckChooser = getDeckChooser(i);
+ final FDeckChooser commanderDeckChooser = getCommanderDeckChooser(i);
final PlayerPanel panel;
final boolean isNewPanel;
if (hasPanel) {
@@ -240,6 +257,7 @@ public class VLobby implements ILobbyView {
}
playersScroll.add(panel, constraints);
deckChooser.restoreSavedState();
+ commanderDeckChooser.restoreSavedState();
if (i == 0) {
changePlayerFocus(0);
}
@@ -331,7 +349,7 @@ public class VLobby implements ILobbyView {
@SuppressWarnings("serial")
private void buildDeckPanels(final int playerIndex) {
// Main deck
- final FDeckChooser mainChooser = new FDeckChooser(null, false);
+ final FDeckChooser mainChooser = new FDeckChooser(null, false, GameType.Constructed, false);
mainChooser.getLstDecks().setSelectCommand(new UiCommand() {
@Override public final void run() {
selectMainDeck(playerIndex);
@@ -348,19 +366,36 @@ public class VLobby implements ILobbyView {
});
// Commander deck list
- buildDeckPanel("Commander Deck", playerIndex, commanderDeckLists, commanderDeckPanels, new ListSelectionListener() {
+ /*buildDeckPanel("Commander Deck", playerIndex, commanderDeckLists, commanderDeckPanels, new ListSelectionListener() {
@Override public final void valueChanged(final ListSelectionEvent e) {
selectCommanderDeck(playerIndex);
}
+ });*/
+ final FDeckChooser commanderChooser = new FDeckChooser(null, false, GameType.Commander, true);
+ commanderChooser.getLstDecks().setSelectCommand(new UiCommand() {
+ @Override public final void run() {
+ selectCommanderDeck(playerIndex);
+ }
});
+ commanderChooser.initialize();
+ commanderDeckChoosers.add(commanderChooser);
+
+ final FDeckChooser tinyLeaderChooser = new FDeckChooser(null, false, GameType.TinyLeaders, true);
+ tinyLeaderChooser.getLstDecks().setSelectCommand(new UiCommand() {
+ @Override public final void run() {
+ selectTinyLeadersDeck(playerIndex);
+ }
+ });
+ tinyLeaderChooser.initialize();
+ tinyLeadersDeckChoosers.add(tinyLeaderChooser);
- // Tiny Leaders deck list
+/* // Tiny Leaders deck list
buildDeckPanel("Tiny Leaders Deck", playerIndex, tinyLeadersDeckLists, tinyLeadersDeckPanels, new ListSelectionListener() {
@Override public final void valueChanged(final ListSelectionEvent e) {
selectTinyLeadersDeck(playerIndex);
}
- });
+ });*/
// Planar deck list
buildDeckPanel("Planar Deck", playerIndex, planarDeckLists, planarDeckPanels, new ListSelectionListener() {
@@ -432,6 +467,42 @@ public class VLobby implements ILobbyView {
mainChooser.saveState();
}
+ private void selectCommanderDeck(final int playerIndex) {
+ if (!hasVariant(GameType.Commander)) {
+ // Only these game types use this specific deck panel
+ return;
+ }
+ final FDeckChooser mainChooser = getCommanderDeckChooser(playerIndex);
+ final DeckType type = mainChooser.getSelectedDeckType();
+ final Deck deck = mainChooser.getDeck();
+ final Collection selectedDecks = mainChooser.getLstDecks().getSelectedItems();
+ if (playerIndex < activePlayersNum && lobby.mayEdit(playerIndex)) {
+ final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME);
+ playerPanels.get(playerIndex).setCommanderDeckSelectorButtonText(text);
+ fireDeckChangeListener(playerIndex, deck);
+ }
+ mainChooser.saveState();
+ }
+
+ private void selectTinyLeadersDeck(final int playerIndex) {
+ if (!hasVariant(GameType.TinyLeaders)) {
+ // Only these game types use this specific deck panel
+ return;
+ }
+ final FDeckChooser mainChooser = getTinyLeaderDeckChooser(playerIndex);
+ final DeckType type = mainChooser.getSelectedDeckType();
+ final Deck deck = mainChooser.getDeck();
+ final Collection selectedDecks = mainChooser.getLstDecks().getSelectedItems();
+ if (playerIndex < activePlayersNum && lobby.mayEdit(playerIndex)) {
+ final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME);
+ playerPanels.get(playerIndex).setCommanderDeckSelectorButtonText(text);
+ fireDeckChangeListener(playerIndex, deck);
+ }
+ mainChooser.saveState();
+ }
+
+
+
private void selectSchemeDeck(final int playerIndex) {
if (playerIndex >= activePlayersNum || !(hasVariant(GameType.Archenemy) || hasVariant(GameType.ArchenemyRumble))) {
return;
@@ -463,36 +534,6 @@ public class VLobby implements ILobbyView {
getDeckChooser(playerIndex).saveState();
}
- private void selectCommanderDeck(final int playerIndex) {
- selectCommanderOrTinyLeadersDeck(playerIndex, GameType.Commander, getCommanderDeckLists());
- }
-
- private void selectTinyLeadersDeck(final int playerIndex) {
- selectCommanderOrTinyLeadersDeck(playerIndex, GameType.TinyLeaders, getTinyLeadersDeckLists());
- }
-
- private void selectCommanderOrTinyLeadersDeck(final int playerIndex, final GameType gameType, final List> deckLists) {
- if (playerIndex >= activePlayersNum || !hasVariant(gameType)) {
- return;
- }
-
- final Object selected = deckLists.get(playerIndex).getSelectedValue();
- Deck deck = null;
- if (selected instanceof String) {
- if (selected.equals("Random")) {
- deck = RandomDeckGenerator.getRandomUserDeck(lobby, playerPanels.get(playerIndex).isAi());
- }
- } else if (selected instanceof Deck) {
- deck = (Deck) selected;
- }
- if (deck == null) { //Can be null if player deselects the list selection or chose Generate
- deck = DeckgenUtil.generateCommanderDeck(isPlayerAI(playerIndex), gameType);
- }
- playerPanels.get(playerIndex).setCommanderDeckSelectorButtonText(deck.getName());
- fireDeckChangeListener(playerIndex, deck);
- getDeckChooser(playerIndex).saveState();
- }
-
private void selectPlanarDeck(final int playerIndex) {
if (playerIndex >= activePlayersNum || !hasVariant(GameType.Planechase)) {
return;
@@ -594,10 +635,10 @@ public class VLobby implements ILobbyView {
}
break;
case Commander:
- decksFrame.add(commanderDeckPanels.get(playerWithFocus), "grow, push");
+ decksFrame.add(commanderDeckChoosers.get(playerWithFocus), "grow, push");
break;
case TinyLeaders:
- decksFrame.add(tinyLeadersDeckPanels.get(playerWithFocus), "grow, push");
+ decksFrame.add(tinyLeadersDeckChoosers.get(playerWithFocus), "grow, push");
break;
case Planechase:
decksFrame.add(planarDeckPanels.get(playerWithFocus), "grow, push");
@@ -642,6 +683,14 @@ public class VLobby implements ILobbyView {
return deckChoosers.get(playernum);
}
+ public final FDeckChooser getCommanderDeckChooser(final int playernum) {
+ return commanderDeckChoosers.get(playernum);
+ }
+
+ public final FDeckChooser getTinyLeaderDeckChooser(final int playernum) {
+ return tinyLeadersDeckChoosers.get(playernum);
+ }
+
GameType getCurrentGameMode() {
return lobby.getGameType();
}
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletBuild.java b/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletBuild.java
index ecad23741e8..a6f715c2ea4 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletBuild.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletBuild.java
@@ -2,6 +2,7 @@ package forge.screens.home.gauntlet;
import forge.assets.FSkinProp;
import forge.deckchooser.FDeckChooser;
+import forge.game.GameType;
import forge.gauntlet.GauntletIO;
import forge.gui.framework.DragCell;
import forge.gui.framework.DragTab;
@@ -40,7 +41,7 @@ public enum VSubmenuGauntletBuild implements IVSubmenu {
private final JPanel pnlStrut = new JPanel();
private final JPanel pnlDirections = new JPanel();
- private final FDeckChooser lstLeft = new FDeckChooser(null, false);
+ private final FDeckChooser lstLeft = new FDeckChooser(null, false, GameType.Constructed, false);
private final JList lstRight = new FList();
private final FScrollPane scrRight = new FScrollPane(lstRight, true,
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletContests.java b/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletContests.java
index 1c280b3e0c5..68f48dc66c6 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletContests.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletContests.java
@@ -1,6 +1,7 @@
package forge.screens.home.gauntlet;
import forge.deckchooser.FDeckChooser;
+import forge.game.GameType;
import forge.gui.framework.DragCell;
import forge.gui.framework.DragTab;
import forge.gui.framework.EDocID;
@@ -42,7 +43,7 @@ public enum VSubmenuGauntletContests implements IVSubmenu {
private final JCheckBox boxModernColorDecks = new FCheckBox(DeckType.MODERN_COLOR_DECK.toString());
private final JCheckBox boxThemeDecks = new FCheckBox(DeckType.THEME_DECK.toString());
- private final FDeckChooser lstDecks = new FDeckChooser(null, false);
+ private final FDeckChooser lstDecks = new FDeckChooser(null, false, GameType.Constructed, false);
private final FLabel lblOptions = new FLabel.Builder().fontSize(16)
.fontStyle(Font.BOLD).text("OPTIONS").fontAlign(SwingConstants.CENTER).build();
@@ -85,7 +86,7 @@ public enum VSubmenuGauntletQuick implements IVSubmenu {
boxThemeDecks.setSelected(true);
boxColorDecks.setSelected(true);
boxStandardColorDecks.setSelected(true);
- if(!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY)) {
+ if(FModel.isdeckGenMatrixLoaded()) {
boxStandardCardgenDecks.setSelected(true);
boxModernCardgenDecks.setSelected(true);
}else{
@@ -114,7 +115,7 @@ public enum VSubmenuGauntletQuick implements IVSubmenu {
pnlOptions.add(boxQuestDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
pnlOptions.add(boxThemeDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
pnlOptions.add(boxColorDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
- if(!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY)) {
+ if(FModel.isdeckGenMatrixLoaded()) {
pnlOptions.add(boxStandardCardgenDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
pnlOptions.add(boxModernCardgenDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
}
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java
index 8e676b99abe..883c5843003 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java
@@ -96,6 +96,7 @@ public enum CSubmenuPreferences implements ICDoc {
lstControls.add(Pair.of(view.getCbEnforceDeckLegality(), FPref.ENFORCE_DECK_LEGALITY));
lstControls.add(Pair.of(view.getCbCloneImgSource(), FPref.UI_CLONE_MODE_SOURCE));
lstControls.add(Pair.of(view.getCbRemoveSmall(), FPref.DECKGEN_NOSMALL));
+ lstControls.add(Pair.of(view.getCbCardBased(), FPref.DECKGEN_CARDBASED));
lstControls.add(Pair.of(view.getCbRemoveArtifacts(), FPref.DECKGEN_ARTIFACTS));
lstControls.add(Pair.of(view.getCbSingletons(), FPref.DECKGEN_SINGLETONS));
lstControls.add(Pair.of(view.getCbEnableAICheats(), FPref.UI_ENABLE_AI_CHEATS));
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java
index 10014f2bdb3..5c5052c4d7b 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java
@@ -55,6 +55,7 @@ public enum VSubmenuPreferences implements IVSubmenu {
private final FLabel btnPlayerName = new FLabel.Builder().opaque(true).hoverable(true).text("").build();
private final JCheckBox cbRemoveSmall = new OptionsCheckBox("Remove Small Creatures");
+ private final JCheckBox cbCardBased = new OptionsCheckBox("Include Card-based Deck Generation");
private final JCheckBox cbSingletons = new OptionsCheckBox("Singleton Mode");
private final JCheckBox cbRemoveArtifacts = new OptionsCheckBox("Remove Artifacts");
private final JCheckBox cbAnte = new OptionsCheckBox("Play for Ante");
@@ -211,6 +212,9 @@ public enum VSubmenuPreferences implements IVSubmenu {
pnlPrefs.add(cbRemoveArtifacts, titleConstraints);
pnlPrefs.add(new NoteLabel("Disables artifact cards in generated decks."), descriptionConstraints);
+ pnlPrefs.add(cbCardBased, titleConstraints);
+ pnlPrefs.add(new NoteLabel("Builds more synergistic random decks (requires restart)."), descriptionConstraints);
+
// Deck building options
pnlPrefs.add(new SectionLabel("Deck Editor Options"), sectionConstraints);
@@ -492,6 +496,11 @@ public enum VSubmenuPreferences implements IVSubmenu {
return cbRemoveSmall;
}
+ /** @return {@link javax.swing.JCheckBox} */
+ public final JCheckBox getCbCardBased() {
+ return cbCardBased;
+ }
+
/** @return {@link javax.swing.JCheckBox} */
public final JCheckBox getCbSingletons() {
return cbSingletons;
diff --git a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java
index 7192b53b3e9..be176d263d7 100644
--- a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java
+++ b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java
@@ -3,9 +3,12 @@ package forge.view;
import java.io.File;
import java.io.FilenameFilter;
import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import forge.LobbyPlayer;
import forge.deck.DeckGroup;
+import forge.game.*;
import forge.properties.ForgeConstants;
import forge.tournament.system.*;
import forge.util.TextUtil;
@@ -15,12 +18,6 @@ import org.apache.commons.lang3.time.StopWatch;
import forge.deck.Deck;
import forge.deck.io.DeckSerializer;
-import forge.game.Game;
-import forge.game.GameLogEntry;
-import forge.game.GameRules;
-import forge.game.GameType;
-import forge.game.GameLogEntryType;
-import forge.game.Match;
import forge.game.player.RegisteredPlayer;
import forge.model.FModel;
import forge.player.GamePlayerUtil;
@@ -161,14 +158,38 @@ public class SimulateMatch {
System.out.println("\tq - Quiet flag. Output just the game result, not the entire game log.");
}
+
+
private static void simulateSingleMatch(Match mc, int iGame, boolean outputGamelog) {
StopWatch sw = new StopWatch();
sw.start();
Game g1 = mc.createGame();
// will run match in the same thread
- mc.startGame(g1);
- sw.stop();
+
+ long startTime = System.currentTimeMillis();
+ try {
+ TimeLimitedCodeBlock.runWithTimeout(new Runnable() {
+ @Override
+ public void run() {
+ mc.startGame(g1);
+ sw.stop();
+ }
+ }, 120, TimeUnit.SECONDS);
+ }
+ catch (TimeoutException e) {
+ System.out.println("Stopping slow match as draw");
+ g1.setGameOver(GameEndReason.Draw);
+ sw.stop();
+ }catch (Exception e){
+ e.printStackTrace();
+ g1.setGameOver(GameEndReason.Draw);
+ sw.stop();
+ }catch(StackOverflowError e){
+ g1.setGameOver(GameEndReason.Draw);
+ sw.stop();
+ }
+
List log;
if (outputGamelog) {
diff --git a/forge-gui-desktop/src/main/java/forge/view/TimeLimitedCodeBlock.java b/forge-gui-desktop/src/main/java/forge/view/TimeLimitedCodeBlock.java
new file mode 100644
index 00000000000..15a3f5a188a
--- /dev/null
+++ b/forge-gui-desktop/src/main/java/forge/view/TimeLimitedCodeBlock.java
@@ -0,0 +1,46 @@
+package forge.view;
+
+import java.util.concurrent.*;
+
+/**
+ * Created by maustin on 08/02/2018.
+ */
+public class TimeLimitedCodeBlock {
+
+ public static void runWithTimeout(final Runnable runnable, long timeout, TimeUnit timeUnit) throws Exception {
+ runWithTimeout(new Callable() {
+ @Override
+ public Object call() throws Exception {
+ runnable.run();
+ return null;
+ }
+ }, timeout, timeUnit);
+ }
+
+ public static T runWithTimeout(Callable callable, long timeout, TimeUnit timeUnit) throws Exception {
+ final ExecutorService executor = Executors.newSingleThreadExecutor();
+ final Future future = executor.submit(callable);
+ executor.shutdown(); // This does not cancel the already-scheduled task.
+ try {
+ return future.get(timeout, timeUnit);
+ }
+ catch (TimeoutException e) {
+ //remove this if you do not want to cancel the job in progress
+ //or set the argument to 'false' if you do not want to interrupt the thread
+ future.cancel(true);
+ throw e;
+ }
+ catch (ExecutionException e) {
+ //unwrap the root cause
+ Throwable t = e.getCause();
+ if (t instanceof Error) {
+ throw (Error) t;
+ } else if (t instanceof Exception) {
+ throw (Exception) t;
+ } else {
+ throw new IllegalStateException(t);
+ }
+ }
+ }
+
+}
diff --git a/forge-gui-mobile/src/forge/card/CardZoom.java b/forge-gui-mobile/src/forge/card/CardZoom.java
index c2015ac0488..eeac3ec4dea 100644
--- a/forge-gui-mobile/src/forge/card/CardZoom.java
+++ b/forge-gui-mobile/src/forge/card/CardZoom.java
@@ -10,6 +10,10 @@ import com.badlogic.gdx.math.Rectangle;
import forge.Forge;
import forge.Graphics;
import forge.assets.FSkinImage;
+import forge.deck.CardThemedDeckGenerator;
+import forge.deck.CommanderDeckGenerator;
+import forge.deck.DeckProxy;
+import forge.deck.FDeckViewer;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.item.IPaperCard;
@@ -123,6 +127,17 @@ public class CardZoom extends FOverlay {
if (item instanceof CardView) {
return (CardView)item;
}
+ if (item instanceof DeckProxy) {
+ if (item instanceof CardThemedDeckGenerator){
+ return CardView.getCardForUi(((CardThemedDeckGenerator)item).getPaperCard());
+ }else if (item instanceof CommanderDeckGenerator){
+ return CardView.getCardForUi(((CommanderDeckGenerator)item).getPaperCard());
+ }else{
+ DeckProxy deck = ((DeckProxy)item);
+ return new CardView(-1, null, deck.getName(), null, deck.getImageKey(false));
+ }
+
+ }
if (item instanceof IPaperCard) {
return CardView.getCardForUi((IPaperCard)item);
}
diff --git a/forge-gui-mobile/src/forge/deck/FDeckChooser.java b/forge-gui-mobile/src/forge/deck/FDeckChooser.java
index 8842a3bce77..81ab2b910c9 100644
--- a/forge-gui-mobile/src/forge/deck/FDeckChooser.java
+++ b/forge-gui-mobile/src/forge/deck/FDeckChooser.java
@@ -148,7 +148,8 @@ public class FDeckChooser extends FScreen {
public void handleEvent(FEvent e) {
if (selectedDeckType != DeckType.STANDARD_COLOR_DECK && selectedDeckType != DeckType.STANDARD_CARDGEN_DECK
&& selectedDeckType != DeckType.MODERN_CARDGEN_DECK && selectedDeckType != DeckType.MODERN_COLOR_DECK &&
- selectedDeckType != DeckType.COLOR_DECK && selectedDeckType != DeckType.THEME_DECK) {
+ selectedDeckType != DeckType.COLOR_DECK && selectedDeckType != DeckType.THEME_DECK
+ && selectedDeckType != DeckType.RANDOM_COMMANDER_DECK && selectedDeckType != DeckType.RANDOM_CARDGEN_COMMANDER_DECK) {
FDeckViewer.show(getDeck());
}
}
@@ -269,6 +270,8 @@ public class FDeckChooser extends FScreen {
case COLOR_DECK:
case STANDARD_COLOR_DECK:
case STANDARD_CARDGEN_DECK:
+ case RANDOM_CARDGEN_COMMANDER_DECK:
+ case RANDOM_COMMANDER_DECK:
case MODERN_CARDGEN_DECK:
case MODERN_COLOR_DECK:
case THEME_DECK:
@@ -440,7 +443,7 @@ public class FDeckChooser extends FScreen {
cmbDeckTypes.addItem(DeckType.QUEST_OPPONENT_DECK);
cmbDeckTypes.addItem(DeckType.COLOR_DECK);
cmbDeckTypes.addItem(DeckType.STANDARD_COLOR_DECK);
- if(!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY)) {
+ if(FModel.isdeckGenMatrixLoaded()) {
cmbDeckTypes.addItem(DeckType.STANDARD_CARDGEN_DECK);
cmbDeckTypes.addItem(DeckType.MODERN_CARDGEN_DECK);
}
@@ -453,6 +456,10 @@ public class FDeckChooser extends FScreen {
case TinyLeaders:
cmbDeckTypes.addItem(DeckType.CUSTOM_DECK);
cmbDeckTypes.addItem(DeckType.RANDOM_DECK);
+ if(FModel.isdeckGenMatrixLoaded()) {
+ cmbDeckTypes.addItem(DeckType.RANDOM_CARDGEN_COMMANDER_DECK);
+ }
+ cmbDeckTypes.addItem(DeckType.RANDOM_COMMANDER_DECK);
cmbDeckTypes.addItem(DeckType.NET_DECK);
break;
case DeckManager:
@@ -575,6 +582,17 @@ public class FDeckChooser extends FScreen {
pool = DeckProxy.getAllTinyLeadersDecks();
config = ItemManagerConfig.COMMANDER_DECKS;
break;
+ case RANDOM_COMMANDER_DECK:
+ pool = CommanderDeckGenerator.getCommanderDecks(lstDecks.getGameType().getDeckFormat(),isAi, false);
+ config = ItemManagerConfig.STRING_ONLY;
+ break;
+ case RANDOM_CARDGEN_COMMANDER_DECK:
+ pool= new ArrayList<>();
+ if(FModel.isdeckGenMatrixLoaded()) {
+ pool = CommanderDeckGenerator.getCommanderDecks(lstDecks.getGameType().getDeckFormat(), isAi, true);
+ }
+ config = ItemManagerConfig.STRING_ONLY;
+ break;
case SCHEME_DECKS:
pool = DeckProxy.getAllSchemeDecks();
config = ItemManagerConfig.SCHEME_DECKS;
@@ -603,12 +621,18 @@ public class FDeckChooser extends FScreen {
break;
case STANDARD_CARDGEN_DECK:
maxSelections = 1;
- pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getStandard(), isAi);
+ pool= new ArrayList<>();
+ if(FModel.isdeckGenMatrixLoaded()) {
+ pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getStandard(), isAi);
+ }
config = ItemManagerConfig.STRING_ONLY;
break;
case MODERN_CARDGEN_DECK:
maxSelections = 1;
- pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getModern(), isAi);
+ pool= new ArrayList<>();
+ if(FModel.isdeckGenMatrixLoaded()) {
+ pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getModern(), isAi);
+ }
config = ItemManagerConfig.STRING_ONLY;
break;
case MODERN_COLOR_DECK:
@@ -940,7 +964,7 @@ public class FDeckChooser extends FScreen {
public void run(final Integer numOpponents) {
if (numOpponents == null) { return; }
List deckTypes=null;
- if(!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY)) {
+ if(FModel.isdeckGenMatrixLoaded()) {
deckTypes=Arrays.asList(new DeckType[] {
DeckType.CUSTOM_DECK,
DeckType.PRECONSTRUCTED_DECK,
diff --git a/forge-gui-mobile/src/forge/deck/FDeckViewer.java b/forge-gui-mobile/src/forge/deck/FDeckViewer.java
index 69111ce8704..ea38152df37 100644
--- a/forge-gui-mobile/src/forge/deck/FDeckViewer.java
+++ b/forge-gui-mobile/src/forge/deck/FDeckViewer.java
@@ -5,9 +5,6 @@ import forge.assets.FImage;
import forge.assets.FSkin;
import forge.assets.FSkinImage;
import forge.assets.FTextureRegionImage;
-import forge.deck.CardPool;
-import forge.deck.Deck;
-import forge.deck.DeckSection;
import forge.item.PaperCard;
import forge.itemmanager.CardManager;
import forge.itemmanager.ItemManagerConfig;
diff --git a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java
index 7ab24398da7..20ec1ed293a 100644
--- a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java
+++ b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java
@@ -983,6 +983,9 @@ public abstract class ItemManager extends FContainer im
if (cbxSortOptions != null) {
return cbxSortOptions.getWidth();
}
+ if(filters.isEmpty()){
+ return 0f;
+ }
return filters.get(filters.size() - 1).getWidget().getWidth();
}
}
diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java
index 13f1b774b6e..999a62b16e0 100644
--- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java
+++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java
@@ -11,7 +11,10 @@ import forge.assets.ImageCache;
import forge.card.CardRenderer;
import forge.card.CardRenderer.CardStackPosition;
import forge.card.CardZoom;
+import forge.deck.CardThemedDeckGenerator;
+import forge.deck.CommanderDeckGenerator;
import forge.deck.DeckProxy;
+import forge.deck.FDeckViewer;
import forge.item.InventoryItem;
import forge.item.PaperCard;
import forge.itemmanager.ColumnDef;
@@ -861,6 +864,10 @@ public class ImageView extends ItemView {
public boolean longPress(float x, float y) {
ItemInfo item = getItemAtPoint(x + getLeft(), y + getTop());
if (item != null) {
+ if(item.getKey() instanceof CardThemedDeckGenerator || item.getKey() instanceof CommanderDeckGenerator){
+ FDeckViewer.show(((DeckProxy)item.getKey()).getDeck());
+ return true;
+ }
CardZoom.show(orderedItems, orderedItems.indexOf(item), itemManager);
return true;
}
diff --git a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java
index 2e20886d6c6..f8e05cafa2c 100644
--- a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java
+++ b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java
@@ -149,8 +149,8 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
FThreads.invokeInBackgroundThread(new Runnable() {
@Override
public void run() {
- playerPanels.get(0).initialize(FPref.CONSTRUCTED_P1_DECK_STATE, DeckType.PRECONSTRUCTED_DECK);
- playerPanels.get(1).initialize(FPref.CONSTRUCTED_P2_DECK_STATE, DeckType.COLOR_DECK);
+ playerPanels.get(0).initialize(FPref.CONSTRUCTED_P1_DECK_STATE, FPref.COMMANDER_P1_DECK_STATE, FPref.TINY_LEADER_P1_DECK_STATE, DeckType.PRECONSTRUCTED_DECK);
+ playerPanels.get(1).initialize(FPref.CONSTRUCTED_P2_DECK_STATE, FPref.COMMANDER_P2_DECK_STATE, FPref.TINY_LEADER_P2_DECK_STATE, DeckType.COLOR_DECK);
/*playerPanels.get(2).initialize(FPref.CONSTRUCTED_P3_DECK_STATE, DeckType.COLOR_DECK);
playerPanels.get(3).initialize(FPref.CONSTRUCTED_P4_DECK_STATE, DeckType.COLOR_DECK);
playerPanels.get(4).initialize(FPref.CONSTRUCTED_P5_DECK_STATE, DeckType.COLOR_DECK);
@@ -475,12 +475,15 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
Deck deck;
if (hasVariant(GameType.Commander)) {
deck = playerPanel.getCommanderDeck();
+ playerPanel.getCommanderDeckChooser().saveState();
}
else if (hasVariant(GameType.TinyLeaders)) {
deck = playerPanel.getTinyLeadersDeck();
+ playerPanel.getTinyLeadersDeckChooser().saveState();
}
else {
deck = playerPanel.getDeck();
+ playerPanel.getDeckChooser().saveState();
}
Deck playerDeck = deck;
@@ -505,8 +508,6 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
playerDeck.putSection(DeckSection.Avatar, avatarPool);
}
- playerPanel.getDeckChooser().saveState();
-
decks[playerIndex] = playerDeck;
if (playerChangeListener != null) {
playerChangeListener.update(playerIndex, UpdateLobbyPlayerEvent.deckUpdate(playerDeck));
diff --git a/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java b/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java
index c8947af148f..2cbab44ea67 100644
--- a/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java
+++ b/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java
@@ -109,25 +109,43 @@ public class PlayerPanel extends FContainer {
lstCommanderDecks = new FDeckChooser(GameType.Commander, isAi, new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
- btnCommanderDeck.setText("Commander Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName());
+ if( ((DeckManager)e.getSource()).getSelectedItem() != null) {
+ btnCommanderDeck.setText("Commander Deck: " + ((DeckManager) e.getSource()).getSelectedItem().getName());
+ lstCommanderDecks.saveState();
+ }else{
+ btnCommanderDeck.setText("Commander Deck");
+ }
}
});
lstTinyLeadersDecks = new FDeckChooser(GameType.TinyLeaders, isAi, new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
- btnTinyLeadersDeck.setText("Tiny Leaders Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName());
+ if( ((DeckManager)e.getSource()).getSelectedItem() != null) {
+ btnTinyLeadersDeck.setText("Tiny Leaders Deck: " + ((DeckManager) e.getSource()).getSelectedItem().getName());
+ lstTinyLeadersDecks.saveState();
+ }else{
+ btnTinyLeadersDeck.setText("Tiny Leaders Deck");
+ }
}
});
lstSchemeDecks = new FDeckChooser(GameType.Archenemy, isAi, new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
- btnSchemeDeck.setText("Scheme Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName());
+ if( ((DeckManager)e.getSource()).getSelectedItem() != null){
+ btnSchemeDeck.setText("Scheme Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName());
+ }else{
+ btnSchemeDeck.setText("Scheme Deck");
+ }
}
});
lstPlanarDecks = new FDeckChooser(GameType.Planechase, isAi, new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
- btnPlanarDeck.setText("Planar Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName());
+ if( ((DeckManager)e.getSource()).getSelectedItem() != null){
+ btnPlanarDeck.setText("Planar Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName());
+ }else{
+ btnPlanarDeck.setText("Planar Deck");
+ }
}
});
lstVanguardAvatars = new FVanguardChooser(isAi, new FEventHandler() {
@@ -217,10 +235,10 @@ public class PlayerPanel extends FContainer {
cbTeam.setEnabled(false);
}
- public void initialize(FPref savedStateSetting, DeckType defaultDeckType) {
+ public void initialize(FPref savedStateSetting, FPref savedStateSettingCommander, FPref savedStateSettingTinyLeader, DeckType defaultDeckType) {
deckChooser.initialize(savedStateSetting, defaultDeckType);
- lstCommanderDecks.initialize(null, DeckType.RANDOM_DECK);
- lstTinyLeadersDecks.initialize(null, DeckType.RANDOM_DECK);
+ lstCommanderDecks.initialize(savedStateSettingCommander, DeckType.COMMANDER_DECK);
+ lstTinyLeadersDecks.initialize(savedStateSettingTinyLeader, DeckType.TINY_LEADERS_DECKS);
lstPlanarDecks.initialize(null, DeckType.RANDOM_DECK);
lstSchemeDecks.initialize(null, DeckType.RANDOM_DECK);
}
@@ -666,6 +684,15 @@ public class PlayerPanel extends FContainer {
return deckChooser;
}
+ public FDeckChooser getCommanderDeckChooser() {
+ return lstCommanderDecks;
+ }
+
+ public FDeckChooser getTinyLeadersDeckChooser() {
+ return lstTinyLeadersDecks;
+ }
+
+
public Deck getDeck() {
return deckChooser.getDeck();
}
diff --git a/forge-gui-mobile/src/forge/screens/gauntlet/NewGauntletScreen.java b/forge-gui-mobile/src/forge/screens/gauntlet/NewGauntletScreen.java
index cabee84c191..c8685a347f2 100644
--- a/forge-gui-mobile/src/forge/screens/gauntlet/NewGauntletScreen.java
+++ b/forge-gui-mobile/src/forge/screens/gauntlet/NewGauntletScreen.java
@@ -82,7 +82,7 @@ public class NewGauntletScreen extends LaunchScreen {
if (numOpponents == null) { return; }
ListChooser chooser = new ListChooser(
- "Choose allowed deck types for opponents", 0, 7, Arrays.asList(new DeckType[] {
+ "Choose allowed deck types for opponents", 0, 9, Arrays.asList(new DeckType[] {
DeckType.CUSTOM_DECK,
DeckType.PRECONSTRUCTED_DECK,
DeckType.QUEST_OPPONENT_DECK,
diff --git a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java
index e1328ac1893..de4d2714c4f 100644
--- a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java
+++ b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java
@@ -155,6 +155,10 @@ public class SettingsPage extends TabPage {
"Remove Small Creatures",
"Disables 1/1 and 0/X creatures in generated decks."),
2);
+ lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_CARDBASED,
+ "Include Card-based Deck Generation",
+ "Builds more synergistic random decks"),
+ 2);
lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_SINGLETONS,
"Singleton Mode",
"Disables non-land duplicates in generated decks."),
diff --git a/forge-gui/README.txt b/forge-gui/README.txt
index 11b369846f2..708d464c08f 100644
--- a/forge-gui/README.txt
+++ b/forge-gui/README.txt
@@ -1 +1 @@
-This file is automatically updated by our release bot on Discord, Blacksmith. It is created from the files present in the 'release-files' directory. Please do not hand-edit this file if using the bot to perform a release, as your changes will be overwritten.
\ No newline at end of file
+This file is automatically updated by our release bot on Discord, Blacksmith. It is created from the files present in the 'release-files' directory. Please do not hand-edit this file if using the bot to perform a release, as your changes will be overwritten.
diff --git a/forge-gui/res/deckgendecks/Commander.dat b/forge-gui/res/deckgendecks/Commander.dat
new file mode 100644
index 00000000000..32142b05d93
Binary files /dev/null and b/forge-gui/res/deckgendecks/Commander.dat differ
diff --git a/forge-gui/res/deckgendecks/Modern.dat b/forge-gui/res/deckgendecks/Modern.dat
index bd820f750d4..6f7201b2c5f 100644
Binary files a/forge-gui/res/deckgendecks/Modern.dat and b/forge-gui/res/deckgendecks/Modern.dat differ
diff --git a/forge-gui/res/deckgendecks/Standard.dat b/forge-gui/res/deckgendecks/Standard.dat
index d97fc0c183b..4deb78fbe16 100644
Binary files a/forge-gui/res/deckgendecks/Standard.dat and b/forge-gui/res/deckgendecks/Standard.dat differ
diff --git a/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java b/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java
index fbf0d8dbcfa..a17d1219e0a 100644
--- a/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java
+++ b/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java
@@ -1,9 +1,12 @@
package forge.deck;
+import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import forge.card.CardRules;
import forge.card.CardRulesPredicates;
+import forge.deck.generation.DeckGeneratorBase;
import forge.deck.io.CardThemedMatrixIO;
import forge.deck.io.DeckStorage;
import forge.game.GameFormat;
@@ -22,27 +25,42 @@ import java.util.*;
*/
public final class CardRelationMatrixGenerator {
- public static HashMap>>> cardPools = new HashMap<>();
+ public static HashMap>>> cardPools = new HashMap<>();
+ public static boolean initialize(){
+ List formatStrings = new ArrayList<>();
+ formatStrings.add(FModel.getFormats().getStandard().getName());
+ formatStrings.add(FModel.getFormats().getModern().getName());
+ formatStrings.add(DeckFormat.Commander.toString());
- public static void initialize(){
- HashMap>> standardMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getStandard());
- HashMap>> modernMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getModern());
- if(standardMap==null || modernMap==null){
- reInitialize();
- return;
+ for (String formatString : formatStrings){
+ if(!initializeFormat(formatString)){
+ return false;
+ }
}
- cardPools.put(FModel.getFormats().getStandard(),standardMap);
- cardPools.put(FModel.getFormats().getModern(),modernMap);
+
+ return true;
}
- public static void reInitialize(){
- cardPools.put(FModel.getFormats().getStandard(),initializeFormat(FModel.getFormats().getStandard()));
- cardPools.put(FModel.getFormats().getModern(),initializeFormat(FModel.getFormats().getModern()));
- for(GameFormat format:cardPools.keySet()){
- HashMap>> map = cardPools.get(format);
- CardThemedMatrixIO.saveMatrix(format,map);
+ /** Try to load matrix .dat files, otherwise check for deck folders and build .dat, otherwise return false **/
+ public static boolean initializeFormat(String format){
+ HashMap>> formatMap = CardThemedMatrixIO.loadMatrix(format);
+ if(formatMap==null) {
+ if (CardThemedMatrixIO.getMatrixFolder(format).exists()) {
+ if(format.equals(FModel.getFormats().getStandard().getName())){
+ formatMap=initializeFormat(FModel.getFormats().getStandard());
+ }else if(format.equals(FModel.getFormats().getModern().getName())){
+ formatMap=initializeFormat(FModel.getFormats().getModern());
+ }else{
+ formatMap=initializeCommanderFormat();
+ }
+ CardThemedMatrixIO.saveMatrix(format, formatMap);
+ } else {
+ return false;
+ }
}
+ cardPools.put(format, formatMap);
+ return true;
}
public static HashMap>> initializeFormat(GameFormat format){
@@ -58,8 +76,8 @@ public final class CardRelationMatrixGenerator {
Map cardIntegerMap = new HashMap<>();
Map integerCardMap = new HashMap<>();
for (int i=0; i>> initializeCommanderFormat(){
+
+ IStorage decks = new StorageImmediatelySerialized("Generator",
+ new DeckStorage(new File(ForgeConstants.DECK_GEN_DIR,DeckFormat.Commander.toString()),
+ ForgeConstants.DECK_GEN_DIR, false),
+ true);
+
+ //get all cards
+ final Iterable cards = Iterables.filter(FModel.getMagicDb().getCommonCards().getUniqueCards()
+ , Predicates.compose(Predicates.not(CardRulesPredicates.Presets.IS_BASIC_LAND_NOT_WASTES), PaperCard.FN_GET_RULES));
+ List cardList = Lists.newArrayList(cards);
+ cardList.add(FModel.getMagicDb().getCommonCards().getCard("Wastes"));
+ Map cardIntegerMap = new HashMap<>();
+ Map integerCardMap = new HashMap<>();
+ Map legendIntegerMap = new HashMap<>();
+ Map integerLegendMap = new HashMap<>();
+ //generate lookups for cards to link card names to matrix columns
+ for (int i=0; i legends = Lists.newArrayList(Iterables.filter(cardList,Predicates.compose(
+ new Predicate() {
+ @Override
+ public boolean apply(CardRules rules) {
+ return DeckFormat.Commander.isLegalCommander(rules);
+ }
+ }, PaperCard.FN_GET_RULES)));
+
+ //generate lookups for legends to link commander names to matrix rows
+ for (int i=0; i>> cardPools = new HashMap<>();
+ for (PaperCard card:legends){
+ int col=legendIntegerMap.get(card.getName());
+ int[] distances = matrix[col];
+ int max = (Integer) Collections.max(Arrays.asList(ArrayUtils.toObject(distances)));
+ if (max>0) {
+ List> deckPool=new ArrayList<>();
+ for(int k=0;k0){
+ deckPool.add(new AbstractMap.SimpleEntry(integerCardMap.get(k),matrix[col][k]));
+ }
+ }
+ cardPools.put(card.getName(), deckPool);
+ }
+ }
+ return cardPools;
+ }
+
+ //update the matrix by incrementing the connectivity count for each card in the deck
+ public static void updateLegendMatrix(Deck deck, PaperCard legend, Map cardIntegerMap,
+ Map legendIntegerMap, int[][] matrix){
+ for (PaperCard pairCard:Iterables.filter(deck.getMain().toFlatList(),
+ Predicates.compose(Predicates.not(CardRulesPredicates.Presets.IS_BASIC_LAND_NOT_WASTES), PaperCard.FN_GET_RULES))){
+ if (!pairCard.getName().equals(legend.getName())){
+ try {
+ int old = matrix[legendIntegerMap.get(legend.getName())][cardIntegerMap.get(pairCard.getName())];
+ matrix[legendIntegerMap.get(legend.getName())][cardIntegerMap.get(pairCard.getName())] = old + 1;
+ }catch (NullPointerException ne){
+ //Todo: Not sure what was failing here
+ ne.printStackTrace();
+ }
+ }
+
+ }
+ //add partner commanders to matrix
+ if(deck.getCommanders().size()>1){
+ for(PaperCard partner:deck.getCommanders()){
+ if(!partner.equals(legend)){
+ int old = matrix[legendIntegerMap.get(legend.getName())][cardIntegerMap.get(partner.getName())];
+ matrix[legendIntegerMap.get(legend.getName())][cardIntegerMap.get(partner.getName())] = old + 1;
+ }
+ }
+ }
+ }
+
public static class ArrayIndexComparator implements Comparator
{
private final Integer[] array;
diff --git a/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java b/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java
index 4d48f3df7d7..aa972a8152a 100644
--- a/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java
+++ b/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java
@@ -1,7 +1,11 @@
package forge.deck;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
import forge.card.CardEdition;
import forge.game.GameFormat;
+import forge.item.PaperCard;
+import forge.model.FModel;
import java.util.ArrayList;
import java.util.List;
@@ -12,7 +16,7 @@ import java.util.List;
public class CardThemedDeckGenerator extends DeckProxy implements Comparable {
public static List getMatrixDecks(GameFormat format, boolean isForAi){
final List decks = new ArrayList();
- for(String card: CardRelationMatrixGenerator.cardPools.get(format).keySet()) {
+ for(String card: CardRelationMatrixGenerator.cardPools.get(format.getName()).keySet()) {
decks.add(new CardThemedDeckGenerator(card, format, isForAi));
}
return decks;
@@ -61,4 +65,15 @@ public class CardThemedDeckGenerator extends DeckProxy implements Comparable cardFilter = Predicates.and(format.getFilterPrinted(),PaperCard.Predicates.name(name));
+ List cards=FModel.getMagicDb().getCommonCards().getAllCards(cardFilter);
+ return cards.get(cards.size()-1).getImageKey(altState);*/
+ return FModel.getMagicDb().getCommonCards().getUniqueByName(name).getImageKey(altState);
+ }
+
+ public PaperCard getPaperCard(){
+ return FModel.getMagicDb().getCommonCards().getUniqueByName(name);
+ }
}
diff --git a/forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java b/forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java
new file mode 100644
index 00000000000..52af7d5b7d2
--- /dev/null
+++ b/forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java
@@ -0,0 +1,107 @@
+package forge.deck;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import forge.card.CardEdition;
+import forge.card.CardRules;
+import forge.card.CardRulesPredicates;
+import forge.deck.generation.DeckGeneratorBase;
+import forge.deck.generation.IDeckGenPool;
+import forge.game.GameFormat;
+import forge.game.GameType;
+import forge.item.PaperCard;
+import forge.model.FModel;
+import forge.util.ItemPool;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by maustin on 09/05/2017.
+ */
+public class CommanderDeckGenerator extends DeckProxy implements Comparable {
+ public static List getCommanderDecks(DeckFormat format, boolean isForAi, boolean isCardGen){
+ ItemPool uniqueCards;
+ if(isCardGen){
+ uniqueCards = new ItemPool(PaperCard.class);
+ Iterable legendNames=CardRelationMatrixGenerator.cardPools.get(DeckFormat.Commander.toString()).keySet();
+ for(String legendName:legendNames) {
+ uniqueCards.add(FModel.getMagicDb().getCommonCards().getUniqueByName(legendName));
+ }
+ }else {
+ uniqueCards = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getUniqueCards(), PaperCard.class);
+ }
+ Predicate canPlay = isForAi ? DeckGeneratorBase.AI_CAN_PLAY : DeckGeneratorBase.HUMAN_CAN_PLAY;
+ @SuppressWarnings("unchecked")
+ Iterable legends = Iterables.filter(uniqueCards.toFlatList(), Predicates.compose(Predicates.and(
+ new Predicate() {
+ @Override
+ public boolean apply(CardRules rules) {
+ return format.isLegalCommander(rules);
+ }
+ },
+ canPlay), PaperCard.FN_GET_RULES));
+ final List decks = new ArrayList();
+ for(PaperCard legend: legends) {
+ decks.add(new CommanderDeckGenerator(legend, format, isForAi, isCardGen));
+ }
+ return decks;
+ }
+
+ private final PaperCard legend;
+ private final int index;
+ private final DeckFormat format;
+ private final boolean isForAi;
+ private final boolean isCardgen;
+
+
+ private CommanderDeckGenerator(PaperCard legend0, DeckFormat format0, boolean isForAi0, boolean isCardgen0) {
+ super();
+ legend = legend0;
+ index = 0;
+ isForAi=isForAi0;
+ format=format0;
+ isCardgen=isCardgen0;
+ }
+
+ public CardEdition getEdition() {
+ return CardEdition.UNKNOWN;
+ }
+
+
+ @Override
+ public String getName() {
+ return legend.getName();
+ }
+
+ @Override
+ public String toString() {
+ return legend.getName();
+ }
+
+ @Override
+ public int compareTo(final CommanderDeckGenerator d) {
+ return this.getName().compareTo(d.getName());
+ }
+
+ @Override
+ public Deck getDeck() {
+
+ return DeckgenUtil.generateRandomCommanderDeck(legend, format,isForAi, isCardgen);
+ }
+
+ @Override
+ public boolean isGeneratedDeck() {
+ return true;
+ }
+
+ public String getImageKey(boolean altState) {
+ return legend.getImageKey(altState);
+ }
+
+ public PaperCard getPaperCard(){
+ return legend;
+ }
+}
diff --git a/forge-gui/src/main/java/forge/deck/DeckType.java b/forge-gui/src/main/java/forge/deck/DeckType.java
index 9b074229372..5eabef8f923 100644
--- a/forge-gui/src/main/java/forge/deck/DeckType.java
+++ b/forge-gui/src/main/java/forge/deck/DeckType.java
@@ -7,6 +7,8 @@ public enum DeckType {
CUSTOM_DECK ("Custom User Decks"),
CONSTRUCTED_DECK ("Constructed Decks"),
COMMANDER_DECK ("Commander Decks"),
+ RANDOM_COMMANDER_DECK ("Random Commander Decks"),
+ RANDOM_CARDGEN_COMMANDER_DECK ("Random Commander Card-based Decks"),
TINY_LEADERS_DECKS ("Tiny Leaders Decks"),
SCHEME_DECKS ("Scheme Decks"),
PLANAR_DECKS ("Planar Decks"),
@@ -25,9 +27,10 @@ public enum DeckType {
NET_COMMANDER_DECK ("Net Commander Decks");
public static DeckType[] ConstructedOptions;
+ public static DeckType[] CommanderOptions;
static {
- if (!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY)) {
+ if (FModel.isdeckGenMatrixLoaded()) {
ConstructedOptions = new DeckType[]{
DeckType.CUSTOM_DECK,
DeckType.PRECONSTRUCTED_DECK,
@@ -55,6 +58,25 @@ public enum DeckType {
};
}
}
+ static {
+ if (FModel.isdeckGenMatrixLoaded()) {
+ CommanderOptions = new DeckType[]{
+ DeckType.COMMANDER_DECK,
+ DeckType.RANDOM_COMMANDER_DECK,
+ DeckType.RANDOM_CARDGEN_COMMANDER_DECK,
+ DeckType.RANDOM_DECK,
+ DeckType.NET_COMMANDER_DECK
+ };
+ }else{
+ CommanderOptions = new DeckType[]{
+ DeckType.COMMANDER_DECK,
+ DeckType.RANDOM_COMMANDER_DECK,
+ DeckType.RANDOM_DECK,
+ DeckType.NET_COMMANDER_DECK
+ };
+ }
+
+ }
private String value;
private DeckType(final String value) {
diff --git a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java
index 326ee1325b6..1b48fc05bf7 100644
--- a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java
+++ b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java
@@ -16,6 +16,7 @@ import forge.game.GameFormat;
import forge.game.GameType;
import forge.item.PaperCard;
import forge.itemmanager.IItemManager;
+import forge.limited.CardThemedCommanderDeckBuilder;
import forge.limited.CardThemedDeckBuilder;
import forge.model.FModel;
import forge.properties.ForgePreferences.FPref;
@@ -44,7 +45,7 @@ public class DeckgenUtil {
public static Deck buildCardGenDeck(GameFormat format, boolean isForAI){
Random random = new Random();
try {
- List keys = new ArrayList<>(CardRelationMatrixGenerator.cardPools.get(format).keySet());
+ List keys = new ArrayList<>(CardRelationMatrixGenerator.cardPools.get(format.getName()).keySet());
String randomKey = keys.get( random.nextInt(keys.size()) );
Predicate cardFilter = Predicates.and(format.getFilterPrinted(),PaperCard.Predicates.name(randomKey));
PaperCard keyCard = FModel.getMagicDb().getCommonCards().getAllCards(cardFilter).get(0);
@@ -119,7 +120,7 @@ public class DeckgenUtil {
*/
public static Deck buildCardGenDeck(PaperCard card, GameFormat format, boolean isForAI){
List> potentialCards = new ArrayList<>();
- potentialCards.addAll(CardRelationMatrixGenerator.cardPools.get(format).get(card.getName()));
+ potentialCards.addAll(CardRelationMatrixGenerator.cardPools.get(format.getName()).get(card.getName()));
Collections.sort(potentialCards,new CardDistanceComparator());
Collections.reverse(potentialCards);
//get second keycard
@@ -146,7 +147,7 @@ public class DeckgenUtil {
randMax=preSelectedCards.size();
}
PaperCard secondKeycard = preSelectedCards.get(r.nextInt(randMax));
- List> potentialSecondCards = CardRelationMatrixGenerator.cardPools.get(format).get(secondKeycard.getName());
+ List> potentialSecondCards = CardRelationMatrixGenerator.cardPools.get(format.getName()).get(secondKeycard.getName());
//combine card distances from second key card and re-sort
if(potentialSecondCards !=null && potentialSecondCards.size()>0) {
@@ -486,7 +487,7 @@ public class DeckgenUtil {
return res;
}
- /** Generate a 2-color Commander deck. */
+ /** Generate a 2-5-color Commander deck. */
public static Deck generateCommanderDeck(boolean forAi, GameType gameType) {
final Deck deck;
IDeckGenPool cardDb = FModel.getMagicDb().getCommonCards();
@@ -504,7 +505,6 @@ public class DeckgenUtil {
return format.isLegalCommander(rules);
}
},
- CardRulesPredicates.Presets.IS_MULTICOLOR,
canPlay), PaperCard.FN_GET_RULES));
do {
@@ -534,6 +534,104 @@ public class DeckgenUtil {
return deck;
}
+ /** Generate a ramdom Commander deck. */
+ public static Deck generateRandomCommanderDeck(PaperCard commander, DeckFormat format, boolean forAi, boolean isCardGen) {
+ final Deck deck;
+ IDeckGenPool cardDb;
+ DeckGeneratorBase gen = null;
+ PaperCard selectedPartner=null;
+ if(isCardGen){
+ List> potentialCards = new ArrayList<>();
+ potentialCards.addAll(CardRelationMatrixGenerator.cardPools.get(DeckFormat.Commander.toString()).get(commander.getName()));
+ Random r = new Random();
+ //Collections.shuffle(potentialCards, r);
+ List preSelectedCards = new ArrayList<>();
+ for(Map.Entry pair:potentialCards){
+ if(format.isLegalCard(pair.getKey())) {
+ preSelectedCards.add(pair.getKey());
+ }
+ }
+ //check for partner commanders
+ List partners=new ArrayList<>();
+ for(PaperCard c:preSelectedCards){
+ if(c.getRules().canBePartnerCommander()){
+ partners.add(c);
+ }
+ }
+
+ if(partners.size()>0&&commander.getRules().canBePartnerCommander()){
+ selectedPartner=partners.get(MyRandom.getRandom().nextInt(partners.size()));
+ preSelectedCards.remove(selectedPartner);
+ }
+ //randomly remove cards
+ int removeCount=0;
+ int i=0;
+ List toRemove = new ArrayList<>();
+ for(PaperCard c:preSelectedCards){
+ if(!format.isLegalCard(c)){
+ toRemove.add(c);
+ removeCount++;
+ }
+ if(preSelectedCards.size()<75){
+ break;
+ }
+ if(r.nextInt(100)>60+(15-(i/preSelectedCards.size())*preSelectedCards.size()) && removeCount<4 //randomly remove some cards - more likely as distance increases
+ &&!c.getName().contains("Urza")&&!c.getName().contains("Wastes")){ //avoid breaking Tron decks
+ toRemove.add(c);
+ removeCount++;
+ }
+ ++i;
+ }
+ preSelectedCards.removeAll(toRemove);
+ gen = new CardThemedCommanderDeckBuilder(commander, selectedPartner,preSelectedCards,forAi,format);
+ }else{
+ cardDb = FModel.getMagicDb().getCommonCards();
+ ColorSet colorID;
+ colorID = commander.getRules().getColorIdentity();
+ List comColors = new ArrayList(2);
+ if (colorID.hasWhite()) { comColors.add("White"); }
+ if (colorID.hasBlue()) { comColors.add("Blue"); }
+ if (colorID.hasBlack()) { comColors.add("Black"); }
+ if (colorID.hasRed()) { comColors.add("Red"); }
+ if (colorID.hasGreen()) { comColors.add("Green"); }
+
+ if(comColors.size()==1){
+ gen = new DeckGeneratorMonoColor(cardDb, format, comColors.get(0));
+ }else if(comColors.size()==2){
+ gen = new DeckGenerator2Color(cardDb, format, comColors.get(0), comColors.get(1));
+ }else if(comColors.size()==3){
+ gen = new DeckGenerator3Color(cardDb, format, comColors.get(0), comColors.get(1), comColors.get(2));
+ }else if(comColors.size()==4){
+ gen = new DeckGenerator4Color(cardDb, format, comColors.get(0), comColors.get(1), comColors.get(2), comColors.get(3));
+ }else if(comColors.size()==5){
+ gen = new DeckGenerator5Color(cardDb, format);
+ }
+
+ }
+
+
+
+ gen.setSingleton(true);
+ gen.setUseArtifacts(!FModel.getPreferences().getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
+ CardPool cards = gen.getDeck(format.getMainRange().getMaximum(), forAi);
+
+ // After generating card lists, build deck.
+ if(selectedPartner!=null){
+ deck = new Deck("Generated " + format.toString() + " deck (" + commander.getName() +
+ "--" + selectedPartner.getName() + ")");
+ }else{
+ deck = new Deck("Generated " + format.toString() + " deck (" + commander.getName() + ")");
+ }
+ deck.setDirectory("generated/commander");
+ deck.getMain().addAll(cards);
+ deck.getOrCreate(DeckSection.Commander).add(commander);
+ if(selectedPartner!=null){
+ deck.getOrCreate(DeckSection.Commander).add(selectedPartner);
+ }
+
+ return deck;
+ }
+
public static Map suggestBasicLandCount(Deck d) {
int W=0, U=0, R=0, B=0, G=0, total=0;
List cards = d.getOrCreate(DeckSection.Main).toFlatList();
diff --git a/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java b/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java
index 1bca2c69420..29253b5f0f6 100644
--- a/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java
+++ b/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java
@@ -22,7 +22,7 @@ public class CardThemedMatrixIO {
/** suffix for all gauntlet data files */
public static final String SUFFIX_DATA = ".dat";
- public static void saveMatrix(GameFormat format, HashMap>> map){
+ public static void saveMatrix(String format, HashMap>> map){
File file = getMatrixFile(format);
ObjectOutputStream s = null;
try {
@@ -43,7 +43,7 @@ public class CardThemedMatrixIO {
}
}
- public static HashMap>> loadMatrix(GameFormat format){
+ public static HashMap>> loadMatrix(String format){
try {
FileInputStream fin = new FileInputStream(getMatrixFile(format));
ObjectInputStream s = new ObjectInputStream(fin);
@@ -61,6 +61,10 @@ public class CardThemedMatrixIO {
return new File(ForgeConstants.DECK_GEN_DIR, name + SUFFIX_DATA);
}
+ public static File getMatrixFolder(final String name) {
+ return new File(ForgeConstants.DECK_GEN_DIR, name);
+ }
+
public static File getMatrixFile(final GameFormat gf) {
return getMatrixFile(gf.getName());
}
diff --git a/forge-gui/src/main/java/forge/limited/CardThemedCommanderDeckBuilder.java b/forge-gui/src/main/java/forge/limited/CardThemedCommanderDeckBuilder.java
new file mode 100644
index 00000000000..ab2887b2e25
--- /dev/null
+++ b/forge-gui/src/main/java/forge/limited/CardThemedCommanderDeckBuilder.java
@@ -0,0 +1,781 @@
+package forge.limited;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import forge.card.*;
+import forge.card.mana.ManaCost;
+import forge.card.mana.ManaCostShard;
+import forge.deck.CardPool;
+import forge.deck.Deck;
+import forge.deck.DeckFormat;
+import forge.deck.DeckSection;
+import forge.deck.generation.DeckGenPool;
+import forge.deck.generation.DeckGeneratorBase;
+import forge.item.IPaperCard;
+import forge.item.PaperCard;
+import forge.model.FModel;
+import forge.util.MyRandom;
+
+import java.util.*;
+
+/**
+ * Limited format deck.
+ */
+public class CardThemedCommanderDeckBuilder extends DeckGeneratorBase {
+ @Override
+ protected final float getLandPercentage() {
+ return 0.44f;
+ }
+ @Override
+ protected final float getCreaturePercentage() {
+ return 0.33f;
+ }
+ @Override
+ protected final float getSpellPercentage() {
+ return 0.23f;
+ }
+
+ protected int targetSize;
+ protected int numSpellsNeeded;
+ protected int numCreaturesToStart;
+ protected int landsNeeded;
+
+ protected final PaperCard commanderCard;
+ protected final PaperCard partnerCard;
+
+ protected Predicate hasColor;
+ protected final List availableList;
+ protected final List aiPlayables;
+ protected final List deckList = new ArrayList<>();
+ protected final List setsWithBasicLands = new ArrayList<>();
+ protected List rankedColorList;
+
+ // Views for aiPlayable
+ protected Iterable onColorCreatures;
+ protected Iterable onColorNonCreatures;
+
+ protected static final boolean logToConsole = false;
+ protected static final boolean logColorsToConsole = false;
+
+
+ /**
+ *
+ * Constructor.
+ *
+ * @param dList
+ * Cards to build the deck from.
+ */
+ public CardThemedCommanderDeckBuilder(PaperCard commanderCard0,PaperCard partner0, final List dList, boolean isForAI, DeckFormat format) {
+ super(FModel.getMagicDb().getCommonCards(), format);
+ this.availableList = dList;
+ commanderCard = commanderCard0;
+ partnerCard = partner0;
+ // remove Unplayables
+ if(isForAI) {
+ final Iterable playables = Iterables.filter(availableList,
+ Predicates.compose(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, PaperCard.FN_GET_RULES));
+ this.aiPlayables = Lists.newArrayList(playables);
+ }else{
+ this.aiPlayables = Lists.newArrayList(availableList);
+ }
+ this.availableList.removeAll(aiPlayables);
+ targetSize=format.getMainRange().getMinimum();
+ colors = commanderCard.getRules().getColorIdentity();
+ colors = ColorSet.fromMask(colors.getColor() | commanderCard.getRules().getColorIdentity().getColor());
+ if(partnerCard!=null) {
+ colors = ColorSet.fromMask(colors.getColor() | partnerCard.getRules().getColorIdentity().getColor());
+ targetSize--;
+ }
+ numSpellsNeeded = ((Double)Math.floor(targetSize*(getCreaturePercentage()+getSpellPercentage()))).intValue();
+ numCreaturesToStart = ((Double)Math.ceil(targetSize*(getCreaturePercentage()))).intValue();
+ landsNeeded = ((Double)Math.ceil(targetSize*(getLandPercentage()))).intValue();;
+ if (logColorsToConsole) {
+ System.out.println(commanderCard.getName());
+ System.out.println("Pre Colors: " + colors.toEnumSet().toString());
+ }
+ findBasicLandSets();
+ }
+
+
+
+ @Override
+ public CardPool getDeck(final int size, final boolean forAi) {
+ return buildDeck().getMain();
+ }
+
+ /**
+ *
+ * buildDeck.
+ *
+ *
+ * @return the new Deck.
+ */
+ @SuppressWarnings("unused")
+ public Deck buildDeck() {
+ // 1. Prepare
+ hasColor = Predicates.or(new MatchColorIdentity(colors), COLORLESS_CARDS);
+ if (logColorsToConsole) {
+ System.out.println(commanderCard.getName());
+ System.out.println("Colors: " + colors.toEnumSet().toString());
+ }
+ Iterable colorList = Iterables.filter(aiPlayables,
+ Predicates.compose(hasColor, PaperCard.FN_GET_RULES));
+ rankedColorList = Lists.newArrayList(colorList);
+ onColorCreatures = Iterables.filter(rankedColorList,
+ Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES));
+ onColorNonCreatures = Iterables.filter(rankedColorList,
+ Predicates.compose(CardRulesPredicates.Presets.IS_NON_CREATURE_SPELL, PaperCard.FN_GET_RULES));
+ // Guava iterables do not copy the collection contents, instead they act
+ // as filters and iterate over _source_ collection each time. So even if
+ // aiPlayable has changed, there is no need to create a new iterable.
+
+ // 2. Add any planeswalkers - removed - treat as non-creature
+
+ // 3. Add creatures, trying to follow mana curve
+
+ addManaCurveCards(onColorCreatures, numCreaturesToStart, "Creatures");
+ if (logToConsole) {
+ System.out.println("Post Creatures : " + deckList.size());
+ }
+
+ // 4.Try to fill up to num needed with on-color non-creature cards
+ addManaCurveCards(onColorNonCreatures, numSpellsNeeded - deckList.size(), "Spells");
+ if (logToConsole) {
+ System.out.println("Post Spells : " + deckList.size());
+ }
+
+ // 5.If we couldn't get enough, try to fill up with on-color cards
+ addCards(rankedColorList, numSpellsNeeded - deckList.size());
+ if (logToConsole) {
+ System.out.println("Post more creatures : " + deckList.size());
+ }
+
+ // 6. If there are still on-color cards, and the average cmc is low, add
+ // extras.
+ double avCMC=getAverageCMC(deckList);
+ int maxCMC=getMaxCMC(deckList);
+ if (deckList.size() == numSpellsNeeded && avCMC < 4) {
+ addLowCMCCard();
+ if(targetSize>60){
+ addLowCMCCard();
+ }
+ }
+ if (deckList.size() >= numSpellsNeeded && avCMC < 3 && maxCMC<6) {
+ addLowCMCCard();
+ }
+ if (deckList.size() >= numSpellsNeeded && avCMC < 2.5 && maxCMC<5) {
+ addLowCMCCard();
+ if(targetSize>60){
+ addLowCMCCard();
+ }
+ }
+ if (logToConsole) {
+ System.out.println("Post lowcoc : " + deckList.size());
+ }
+
+/* // 7. If not enough cards yet, try to add a third color,
+ // to try and avoid adding purely random cards.
+ addThirdColorCards(numSpellsNeeded - deckList.size());
+ if (logColorsToConsole) {
+ System.out.println("Post 3rd colour : " + deckList.size());
+ System.out.println("Colors: " + colors.toEnumSet().toString());
+ }*/
+
+ // 8. Check for DeckNeeds cards.
+ //checkRemRandomDeckCards(); - no need
+
+ // 9. If there are still less than 22 non-land cards add off-color
+ // cards. This should be avoided.
+ addRandomCards(numSpellsNeeded - deckList.size());
+ if (logToConsole) {
+ System.out.println("Post Randoms : " + deckList.size());
+ }
+
+ List duals = getDualLandList();
+ addNonBasicLands();
+ if (logToConsole) {
+ System.out.println("Post Nonbasic lands : " + deckList.size());
+ }
+
+ checkEvolvingWilds();
+
+ // 11. Fill up with basic lands.
+ final int[] clrCnts = calculateLandNeeds();
+
+ // Add dual lands
+ if (clrCnts.length>1) {
+
+ for (String s : duals) {
+ this.cardCounts.put(s, 0);
+ }
+ }
+
+
+ if (landsNeeded > 0) {
+ addLands(clrCnts);
+ }
+ if (logToConsole) {
+ System.out.println("Post Lands : " + deckList.size());
+ }
+ if (commanderCard.getRules().getColorIdentity().isColorless()&&landsNeeded>0){
+ // 10. Add non-basic lands that were drafted.
+ addWastesIfRequired();
+ }
+ fixDeckSize();
+ if (logToConsole) {
+ System.out.println("Post Size fix : " + deckList.size());
+ }
+
+ //Create Deck
+ final Deck result = new Deck(generateName());
+ result.getMain().add(deckList);
+
+ //Add remaining non-land colour matching cards to sideboard
+ final CardPool cp = result.getOrCreate(DeckSection.Sideboard);
+ Iterable potentialSideboard = Iterables.filter(aiPlayables,
+ Predicates.and(Predicates.compose(hasColor, PaperCard.FN_GET_RULES),
+ Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)));
+ int i=0;
+ while(i<15 && potentialSideboard.iterator().hasNext()){
+ PaperCard sbCard = potentialSideboard.iterator().next();
+ cp.add(sbCard);
+ aiPlayables.remove(sbCard);
+ rankedColorList.remove(sbCard);
+
+
+ ++i;
+ }
+ if (logToConsole) {
+ debugFinalDeck();
+ }
+ return result;
+
+ }
+
+ /**
+ * If evolving wilds is in the deck and there are fewer than 4 spaces for basic lands - remove evolving wilds
+ */
+ protected void checkEvolvingWilds(){
+ List evolvingWilds = Lists.newArrayList(Iterables.filter(deckList,PaperCard.Predicates.name("Evolving Wilds")));
+ if((evolvingWilds.size()>0 && landsNeeded<4 ) || colors.countColors()<2){
+ deckList.removeAll(evolvingWilds);
+ landsNeeded=landsNeeded+evolvingWilds.size();
+ aiPlayables.addAll(evolvingWilds);
+ }
+ }
+
+
+ protected void addLowCMCCard(){
+ final Iterable nonLands = Iterables.filter(aiPlayables,
+ Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES));
+ final PaperCard card = Iterables.getFirst(nonLands, null);
+ if (card != null) {
+ deckList.add(card);
+ aiPlayables.remove(card);
+ rankedColorList.remove(card);
+
+ landsNeeded--;
+ if (logToConsole) {
+ System.out.println("Low CMC: " + card.getName());
+ }
+ }
+ }
+
+ /**
+ * Set the basic land pool
+ * @param edition
+ * @return
+ */
+ protected boolean setBasicLandPool(String edition){
+ Predicate isSetBasicLand;
+ if (edition !=null){
+ isSetBasicLand = Predicates.and(IPaperCard.Predicates.printedInSet(edition),
+ Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES));
+ }else{
+ isSetBasicLand = Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES);
+ }
+
+ landPool = new DeckGenPool(format.getCardPool(fullCardDB).getAllCards(isSetBasicLand));
+ return landPool.contains("Plains");
+ }
+
+ /**
+ * Generate a descriptive name.
+ *
+ * @return name
+ */
+ private String generateName() {
+ return commanderCard.getName() +" based commander deck";
+ }
+
+ /**
+ * Print out listing of all cards for debugging.
+ */
+ private void debugFinalDeck() {
+ int i = 0;
+ System.out.println("DECK");
+ for (final PaperCard c : deckList) {
+ i++;
+ System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString());
+ }
+ i = 0;
+ System.out.println("NOT PLAYABLE");
+ for (final PaperCard c : availableList) {
+ i++;
+ System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString());
+ }
+ i = 0;
+ System.out.println("NOT PICKED");
+ for (final PaperCard c : aiPlayables) {
+ i++;
+ System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString());
+ }
+ }
+
+ /**
+ * If the deck does not have 40 cards, fix it. This method should not be
+ * called if the stuff above it is working correctly.
+ *
+ */
+ private void fixDeckSize() {
+ while (deckList.size() > targetSize) {
+ if (logToConsole) {
+ System.out.println("WARNING: Fixing deck size, currently " + deckList.size() + " cards.");
+ }
+ final PaperCard c = deckList.get(MyRandom.getRandom().nextInt(deckList.size() - 1));
+ deckList.remove(c);
+ aiPlayables.add(c);
+ if (logToConsole) {
+ System.out.println(" - Removed " + c.getName() + " randomly.");
+ }
+ }
+ if(deckList.size()==targetSize){
+ return;
+ }
+
+ Predicate possibleFromFullPool = new Predicate() {
+ @Override
+ public boolean apply(PaperCard card) {
+ return format.isLegalCard(card)
+ &&!card.getRules().getManaCost().isPureGeneric()
+ && colors.containsAllColorsFrom(card.getRules().getColorIdentity().getColor())
+ && !deckList.contains(card)
+ &&!card.getRules().getAiHints().getRemAIDecks()
+ &&!card.getRules().getAiHints().getRemRandomDecks()
+ &&!card.getRules().getMainPart().getType().isLand();
+ }
+ };
+ List randomPool = Lists.newArrayList(pool.getAllCards(possibleFromFullPool));
+ Collections.shuffle(randomPool,new Random());
+ Iterator iRandomPool=randomPool.iterator();
+ while (deckList.size() < targetSize) {
+ if (logToConsole) {
+ System.out.println("WARNING: Fixing deck size, currently " + deckList.size() + " cards.");
+ }
+ PaperCard randomCard = iRandomPool.next();
+ deckList.add(randomCard);
+ if (logToConsole) {
+ System.out.println(" - Added " + randomCard.getName() + " randomly.");
+ }
+ }
+ }
+
+ /**
+ * Find the sets that have basic lands for the available cards.
+ */
+ private void findBasicLandSets() {
+ final Set sets = new HashSet<>();
+ for (final PaperCard cp : aiPlayables) {
+ final CardEdition ee = FModel.getMagicDb().getEditions().get(cp.getEdition());
+ if( !sets.contains(cp.getEdition()) && CardEdition.Predicates.hasBasicLands.apply(ee)) {
+ sets.add(cp.getEdition());
+ }
+ }
+ setsWithBasicLands.addAll(sets);
+ if (setsWithBasicLands.isEmpty()) {
+ setsWithBasicLands.add("BFZ");
+ }
+ }
+
+ /**
+ * Add lands to fulfill the given color counts.
+ *
+ * @param clrCnts
+ * counts of lands needed, by color
+ */
+ private void addLands(final int[] clrCnts) {
+ // basic lands that are available in the deck
+ final Iterable basicLands = Iterables.filter(aiPlayables, Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES));
+ final Set snowLands = new HashSet();
+
+ // total of all ClrCnts
+ int totalColor = 0;
+ int numColors = 0;
+ for (int i = 0; i < 5; i++) {
+ totalColor += clrCnts[i];
+ if (clrCnts[i] > 0) {
+ numColors++;
+ }
+ }
+ /*if (totalColor == 0) {
+ for (int j = 0; j < nLand; j++) {
+ deckList.add(getBasicLand(i));
+ }
+ }*/
+
+ // do not update landsNeeded until after the loop, because the
+ // calculation involves landsNeeded
+ for (int i = 0; i < 5; i++) {
+ if (clrCnts[i] > 0) {
+ // calculate number of lands for each color
+ float p = (float) clrCnts[i] / (float) totalColor;
+ if (numColors == 2) {
+ // In the normal two-color case, constrain to within 40% and 60% so that the AI
+ // doesn't put too few lands of the lesser color, risking getting screwed on that color.
+ // Don't do this for the odd case where a third color had to be added to the deck.
+ p = Math.min(Math.max(p, 0.4f), 0.6f);
+ }
+ int nLand = Math.round(landsNeeded * p); // desired truncation to int
+ if (logToConsole) {
+ System.out.printf("Basics[%s]: %d/%d = %f%% = %d cards%n", MagicColor.Constant.BASIC_LANDS.get(i), clrCnts[i], totalColor, 100*p, nLand);
+ }
+
+ // if appropriate snow-covered lands are available, add them
+ for (final PaperCard cp : basicLands) {
+ if (cp.getName().equals(MagicColor.Constant.SNOW_LANDS.get(i))) {
+ snowLands.add(cp);
+ nLand--;
+ }
+ }
+
+ for (int j = 0; j < nLand; j++) {
+ deckList.add(getBasicLand(i));
+ }
+ }
+ }
+
+ // A common problem at this point is that p in the above loop was exactly 1/2,
+ // and nLand rounded up for both colors, so that one too many lands was added.
+ // So if the deck size is > 60, remove the last land added.
+ // Otherwise, the fixDeckSize() method would remove random cards.
+ while (deckList.size() > targetSize) {
+ deckList.remove(deckList.size() - 1);
+ }
+
+ deckList.addAll(snowLands);
+ aiPlayables.removeAll(snowLands);
+ rankedColorList.remove(snowLands);
+ }
+
+ /**
+ * Get basic land.
+ *
+ * @param basicLand
+ * the set to take basic lands from (pass 'null' for random).
+ * @return card
+ */
+ private PaperCard getBasicLand(final int basicLand) {
+ String set;
+ if (setsWithBasicLands.size() > 1) {
+ set = setsWithBasicLands.get(MyRandom.getRandom().nextInt(setsWithBasicLands.size() - 1));
+ } else {
+ set = setsWithBasicLands.get(0);
+ }
+ return FModel.getMagicDb().getCommonCards().getCard(MagicColor.Constant.BASIC_LANDS.get(basicLand), set);
+ }
+
+ /**
+ * Only adds wastes if present in the card pool but if present adds them all
+ */
+ private void addWastesIfRequired(){
+ Iterable wastes = Iterables.filter(aiPlayables,PaperCard.Predicates.name("Wastes"));
+ if(wastes.iterator().hasNext()){
+ PaperCard waste = wastes.iterator().next();
+ while(landsNeeded>0) {
+ deckList.add(waste);
+ landsNeeded--;
+ }
+ aiPlayables.remove(waste);
+ rankedColorList.remove(waste);
+ }
+ }
+
+ /**
+ * Attempt to optimize basic land counts according to color representation.
+ * Only consider colors that are supposed to be in the deck. It's not worth
+ * putting one land in for that random off-color card we had to stick in at
+ * the end...
+ *
+ * @return CCnt
+ */
+ private int[] calculateLandNeeds() {
+ final int[] clrCnts = { 0,0,0,0,0 };
+ // count each card color using mana costs
+ for (final PaperCard cp : deckList) {
+ final ManaCost mc = cp.getRules().getManaCost();
+
+ // count each mana symbol in the mana cost
+ for (final ManaCostShard shard : mc) {
+ for ( int i = 0 ; i < MagicColor.WUBRG.length; i++ ) {
+ final byte c = MagicColor.WUBRG[i];
+
+ if ( shard.canBePaidWithManaOfColor(c) && colors.hasAnyColor(c)) {
+ clrCnts[i]++;
+ }
+ }
+ }
+ }
+ return clrCnts;
+ }
+
+ /**
+ * Add non-basic lands to the deck.
+ */
+ private void addNonBasicLands() {
+ final Iterable lands = Iterables.filter(aiPlayables,
+ Predicates.compose(CardRulesPredicates.Presets.IS_NONBASIC_LAND, PaperCard.FN_GET_RULES));
+ List landsToAdd = new ArrayList<>();
+ int minBasics;//Keep a minimum number of basics to ensure playable decks
+ if(colors.isMonoColor()){
+ minBasics=Math.round((r.nextInt(15)+6)*targetSize/60);
+ }else{
+ minBasics=Math.round((r.nextInt(8)+6)*targetSize/60);
+ }
+
+
+ for (final PaperCard card : lands) {
+ if (landsNeeded > minBasics) {
+ // Throw out any dual-lands for the wrong colors. Assume
+ // everything else is either
+ // (a) dual-land of the correct two colors, or
+ // (b) a land that generates colorless mana and has some other
+ // beneficial effect.
+ if (!card.getRules().getColorIdentity().isColorless() && card.getRules().getColorIdentity().getSharedColors(colors).countColors()==0){
+ //skip as does not match colours
+ if (logToConsole) {
+ System.out.println("Excluding NonBasicLand: " + card.getName());
+ }
+ continue;
+ }
+ if (!inverseDLands.contains(card.getName())&&!dLands.contains(card.getName())&&r.nextInt(100)<90) {
+ landsToAdd.add(card);
+ landsNeeded--;
+ if (logToConsole) {
+ System.out.println("NonBasicLand[" + landsNeeded + "]:" + card.getName());
+ }
+ }
+ }
+ }
+ deckList.addAll(landsToAdd);
+ aiPlayables.removeAll(landsToAdd);
+ rankedColorList.removeAll(landsToAdd);
+ }
+
+
+ /**
+ * Add random cards to the deck.
+ *
+ * @param num
+ * number to add
+ */
+ private void addRandomCards(int num) {
+ Predicate possibleFromFullPool = new Predicate() {
+ @Override
+ public boolean apply(PaperCard card) {
+ return format.isLegalCard(card)
+ &&!card.getRules().getManaCost().isPureGeneric()
+ && colors.containsAllColorsFrom(card.getRules().getColorIdentity().getColor())
+ && !deckList.contains(card)
+ &&!card.getRules().getAiHints().getRemAIDecks()
+ &&!card.getRules().getAiHints().getRemRandomDecks()
+ &&!card.getRules().getMainPart().getType().isLand();
+ }
+ };
+ List randomPool = Lists.newArrayList(pool.getAllCards(possibleFromFullPool));
+ Collections.shuffle(randomPool,new Random());
+ Iterator iRandomPool=randomPool.iterator();
+ for(int i=0;i cards, int num) {
+ List cardsToAdd = new ArrayList<>();
+ for (final PaperCard card : cards) {
+ if(card.getRules().getMainPart().getType().isLand()){
+ continue;
+ }
+ if (num +1 > 0) {
+ cardsToAdd.add(card);
+ if (logToConsole) {
+ System.out.println("Extra needed[" + num + "]:" + card.getName() + " (" + card.getRules().getManaCost() + ")");
+ }
+ num--;
+ } else {
+ break;
+ }
+ }
+ deckList.addAll(cardsToAdd);
+ aiPlayables.removeAll(cardsToAdd);
+ rankedColorList.removeAll(cardsToAdd);
+ }
+
+ /**
+ * Add cards to the deck, trying to follow some mana curve. Trying to
+ * have generous limits at each cost, but perhaps still too strict. But
+ * we're trying to prevent the AI from adding everything at a single cost.
+ *
+ * @param creatures
+ * cards to choose from
+ * @param num
+ * number to add
+ */
+ private void addManaCurveCards(final Iterable creatures, int num, String nameForLog) {
+/* // Add the deck card
+ if(commanderCard.getRules().getMainPart().getType().isCreature()) {
+ keyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(commanderCard.getName()));
+ final List keyCardList = Lists.newArrayList(keyCards);
+ deckList.addAll(keyCardList);
+ aiPlayables.removeAll(keyCardList);
+ rankedColorList.removeAll(keyCardList);
+ }*/
+ final Map targetCMCs = new HashMap<>();
+ targetCMCs.put(1,Math.round((r.nextInt(4)+2)*targetSize/60));//2
+ targetCMCs.put(2,Math.round((r.nextInt(5)+5)*targetSize/60));//6
+ targetCMCs.put(3,Math.round((r.nextInt(5)+6)*targetSize/60));//7
+ targetCMCs.put(4,Math.round((r.nextInt(3)+3)*targetSize/60));//4
+ targetCMCs.put(5,Math.round((r.nextInt(3)+3)*targetSize/60));//3
+ targetCMCs.put(6,Math.round((r.nextInt(3)+1)*targetSize/60));//2
+
+
+ final Map creatureCosts = new HashMap();
+ for (int i = 1; i < 7; i++) {
+ creatureCosts.put(i, 0);
+ }
+ final Predicate filter = Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE,
+ PaperCard.FN_GET_RULES);
+ for (final IPaperCard creature : Iterables.filter(deckList, filter)) {
+ int cmc = creature.getRules().getManaCost().getCMC();
+ if (cmc < 1) {
+ cmc = 1;
+ } else if (cmc > 6) {
+ cmc = 6;
+ }
+ creatureCosts.put(cmc, creatureCosts.get(cmc) + 1);
+ }
+
+ List creaturesToAdd = new ArrayList<>();
+ for (final PaperCard card : creatures) {
+ int cmc = card.getRules().getManaCost().getCMC();
+ if (cmc < 1) {
+ cmc = 1;
+ } else if (cmc > 6) {
+ cmc = 6;
+ }
+ final Integer currentAtCmc = creatureCosts.get(cmc);
+ boolean willAddCreature = false;
+ if (cmc <= 1 && currentAtCmc < targetCMCs.get(1)) {
+ willAddCreature = true;
+ } else if (cmc == 2 && currentAtCmc < targetCMCs.get(2)) {
+ willAddCreature = true;
+ } else if (cmc == 3 && currentAtCmc < targetCMCs.get(3)) {
+ willAddCreature = true;
+ } else if (cmc == 4 && currentAtCmc < targetCMCs.get(4)) {
+ willAddCreature = true;
+ } else if (cmc == 5 && currentAtCmc < targetCMCs.get(5)) {
+ willAddCreature = true;
+ } else if (cmc >= 6 && currentAtCmc < targetCMCs.get(6)) {
+ willAddCreature = true;
+ }
+
+ if (willAddCreature) {
+ creaturesToAdd.add(card);
+ num--;
+ creatureCosts.put(cmc, creatureCosts.get(cmc) + 1);
+ if (logToConsole) {
+ System.out.println(nameForLog+"[" + num + "]:" + card.getName() + " (" + card.getRules().getManaCost() + ")");
+ }
+ } else {
+ if (logToConsole) {
+ System.out.println(card.getName() + " not added because CMC " + card.getRules().getManaCost().getCMC()
+ + " has " + currentAtCmc + " already.");
+ }
+ }
+ if (num <= 0) {
+ break;
+ }
+ }
+ deckList.addAll(creaturesToAdd);
+ aiPlayables.removeAll(creaturesToAdd);
+ rankedColorList.removeAll(creaturesToAdd);
+ }
+
+ /**
+ * Calculate average CMC.
+ *
+ * @param cards
+ * cards to choose from
+ * @return the average
+ */
+ private static double getAverageCMC(final List cards) {
+ double sum = 0.0;
+ for (final IPaperCard cardPrinted : cards) {
+ sum += cardPrinted.getRules().getManaCost().getCMC();
+ }
+ return sum / cards.size();
+ }
+
+ /**
+ * Calculate max CMC.
+ *
+ * @param cards
+ * cards to choose from
+ * @return the average
+ */
+ private static int getMaxCMC(final List cards) {
+ int max = 0;
+ for (final IPaperCard cardPrinted : cards) {
+ if(cardPrinted.getRules().getManaCost().getCMC()>max) {
+ max = cardPrinted.getRules().getManaCost().getCMC();
+ }
+ }
+ return max;
+ }
+
+ /**
+ * @return the colors
+ */
+ public ColorSet getColors() {
+ return colors;
+ }
+
+ /**
+ * @param colors0
+ * the colors to set
+ */
+ public void setColors(final ColorSet colors0) {
+ colors = colors0;
+ }
+
+ /**
+ * @return the aiPlayables
+ */
+ public List getAiPlayables() {
+ return aiPlayables;
+ }
+
+}
diff --git a/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java b/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java
index ef205c81ee6..4c6ca201730 100644
--- a/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java
+++ b/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java
@@ -190,7 +190,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase {
// extras.
double avCMC=getAverageCMC(deckList);
int maxCMC=getMaxCMC(deckList);
- if (deckList.size() == numSpellsNeeded && avCMC < 4) {
+ if (deckList.size() >= numSpellsNeeded && avCMC < 4) {
addLowCMCCard();
}
if (deckList.size() >= numSpellsNeeded && avCMC < 3 && maxCMC<6) {
diff --git a/forge-gui/src/main/java/forge/model/FModel.java b/forge-gui/src/main/java/forge/model/FModel.java
index 4be373b2543..33f6bfc8d89 100644
--- a/forge-gui/src/main/java/forge/model/FModel.java
+++ b/forge-gui/src/main/java/forge/model/FModel.java
@@ -27,6 +27,8 @@ import forge.ai.AiProfileUtil;
import forge.card.CardPreferences;
import forge.card.CardType;
import forge.deck.CardRelationMatrixGenerator;
+import forge.deck.DeckFormat;
+import forge.deck.io.CardThemedMatrixIO;
import forge.deck.io.DeckPreferences;
import forge.game.GameFormat;
import forge.game.GameType;
@@ -218,11 +220,18 @@ public final class FModel {
AiProfileUtil.loadAllProfiles(ForgeConstants.AI_PROFILE_DIR);
//generate Deck Gen matrix
- if(!FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY)) {
- CardRelationMatrixGenerator.initialize();
+ if(!FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY)
+ &&FModel.getPreferences().getPrefBoolean(FPref.DECKGEN_CARDBASED)) {
+ deckGenMatrixLoaded=CardRelationMatrixGenerator.initialize();
}
}
+ private static boolean deckGenMatrixLoaded=false;
+
+ public static boolean isdeckGenMatrixLoaded(){
+ return deckGenMatrixLoaded;
+ }
+
public static QuestController getQuest() {
return quest;
}
diff --git a/forge-gui/src/main/java/forge/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/properties/ForgePreferences.java
index f3cce03850b..c5258999e0e 100644
--- a/forge-gui/src/main/java/forge/properties/ForgePreferences.java
+++ b/forge-gui/src/main/java/forge/properties/ForgePreferences.java
@@ -34,6 +34,22 @@ public class ForgePreferences extends PreferencesStore {
CONSTRUCTED_P6_DECK_STATE(""),
CONSTRUCTED_P7_DECK_STATE(""),
CONSTRUCTED_P8_DECK_STATE(""),
+ COMMANDER_P1_DECK_STATE(""),
+ COMMANDER_P2_DECK_STATE(""),
+ COMMANDER_P3_DECK_STATE(""),
+ COMMANDER_P4_DECK_STATE(""),
+ COMMANDER_P5_DECK_STATE(""),
+ COMMANDER_P6_DECK_STATE(""),
+ COMMANDER_P7_DECK_STATE(""),
+ COMMANDER_P8_DECK_STATE(""),
+ TINY_LEADER_P1_DECK_STATE(""),
+ TINY_LEADER_P2_DECK_STATE(""),
+ TINY_LEADER_P3_DECK_STATE(""),
+ TINY_LEADER_P4_DECK_STATE(""),
+ TINY_LEADER_P5_DECK_STATE(""),
+ TINY_LEADER_P6_DECK_STATE(""),
+ TINY_LEADER_P7_DECK_STATE(""),
+ TINY_LEADER_P8_DECK_STATE(""),
UI_LANDSCAPE_MODE ("false"),
UI_COMPACT_MAIN_MENU ("false"),
UI_USE_OLD ("false"),
@@ -140,6 +156,7 @@ public class ForgePreferences extends PreferencesStore {
DECKGEN_SINGLETONS ("false"),
DECKGEN_ARTIFACTS ("false"),
DECKGEN_NOSMALL ("false"),
+ DECKGEN_CARDBASED ("true"),
PHASE_AI_UPKEEP ("false"),
PHASE_AI_DRAW ("false"),
@@ -210,6 +227,19 @@ public class ForgePreferences extends PreferencesStore {
CONSTRUCTED_P3_DECK_STATE, CONSTRUCTED_P4_DECK_STATE,
CONSTRUCTED_P5_DECK_STATE, CONSTRUCTED_P6_DECK_STATE,
CONSTRUCTED_P7_DECK_STATE, CONSTRUCTED_P8_DECK_STATE };
+
+ public static FPref[] COMMANDER_DECK_STATES = {
+ COMMANDER_P1_DECK_STATE, COMMANDER_P2_DECK_STATE,
+ COMMANDER_P3_DECK_STATE, COMMANDER_P4_DECK_STATE,
+ COMMANDER_P5_DECK_STATE, COMMANDER_P6_DECK_STATE,
+ COMMANDER_P7_DECK_STATE, COMMANDER_P8_DECK_STATE };
+
+ public static FPref[] TINY_LEADER_DECK_STATES = {
+ TINY_LEADER_P1_DECK_STATE, TINY_LEADER_P2_DECK_STATE,
+ TINY_LEADER_P3_DECK_STATE, TINY_LEADER_P4_DECK_STATE,
+ TINY_LEADER_P5_DECK_STATE, TINY_LEADER_P6_DECK_STATE,
+ TINY_LEADER_P7_DECK_STATE, TINY_LEADER_P8_DECK_STATE };
+
}
/** Instantiates a ForgePreferences object. */