From b19efd6c0d94e6734c191ce1b2b7c046d162894f Mon Sep 17 00:00:00 2001 From: Agetian Date: Sun, 18 Jun 2017 16:41:51 +0000 Subject: [PATCH] - Implemented a way to auto-suggest basic lands in the Add Basic Lands dialog in desktop and mobile Forge. --- .../deckeditor/AddBasicLandsDialog.java | 24 ++++++++ .../src/forge/deck/AddBasicLandsDialog.java | 29 +++++++++- forge-gui/release-files/CHANGES.txt | 3 + .../src/main/java/forge/deck/DeckgenUtil.java | 57 +++++++++++++++++++ 4 files changed, 111 insertions(+), 2 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/AddBasicLandsDialog.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/AddBasicLandsDialog.java index fe966405c67..433493a03d7 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/AddBasicLandsDialog.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/AddBasicLandsDialog.java @@ -42,17 +42,21 @@ import forge.card.mana.ManaCostShard; import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckProxy; +import forge.deck.DeckgenUtil; import forge.item.PaperCard; import forge.model.FModel; import forge.toolbox.FComboBox; import forge.toolbox.FComboBoxPanel; import forge.toolbox.FHtmlViewer; import forge.toolbox.FLabel; +import forge.toolbox.FMouseAdapter; import forge.toolbox.FOptionPane; import forge.toolbox.FSkin; import forge.toolbox.FTextField; import forge.toolbox.FSkin.SkinnedPanel; import forge.view.arcane.CardPanel; +import java.awt.event.MouseEvent; +import java.util.Map; @SuppressWarnings("serial") @@ -99,6 +103,26 @@ public class AddBasicLandsDialog { panel.add(lblDeckInfo); lblDeckInfo.setFont(FSkin.getFont(14)); + lblDeckInfo.addMouseListener(new FMouseAdapter() { + @Override + public void onLeftDoubleClick(MouseEvent e) { + Map suggestionMap = DeckgenUtil.suggestBasicLandCount(deck); + pnlPlains.count = suggestionMap.get(ManaCostShard.WHITE); + pnlIsland.count = suggestionMap.get(ManaCostShard.BLUE); + pnlSwamp.count = suggestionMap.get(ManaCostShard.BLACK); + pnlMountain.count = suggestionMap.get(ManaCostShard.RED); + pnlForest.count = suggestionMap.get(ManaCostShard.GREEN); + + pnlPlains.lblCount.setText(String.valueOf(pnlPlains.count)); + pnlIsland.lblCount.setText(String.valueOf(pnlIsland.count)); + pnlSwamp.lblCount.setText(String.valueOf(pnlSwamp.count)); + pnlMountain.lblCount.setText(String.valueOf(pnlMountain.count)); + pnlForest.lblCount.setText(String.valueOf(pnlForest.count)); + + updateDeckInfoLabel(); + } + }); + lblDeckInfo.setToolTipText("Deck statistics. Double click to auto-suggest basic lands."); cbLandSet.addActionListener(new ActionListener() { @Override diff --git a/forge-gui-mobile/src/forge/deck/AddBasicLandsDialog.java b/forge-gui-mobile/src/forge/deck/AddBasicLandsDialog.java index 76250a2ab6e..b3f86bc6197 100644 --- a/forge-gui-mobile/src/forge/deck/AddBasicLandsDialog.java +++ b/forge-gui-mobile/src/forge/deck/AddBasicLandsDialog.java @@ -48,12 +48,15 @@ import forge.util.Callback; import forge.util.Utils; import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment; +import java.util.Map; public class AddBasicLandsDialog extends FDialog { private static final float ADD_BTN_SIZE = Utils.AVG_FINGER_HEIGHT * 0.75f; private static final float LAND_PANEL_PADDING = Utils.scale(3); + private final Deck currentDeck; + private final Callback callback; private final FLabel lblLandSet = add(new FLabel.Builder().text("Land Set:").font(FSkinFont.get(12)).textColor(FLabel.INLINE_LABEL_COLOR).build()); @@ -86,15 +89,37 @@ public class AddBasicLandsDialog extends FDialog { private final LandPanel pnlMountain = scroller.add(new LandPanel("Mountain")); private final LandPanel pnlForest = scroller.add(new LandPanel("Forest")); - private final FTextArea lblDeckInfo = add(new FTextArea(true)); + private final FTextArea lblDeckInfo = add(new FTextArea(true) { + @Override + public boolean tap(float x, float y, int count) { + if (count == 2) { + Map suggestionMap = DeckgenUtil.suggestBasicLandCount(currentDeck); + pnlPlains.count = suggestionMap.get(ManaCostShard.WHITE); + pnlIsland.count = suggestionMap.get(ManaCostShard.BLUE); + pnlSwamp.count = suggestionMap.get(ManaCostShard.BLACK); + pnlMountain.count = suggestionMap.get(ManaCostShard.RED); + pnlForest.count = suggestionMap.get(ManaCostShard.GREEN); + + pnlPlains.lblCount.setText(String.valueOf(pnlPlains.count)); + pnlIsland.lblCount.setText(String.valueOf(pnlIsland.count)); + pnlSwamp.lblCount.setText(String.valueOf(pnlSwamp.count)); + pnlMountain.lblCount.setText(String.valueOf(pnlMountain.count)); + pnlForest.lblCount.setText(String.valueOf(pnlForest.count)); + + updateDeckInfoLabel(); + } + return true; + } + }); private int nonLandCount, oldLandCount; private CardEdition landSet; public AddBasicLandsDialog(Deck deck, CardEdition defaultLandSet, final Callback callback0) { - super("Add Basic Lands \n" + deck.getName(), 2); + super("Add Basic Lands to " + deck.getName() + "\n(double-tap statistics to auto-suggest)", 2); callback = callback0; + currentDeck = deck; lblDeckInfo.setAlignment(HAlignment.CENTER); lblDeckInfo.setFont(FSkinFont.get(12)); diff --git a/forge-gui/release-files/CHANGES.txt b/forge-gui/release-files/CHANGES.txt index eb4b6f491bc..0bf4d9fa2db 100644 --- a/forge-gui/release-files/CHANGES.txt +++ b/forge-gui/release-files/CHANGES.txt @@ -8,6 +8,9 @@ Some adjustments have been made to the algorithm used for calculating probabilit - Achievement System updates - Planar Conquest now uses its own set of achievements separate from Constructed achievements (this includes the Planar Conquest-exclusive Planeswalker game mode). Puzzle Mode should no longer give Constructed achievements and uses its own achievement set (currently only the number of solved puzzles is tracked, but this may improve in the future). +- Automatically Suggest Basic Lands in Deck Editor - +It is now possible to ask Forge to automatically suggest basic lands for your deck in the Add Basic Lands dialog of the deck editor. In desktop Forge, this is accomplished by double-clicking the statistics text in the bottom part of the dialog (hopefully someone will be able to code in a better, more intuitive way of doing this eventually). In mobile Forge, this is done by double tapping the statistics text. A tooltip is available in desktop Forge (and a hint text in mobile Forge) to make this feature more visible. Three deck sizes are supported by this algorithm: 40-card decks (if the number of cards in the main portion of the deck is less than 30, the algorithm will target a 40-card deck), 60-card decks (if the number of cards in the main portion is between 30 and 60, a 60-card deck will be targeted), and 100-card decks (if the deck is already above 60 cards when the feature is used, a 100-card deck will be targeted). + - Java 8 - As part of increased privacy protections (cardforge.org now uses SSL), the content downloaders now require a minimum Java version of 8u101. In the future, all of Forge will be updated to require Java 8. If you're running the latest version of Java, but Forge does not let you use the content downloaders, check to make sure that you've uninstalled old versions. Additionally, check any JDK installations you have and ensure Forge is using the most up-to-date version. diff --git a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java index 66cf34c15fa..12b90abb919 100644 --- a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java +++ b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java @@ -9,6 +9,8 @@ import forge.card.CardDb; import forge.card.CardRules; import forge.card.CardRulesPredicates; import forge.card.ColorSet; +import forge.card.mana.ManaCost; +import forge.card.mana.ManaCostShard; import forge.deck.generation.*; import forge.game.GameFormat; import forge.game.GameType; @@ -530,4 +532,59 @@ public class DeckgenUtil { 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(); + + // attempt to determine if building for sealed, constructed or EDH + int targetDeckSize = cards.size() < 30 ? 40 + : cards.size() > 60 ? 100 : 60; + + int numLandsToAdd = targetDeckSize - cards.size(); + + for (PaperCard c : d.getMain().toFlatList()) { + ManaCost m = c.getRules().getManaCost(); + W += m.getShardCount(ManaCostShard.WHITE); + U += m.getShardCount(ManaCostShard.BLUE); + R += m.getShardCount(ManaCostShard.RED); + B += m.getShardCount(ManaCostShard.BLACK); + G += m.getShardCount(ManaCostShard.GREEN); + } + total = W + U + R + B + G; + + int whiteSources = Math.round(numLandsToAdd * ((float)W / (float)total)); + numLandsToAdd -= whiteSources; + total -= W; + int blueSources = Math.round(numLandsToAdd * ((float)U / (float)total)); + numLandsToAdd -= blueSources; + total -= U; + int blackSources = Math.round(numLandsToAdd * ((float)B / (float)total)); + numLandsToAdd -= blackSources; + total -= B; + int redSources = Math.round(numLandsToAdd * ((float)R / (float)total)); + numLandsToAdd -= redSources; + total -= R; + int greenSources = Math.round(numLandsToAdd * ((float)G / (float)total)); + numLandsToAdd -= greenSources; + total -= G; + + // in case of a rounding error, add the remaining lands to one of the relevant colors + if (numLandsToAdd > 0) { + if (whiteSources > 0) { whiteSources += numLandsToAdd; } + else if (blueSources > 0) { blueSources += numLandsToAdd; } + else if (blackSources > 0) { blackSources += numLandsToAdd; } + else if (redSources > 0) { redSources += numLandsToAdd; } + else if (greenSources > 0) { greenSources += numLandsToAdd; } + } + + HashMap suggestionMap = new HashMap<>(); + suggestionMap.put(ManaCostShard.WHITE, whiteSources); + suggestionMap.put(ManaCostShard.BLUE, blueSources); + suggestionMap.put(ManaCostShard.RED, redSources); + suggestionMap.put(ManaCostShard.BLACK, blackSources); + suggestionMap.put(ManaCostShard.GREEN, greenSources); + + return suggestionMap; + } }