Deck Importer support for Adventure, Quest, and Planar Conquest (#8681)

* Some cleanup.

* Expanded/fixed basic land set functions for quest and adventure.

* Get land sets from unlocked planes in conquest mode.

* Add importer for Adventure, Quest, and Conquest.

* Remove unused import

* Remove redundant override

* Deprecate hasBasicLands predicate.

* Delete getManaNameAndSymbol

---------

Co-authored-by: Jetz <Jetz722@gmail.com>
This commit is contained in:
Jetz72
2025-09-11 11:22:17 -05:00
committed by GitHub
parent db8e084332
commit 43a1570601
22 changed files with 596 additions and 437 deletions

View File

@@ -1018,16 +1018,13 @@ public final class CardEdition implements Comparable<CardEdition> {
public static final Predicate<CardEdition> HAS_BOOSTER_BOX = edition -> edition.getBoosterBoxCount() > 0; public static final Predicate<CardEdition> HAS_BOOSTER_BOX = edition -> edition.getBoosterBoxCount() > 0;
@Deprecated //Use CardEdition::hasBasicLands and a nonnull test.
public static final Predicate<CardEdition> hasBasicLands = ed -> { public static final Predicate<CardEdition> hasBasicLands = ed -> {
if (ed == null) { if (ed == null) {
// Happens for new sets with "???" code // Happens for new sets with "???" code
return false; return false;
} }
for(String landName : MagicColor.Constant.BASIC_LANDS) { return ed.hasBasicLands();
if (null == StaticData.instance().getCommonCards().getCard(landName, ed.getCode(), 0))
return false;
}
return true;
}; };
} }
@@ -1048,7 +1045,7 @@ public final class CardEdition implements Comparable<CardEdition> {
public boolean hasBasicLands() { public boolean hasBasicLands() {
for(String landName : MagicColor.Constant.BASIC_LANDS) { for(String landName : MagicColor.Constant.BASIC_LANDS) {
if (null == StaticData.instance().getCommonCards().getCard(landName, this.getCode(), 0)) if (this.getCardInSet(landName).isEmpty())
return false; return false;
} }
return true; return true;

View File

@@ -49,6 +49,16 @@ public class DeckRecognizer {
LIMITED_CARD, LIMITED_CARD,
CARD_FROM_NOT_ALLOWED_SET, CARD_FROM_NOT_ALLOWED_SET,
CARD_FROM_INVALID_SET, CARD_FROM_INVALID_SET,
/**
* Valid card request, but can't be imported because the player does not have enough copies.
* Should be replaced with a different printing if possible.
*/
CARD_NOT_IN_INVENTORY,
/**
* Valid card request for a card that isn't in the player's inventory, but new copies can be acquired freely.
* Usually used for basic lands. Should be supplied to the import controller by the editor.
*/
FREE_CARD_NOT_IN_INVENTORY,
// Warning messages // Warning messages
WARNING_MESSAGE, WARNING_MESSAGE,
UNKNOWN_CARD, UNKNOWN_CARD,
@@ -63,10 +73,14 @@ public class DeckRecognizer {
CARD_TYPE, CARD_TYPE,
CARD_RARITY, CARD_RARITY,
CARD_CMC, CARD_CMC,
MANA_COLOUR MANA_COLOUR;
public static final EnumSet<TokenType> CARD_TOKEN_TYPES = EnumSet.of(LEGAL_CARD, LIMITED_CARD, CARD_FROM_NOT_ALLOWED_SET, CARD_FROM_INVALID_SET, CARD_NOT_IN_INVENTORY, FREE_CARD_NOT_IN_INVENTORY);
public static final EnumSet<TokenType> IN_DECK_TOKEN_TYPES = EnumSet.of(LEGAL_CARD, LIMITED_CARD, DECK_NAME, FREE_CARD_NOT_IN_INVENTORY);
public static final EnumSet<TokenType> CARD_PLACEHOLDER_TOKEN_TYPES = EnumSet.of(CARD_TYPE, CARD_RARITY, CARD_CMC, MANA_COLOUR);
} }
public enum LimitedCardType{ public enum LimitedCardType {
BANNED, BANNED,
RESTRICTED, RESTRICTED,
} }
@@ -108,6 +122,10 @@ public class DeckRecognizer {
return new Token(TokenType.CARD_FROM_INVALID_SET, count, card, cardRequestHasSetCode); return new Token(TokenType.CARD_FROM_INVALID_SET, count, card, cardRequestHasSetCode);
} }
public static Token NotInInventoryFree(final PaperCard card, final int count, final DeckSection section) {
return new Token(TokenType.FREE_CARD_NOT_IN_INVENTORY, count, card, section, true);
}
// WARNING MESSAGES // WARNING MESSAGES
// ================ // ================
public static Token UnknownCard(final String cardName, final String setCode, final int count) { public static Token UnknownCard(final String cardName, final String setCode, final int count) {
@@ -126,6 +144,10 @@ public class DeckRecognizer {
return new Token(TokenType.WARNING_MESSAGE, msg); return new Token(TokenType.WARNING_MESSAGE, msg);
} }
public static Token NotInInventory(final PaperCard card, final int count, final DeckSection section) {
return new Token(TokenType.CARD_NOT_IN_INVENTORY, count, card, section, false);
}
/* ================================= /* =================================
* DECK SECTIONS * DECK SECTIONS
* ================================= */ * ================================= */
@@ -239,14 +261,11 @@ public class DeckRecognizer {
/** /**
* Filters all token types that have a PaperCard instance set (not null) * Filters all token types that have a PaperCard instance set (not null)
* @return true for tokens of type: * @return true for tokens of type:
* LEGAL_CARD, LIMITED_CARD, CARD_FROM_NOT_ALLOWED_SET and CARD_FROM_INVALID_SET. * LEGAL_CARD, LIMITED_CARD, CARD_FROM_NOT_ALLOWED_SET and CARD_FROM_INVALID_SET, CARD_NOT_IN_INVENTORY, FREE_CARD_NOT_IN_INVENTORY.
* False otherwise. * False otherwise.
*/ */
public boolean isCardToken() { public boolean isCardToken() {
return (this.type == TokenType.LEGAL_CARD || return TokenType.CARD_TOKEN_TYPES.contains(this.type);
this.type == TokenType.LIMITED_CARD ||
this.type == TokenType.CARD_FROM_NOT_ALLOWED_SET ||
this.type == TokenType.CARD_FROM_INVALID_SET);
} }
/** /**
@@ -255,9 +274,7 @@ public class DeckRecognizer {
* LEGAL_CARD, LIMITED_CARD, DECK_NAME; false otherwise. * LEGAL_CARD, LIMITED_CARD, DECK_NAME; false otherwise.
*/ */
public boolean isTokenForDeck() { public boolean isTokenForDeck() {
return (this.type == TokenType.LEGAL_CARD || return TokenType.IN_DECK_TOKEN_TYPES.contains(this.type);
this.type == TokenType.LIMITED_CARD ||
this.type == TokenType.DECK_NAME);
} }
/** /**
@@ -266,7 +283,7 @@ public class DeckRecognizer {
* False otherwise. * False otherwise.
*/ */
public boolean isCardTokenForDeck() { public boolean isCardTokenForDeck() {
return (this.type == TokenType.LEGAL_CARD || this.type == TokenType.LIMITED_CARD); return isCardToken() && isTokenForDeck();
} }
/** /**
@@ -276,10 +293,7 @@ public class DeckRecognizer {
* CARD_RARITY, CARD_CMC, CARD_TYPE, MANA_COLOUR * CARD_RARITY, CARD_CMC, CARD_TYPE, MANA_COLOUR
*/ */
public boolean isCardPlaceholder(){ public boolean isCardPlaceholder(){
return (this.type == TokenType.CARD_RARITY || return TokenType.CARD_PLACEHOLDER_TOKEN_TYPES.contains(this.type);
this.type == TokenType.CARD_CMC ||
this.type == TokenType.MANA_COLOUR ||
this.type == TokenType.CARD_TYPE);
} }
/** Determines if current token is a Deck Section token /** Determines if current token is a Deck Section token
@@ -536,7 +550,7 @@ public class DeckRecognizer {
PaperCard tokenCard = token.getCard(); PaperCard tokenCard = token.getCard();
if (isAllowed(tokenSection)) { if (isAllowed(tokenSection)) {
if (!tokenSection.equals(referenceDeckSectionInParsing)) { if (tokenSection != referenceDeckSectionInParsing) {
Token sectionToken = Token.DeckSection(tokenSection.name(), this.allowedDeckSections); Token sectionToken = Token.DeckSection(tokenSection.name(), this.allowedDeckSections);
// just check that last token is stack is a card placeholder. // just check that last token is stack is a card placeholder.
// In that case, add the new section token before the placeholder // In that case, add the new section token before the placeholder
@@ -575,7 +589,7 @@ public class DeckRecognizer {
refLine = purgeAllLinks(refLine); refLine = purgeAllLinks(refLine);
String line; String line;
if (StringUtils.startsWith(refLine, LINE_COMMENT_DELIMITER_OR_MD_HEADER)) if (refLine.startsWith(LINE_COMMENT_DELIMITER_OR_MD_HEADER))
line = refLine.replaceAll(LINE_COMMENT_DELIMITER_OR_MD_HEADER, ""); line = refLine.replaceAll(LINE_COMMENT_DELIMITER_OR_MD_HEADER, "");
else else
line = refLine.trim(); // Remove any trailing formatting line = refLine.trim(); // Remove any trailing formatting
@@ -584,7 +598,7 @@ public class DeckRecognizer {
// Final fantasy cards like Summon: Choco/Mog should be ommited to be recognized. TODO: fix maybe for future cards // Final fantasy cards like Summon: Choco/Mog should be ommited to be recognized. TODO: fix maybe for future cards
if (!line.contains("Summon:")) if (!line.contains("Summon:"))
line = SEARCH_SINGLE_SLASH.matcher(line).replaceFirst(" // "); line = SEARCH_SINGLE_SLASH.matcher(line).replaceFirst(" // ");
if (StringUtils.startsWith(line, ASTERISK)) // markdown lists (tappedout md export) if (line.startsWith(ASTERISK)) // Markdown lists (tappedout md export)
line = line.substring(2); line = line.substring(2);
// == Patches to Corner Cases // == Patches to Corner Cases
@@ -600,8 +614,8 @@ public class DeckRecognizer {
Token result = recogniseCardToken(line, referenceSection); Token result = recogniseCardToken(line, referenceSection);
if (result == null) if (result == null)
result = recogniseNonCardToken(line); result = recogniseNonCardToken(line);
return result != null ? result : StringUtils.startsWith(refLine, DOUBLE_SLASH) || return result != null ? result : refLine.startsWith(DOUBLE_SLASH) ||
StringUtils.startsWith(refLine, LINE_COMMENT_DELIMITER_OR_MD_HEADER) ? refLine.startsWith(LINE_COMMENT_DELIMITER_OR_MD_HEADER) ?
new Token(TokenType.COMMENT, 0, refLine) : new Token(TokenType.UNKNOWN_TEXT, 0, refLine); new Token(TokenType.COMMENT, 0, refLine) : new Token(TokenType.UNKNOWN_TEXT, 0, refLine);
} }
@@ -613,7 +627,7 @@ public class DeckRecognizer {
while (m.find()) { while (m.find()) {
line = line.replaceAll(m.group(), "").trim(); line = line.replaceAll(m.group(), "").trim();
} }
if (StringUtils.endsWith(line, "()")) if (line.endsWith("()"))
return line.substring(0, line.length()-2); return line.substring(0, line.length()-2);
return line; return line;
} }
@@ -741,21 +755,12 @@ public class DeckRecognizer {
// This would save tons of time in parsing Input + would also allow to return UnsupportedCardTokens beforehand // This would save tons of time in parsing Input + would also allow to return UnsupportedCardTokens beforehand
private DeckSection getTokenSection(String deckSec, DeckSection currentDeckSection, PaperCard card){ private DeckSection getTokenSection(String deckSec, DeckSection currentDeckSection, PaperCard card){
if (deckSec != null) { if (deckSec != null) {
DeckSection cardSection; DeckSection cardSection = switch (deckSec.toUpperCase().trim()) {
switch (deckSec.toUpperCase().trim()) { case "MB" -> DeckSection.Main;
case "MB": case "SB" -> DeckSection.Sideboard;
cardSection = DeckSection.Main; case "CM" -> DeckSection.Commander;
break; default -> DeckSection.matchingSection(card);
case "SB": };
cardSection = DeckSection.Sideboard;
break;
case "CM":
cardSection = DeckSection.Commander;
break;
default:
cardSection = DeckSection.matchingSection(card);
break;
}
if (cardSection.validate(card)) if (cardSection.validate(card))
return cardSection; return cardSection;
} }
@@ -1017,51 +1022,21 @@ public class DeckRecognizer {
private static MagicColor.Color getMagicColor(String colorName){ private static MagicColor.Color getMagicColor(String colorName){
if (colorName.toLowerCase().startsWith("multi") || colorName.equalsIgnoreCase("m")) if (colorName.toLowerCase().startsWith("multi") || colorName.equalsIgnoreCase("m"))
return null; // will be handled separately return null; // will be handled separately
return MagicColor.Color.fromByte(MagicColor.fromName(colorName.toLowerCase()));
byte color = MagicColor.fromName(colorName.toLowerCase());
switch (color) {
case MagicColor.WHITE:
return MagicColor.Color.WHITE;
case MagicColor.BLUE:
return MagicColor.Color.BLUE;
case MagicColor.BLACK:
return MagicColor.Color.BLACK;
case MagicColor.RED:
return MagicColor.Color.RED;
case MagicColor.GREEN:
return MagicColor.Color.GREEN;
default:
return MagicColor.Color.COLORLESS;
}
} }
public static String getLocalisedMagicColorName(String colorName){ public static String getLocalisedMagicColorName(String colorName){
Localizer localizer = Localizer.getInstance(); Localizer localizer = Localizer.getInstance();
switch(colorName.toLowerCase()){ return switch (colorName.toLowerCase()) {
case MagicColor.Constant.WHITE: case MagicColor.Constant.WHITE -> localizer.getMessage("lblWhite");
return localizer.getMessage("lblWhite"); case MagicColor.Constant.BLUE -> localizer.getMessage("lblBlue");
case MagicColor.Constant.BLACK -> localizer.getMessage("lblBlack");
case MagicColor.Constant.BLUE: case MagicColor.Constant.RED -> localizer.getMessage("lblRed");
return localizer.getMessage("lblBlue"); case MagicColor.Constant.GREEN -> localizer.getMessage("lblGreen");
case MagicColor.Constant.COLORLESS -> localizer.getMessage("lblColorless");
case MagicColor.Constant.BLACK: case "multicolour", "multicolor" -> localizer.getMessage("lblMulticolor");
return localizer.getMessage("lblBlack"); default -> "";
};
case MagicColor.Constant.RED:
return localizer.getMessage("lblRed");
case MagicColor.Constant.GREEN:
return localizer.getMessage("lblGreen");
case MagicColor.Constant.COLORLESS:
return localizer.getMessage("lblColorless");
case "multicolour":
case "multicolor":
return localizer.getMessage("lblMulticolor");
default:
return "";
}
} }
/** /**
@@ -1080,37 +1055,6 @@ public class DeckRecognizer {
return ""; return "";
} }
private static Pair<String, String> getManaNameAndSymbol(String matchedMana) {
if (matchedMana == null)
return null;
Localizer localizer = Localizer.getInstance();
switch (matchedMana.toLowerCase()) {
case MagicColor.Constant.WHITE:
case "w":
return Pair.of(localizer.getMessage("lblWhite"), MagicColor.Color.WHITE.getSymbol());
case MagicColor.Constant.BLUE:
case "u":
return Pair.of(localizer.getMessage("lblBlue"), MagicColor.Color.BLUE.getSymbol());
case MagicColor.Constant.BLACK:
case "b":
return Pair.of(localizer.getMessage("lblBlack"), MagicColor.Color.BLACK.getSymbol());
case MagicColor.Constant.RED:
case "r":
return Pair.of(localizer.getMessage("lblRed"), MagicColor.Color.RED.getSymbol());
case MagicColor.Constant.GREEN:
case "g":
return Pair.of(localizer.getMessage("lblGreen"), MagicColor.Color.GREEN.getSymbol());
case MagicColor.Constant.COLORLESS:
case "c":
return Pair.of(localizer.getMessage("lblColorless"), MagicColor.Color.COLORLESS.getSymbol());
default: // Multicolour
return Pair.of(localizer.getMessage("lblMulticolor"), "");
}
}
public static boolean isDeckName(final String lineAsIs) { public static boolean isDeckName(final String lineAsIs) {
if (lineAsIs == null) if (lineAsIs == null)
return false; return false;

View File

@@ -68,7 +68,7 @@ public class AddBasicLandsDialog {
private static final int LAND_PANEL_PADDING = 3; private static final int LAND_PANEL_PADDING = 3;
private final FComboBoxPanel<CardEdition> cbLandSet = new FComboBoxPanel<>(Localizer.getInstance().getMessage("lblLandSet") + ":", FlowLayout.CENTER, private final FComboBoxPanel<CardEdition> cbLandSet = new FComboBoxPanel<>(Localizer.getInstance().getMessage("lblLandSet") + ":", FlowLayout.CENTER,
IterableUtil.filter(StaticData.instance().getSortedEditions(), CardEdition.Predicates.hasBasicLands)); IterableUtil.filter(StaticData.instance().getSortedEditions(), CardEdition::hasBasicLands));
private final MainPanel panel = new MainPanel(); private final MainPanel panel = new MainPanel();
private final LandPanel pnlPlains = new LandPanel("Plains"); private final LandPanel pnlPlains = new LandPanel("Plains");

View File

@@ -46,7 +46,6 @@ import forge.toolbox.*;
import forge.util.Localizer; import forge.util.Localizer;
import forge.view.FDialog; import forge.view.FDialog;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import org.apache.commons.lang3.StringUtils;
import static forge.deck.DeckRecognizer.TokenType.*; import static forge.deck.DeckRecognizer.TokenType.*;
@@ -523,7 +522,7 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
else else
deck.setName(currentDeckName); deck.setName(currentDeckName);
} }
host.getDeckController().loadDeck(deck, controller.getCreateNewDeck()); host.getDeckController().loadDeck(deck, controller.getImportBehavior() != DeckImportController.ImportBehavior.MERGE);
processWindowEvent(new WindowEvent(DeckImport.this, WindowEvent.WINDOW_CLOSING)); processWindowEvent(new WindowEvent(DeckImport.this, WindowEvent.WINDOW_CLOSING));
}); });
@@ -531,7 +530,7 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
this.createNewDeckCheckbox.setSelected(false); this.createNewDeckCheckbox.setSelected(false);
this.createNewDeckCheckbox.addActionListener(e -> { this.createNewDeckCheckbox.addActionListener(e -> {
boolean createNewDeck = createNewDeckCheckbox.isSelected(); boolean createNewDeck = createNewDeckCheckbox.isSelected();
controller.setCreateNewDeck(createNewDeck); controller.setImportBehavior(createNewDeck ? DeckImportController.ImportBehavior.CREATE_NEW : DeckImportController.ImportBehavior.MERGE);
String cmdAcceptLabel = createNewDeck ? CREATE_NEW_DECK_CMD_LABEL : IMPORT_CARDS_CMD_LABEL; String cmdAcceptLabel = createNewDeck ? CREATE_NEW_DECK_CMD_LABEL : IMPORT_CARDS_CMD_LABEL;
cmdAcceptButton.setText(cmdAcceptLabel); cmdAcceptButton.setText(cmdAcceptLabel);
String smartCardArtChboxTooltip = createNewDeck ? SMART_CARDART_TT_NO_DECK : SMART_CARDART_TT_WITH_DECK; String smartCardArtChboxTooltip = createNewDeck ? SMART_CARDART_TT_NO_DECK : SMART_CARDART_TT_WITH_DECK;
@@ -600,7 +599,7 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
if (token.getType() == LIMITED_CARD) if (token.getType() == LIMITED_CARD)
cssClass = WARN_MSG_CLASS; cssClass = WARN_MSG_CLASS;
String statusMsg = String.format("<span class=\"%s\" style=\"font-size: 9px;\">%s</span>", cssClass, String statusMsg = String.format("<span class=\"%s\" style=\"font-size: 9px;\">%s</span>", cssClass,
getTokenStatusMessage(token)); controller.getTokenStatusMessage(token));
statusLbl.append(statusMsg); statusLbl.append(statusMsg);
} }
@@ -740,12 +739,12 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
private String toHTML(final DeckRecognizer.Token token) { private String toHTML(final DeckRecognizer.Token token) {
if (token == null) if (token == null)
return ""; return "";
String tokenMsg = getTokenMessage(token); String tokenMsg = controller.getTokenMessage(token);
if (tokenMsg == null) if (tokenMsg == null)
return ""; return "";
String tokenStatus = getTokenStatusMessage(token); String tokenStatus = controller.getTokenStatusMessage(token);
String cssClass = getTokenCSSClass(token.getType()); String cssClass = getTokenCSSClass(token.getType());
if (tokenStatus.length() == 0) if (tokenStatus.isEmpty())
tokenMsg = padEndWithHTMLSpaces(tokenMsg, 2*PADDING_TOKEN_MSG_LENGTH+10); tokenMsg = padEndWithHTMLSpaces(tokenMsg, 2*PADDING_TOKEN_MSG_LENGTH+10);
else { else {
tokenMsg = padEndWithHTMLSpaces(tokenMsg, PADDING_TOKEN_MSG_LENGTH); tokenMsg = padEndWithHTMLSpaces(tokenMsg, PADDING_TOKEN_MSG_LENGTH);
@@ -755,11 +754,6 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
tokenMsg = String.format("<a class=\"%s\" href=\"%s\">%s</a>", cssClass, tokenMsg = String.format("<a class=\"%s\" href=\"%s\">%s</a>", cssClass,
token.getKey().toString(), tokenMsg); token.getKey().toString(), tokenMsg);
if (tokenStatus == null) {
String tokenTag = String.format("<td colspan=\"2\" class=\"%s\">%s</td>", cssClass, tokenMsg);
return String.format("<tr>%s</tr>", tokenTag);
}
String tokenTag = "<td class=\"%s\">%s</td>"; String tokenTag = "<td class=\"%s\">%s</td>";
String tokenMsgTag = String.format(tokenTag, cssClass, tokenMsg); String tokenMsgTag = String.format(tokenTag, cssClass, tokenMsg);
String tokenStatusTag; String tokenStatusTag;
@@ -776,97 +770,6 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
return String.format("%s%s", targetMsg, spacer); return String.format("%s%s", targetMsg, spacer);
} }
private String getTokenMessage(DeckRecognizer.Token token) {
switch (token.getType()) {
case LEGAL_CARD:
case LIMITED_CARD:
case CARD_FROM_NOT_ALLOWED_SET:
case CARD_FROM_INVALID_SET:
return String.format("%s x %s %s", token.getQuantity(), token.getText(), getTokenFoilLabel(token));
// Card Warning Msgs
case UNKNOWN_CARD:
case UNSUPPORTED_CARD:
return token.getQuantity() > 0 ? String.format("%s x %s", token.getQuantity(), token.getText())
: token.getText();
case UNSUPPORTED_DECK_SECTION:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblWarningMsgPrefix"),
Localizer.getInstance()
.getMessage("lblWarnDeckSectionNotAllowedInEditor", token.getText(),
this.currentGameType));
// Special Case of Card moved into another section (e.g. Commander from Sideboard)
case WARNING_MESSAGE:
return String.format("%s: %s", Localizer.getInstance()
.getMessage("lblWarningMsgPrefix"), token.getText());
// Placeholders
case DECK_SECTION_NAME:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblDeckSection"),
token.getText());
case CARD_RARITY:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblRarity"),
token.getText());
case CARD_TYPE:
case CARD_CMC:
case MANA_COLOUR:
case COMMENT:
return token.getText();
case DECK_NAME:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblDeckName"),
token.getText());
case UNKNOWN_TEXT:
default:
return null;
}
}
private String getTokenStatusMessage(DeckRecognizer.Token token){
if (token == null)
return "";
switch (token.getType()) {
case LIMITED_CARD:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblWarningMsgPrefix"),
Localizer.getInstance().getMessage("lblWarnLimitedCard",
StringUtils.capitalize(token.getLimitedCardType().name()), getGameFormatLabel()));
case CARD_FROM_NOT_ALLOWED_SET:
return Localizer.getInstance().getMessage("lblErrNotAllowedCard", getGameFormatLabel());
case CARD_FROM_INVALID_SET:
return Localizer.getInstance().getMessage("lblErrCardEditionDate");
case UNSUPPORTED_CARD:
return Localizer.getInstance().getMessage("lblErrUnsupportedCard", this.currentGameType);
case UNKNOWN_CARD:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblWarningMsgPrefix"),
Localizer.getInstance().getMessage("lblWarnUnknownCardMsg"));
case UNSUPPORTED_DECK_SECTION:
case WARNING_MESSAGE:
case COMMENT:
case CARD_CMC:
case MANA_COLOUR:
case CARD_TYPE:
case DECK_SECTION_NAME:
case CARD_RARITY:
case DECK_NAME:
case LEGAL_CARD:
case UNKNOWN_TEXT:
default:
return "";
}
}
private String getTokenCSSClass(DeckRecognizer.TokenType tokenType){ private String getTokenCSSClass(DeckRecognizer.TokenType tokenType){
switch (tokenType){ switch (tokenType){
case LEGAL_CARD: case LEGAL_CARD:
@@ -899,17 +802,6 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
return ""; return "";
} }
} }
private String getTokenFoilLabel(DeckRecognizer.Token token) {
if (!token.isCardToken())
return "";
final String foilMarker = "- (Foil)";
return token.getCard().isFoil() ? foilMarker : "";
}
private String getGameFormatLabel() {
return String.format("\"%s\"", this.controller.getCurrentGameFormatName());
}
} }
class GameFormatDropdownRenderer extends JLabel implements ListCellRenderer<GameFormat> { class GameFormatDropdownRenderer extends JLabel implements ListCellRenderer<GameFormat> {

View File

@@ -27,8 +27,6 @@ import forge.item.PaperCard;
import forge.itemmanager.*; import forge.itemmanager.*;
import forge.itemmanager.filters.CardColorFilter; import forge.itemmanager.filters.CardColorFilter;
import forge.itemmanager.filters.CardTypeFilter; import forge.itemmanager.filters.CardTypeFilter;
import forge.localinstance.properties.ForgePreferences;
import forge.menu.FCheckBoxMenuItem;
import forge.menu.FDropDownMenu; import forge.menu.FDropDownMenu;
import forge.menu.FMenuItem; import forge.menu.FMenuItem;
import forge.menu.FPopupMenu; import forge.menu.FPopupMenu;
@@ -41,6 +39,7 @@ import forge.util.Utils;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
public class AdventureDeckEditor extends FDeckEditor { public class AdventureDeckEditor extends FDeckEditor {
protected static class AdventureEditorConfig extends DeckEditorConfig { protected static class AdventureEditorConfig extends DeckEditorConfig {
@@ -146,7 +145,8 @@ public class AdventureDeckEditor extends FDeckEditor {
if(event.cardBlock != null) { if(event.cardBlock != null) {
if(event.cardBlock.getLandSet() != null) if(event.cardBlock.getLandSet() != null)
return List.of(event.cardBlock.getLandSet()); return List.of(event.cardBlock.getLandSet());
List<CardEdition> eventSets = event.cardBlock.getSets(); List<CardEdition> eventSets = new ArrayList<>(event.cardBlock.getSets());
eventSets.removeIf(Predicate.not(CardEdition::hasBasicLands));
if(!eventSets.isEmpty()) if(!eventSets.isEmpty())
return eventSets; return eventSets;
} }
@@ -558,7 +558,7 @@ public class AdventureDeckEditor extends FDeckEditor {
currentEvent.participants[i].setDeck(opponentDecks[i]); currentEvent.participants[i].setDeck(opponentDecks[i]);
} }
currentEvent.draftedDeck = (Deck) currentEvent.registeredDeck.copyTo("Draft Deck"); currentEvent.draftedDeck = (Deck) currentEvent.registeredDeck.copyTo("Draft Deck");
if (allowsAddBasic()) { if (allowAddBasic()) {
showAddBasicLandsDialog(); showAddBasicLandsDialog();
//Might be annoying if you haven't pruned your deck yet, but best to remind player that //Might be annoying if you haven't pruned your deck yet, but best to remind player that
//this probably needs to be done since it's there since it's not normally part of Adventure //this probably needs to be done since it's there since it's not normally part of Adventure
@@ -713,27 +713,6 @@ public class AdventureDeckEditor extends FDeckEditor {
return this.deckHeader; return this.deckHeader;
} }
@Override
protected FPopupMenu createMoreOptionsMenu() {
return new FPopupMenu() {
@Override
protected void buildMenu() {
Localizer localizer = Forge.getLocalizer();
addItem(new FMenuItem(localizer.getMessage("btnCopyToClipboard"), Forge.hdbuttons ? FSkinImage.HDEXPORT : FSkinImage.BLANK, e1 -> FDeckViewer.copyDeckToClipboard(getDeck())));
if (allowsAddBasic()) {
FMenuItem addBasic = new FMenuItem(localizer.getMessage("lblAddBasicLands"), FSkinImage.LANDLOGO, e1 -> showAddBasicLandsDialog());
addItem(addBasic);
}
if(FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.DEV_MODE_ENABLED)) {
addItem(new FCheckBoxMenuItem(localizer.getMessage("cbEnforceDeckLegality"), shouldEnforceConformity(), e -> toggleConformity()));
String devSuffix = " (" + localizer.getMessage("lblDev") + ")";
addItem(new FMenuItem(localizer.getMessage("lblAddcard") + devSuffix, FSkinImage.HDPLUS, e -> showDevAddCardDialog()));
}
((DeckEditorPage) getSelectedPage()).buildDeckMenu(this);
}
};
}
@Override @Override
protected void addChosenBasicLands(CardPool landsToAdd) { protected void addChosenBasicLands(CardPool landsToAdd) {
if(isLimitedEditor()) if(isLimitedEditor())
@@ -765,6 +744,12 @@ public class AdventureDeckEditor extends FDeckEditor {
catalog.moveCards(landsToMove, getMainDeckPage()); catalog.moveCards(landsToMove, getMainDeckPage());
} }
@Override
protected PaperCard supplyPrintForImporter(PaperCard missingCard) {
PaperCard out = super.supplyPrintForImporter(missingCard);
return out == null ? null : out.getNoSellVersion();
}
@Override @Override
protected void cacheTabPages() { protected void cacheTabPages() {
super.cacheTabPages(); super.cacheTabPages();
@@ -775,7 +760,9 @@ public class AdventureDeckEditor extends FDeckEditor {
} }
@Override @Override
protected boolean allowsAddBasic() { protected boolean allowAddBasic() {
if(getEditorConfig() instanceof DeckPreviewConfig)
return false;
AdventureEventData currentEvent = getCurrentEvent(); AdventureEventData currentEvent = getCurrentEvent();
if (currentEvent == null) if (currentEvent == null)
return true; return true;

View File

@@ -59,7 +59,7 @@ public class AddBasicLandsDialog extends FDialog {
private final Consumer<CardPool> callback; private final Consumer<CardPool> callback;
private final FLabel lblLandSet = add(new FLabel.Builder().text(Forge.getLocalizer().getMessage("lblLandSet") + ":").font(FSkinFont.get(12)).textColor(FLabel.getInlineLabelColor()).build()); private final FLabel lblLandSet = add(new FLabel.Builder().text(Forge.getLocalizer().getMessage("lblLandSet") + ":").font(FSkinFont.get(12)).textColor(FLabel.getInlineLabelColor()).build());
private final FComboBox<CardEdition> cbLandSet = add(new FComboBox<>(IterableUtil.filter(StaticData.instance().getEditions(), CardEdition.Predicates.hasBasicLands))); private final FComboBox<CardEdition> cbLandSet = add(new FComboBox<>(IterableUtil.filter(StaticData.instance().getSortedEditions(), CardEdition::hasBasicLands)));
private final FScrollPane scroller = add(new FScrollPane() { private final FScrollPane scroller = add(new FScrollPane() {
@Override @Override

View File

@@ -81,13 +81,21 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
public boolean allowsCardReplacement() { return hasInfiniteCardPool() || usePlayerInventory(); } public boolean allowsCardReplacement() { return hasInfiniteCardPool() || usePlayerInventory(); }
public List<CardEdition> getBasicLandSets(Deck currentDeck) { public List<CardEdition> getBasicLandSets(Deck currentDeck) {
if(hasInfiniteCardPool())
return FModel.getMagicDb().getSortedEditions().stream().filter(CardEdition::hasBasicLands).collect(Collectors.toList());
return List.of(DeckProxy.getDefaultLandSet(currentDeck)); return List.of(DeckProxy.getDefaultLandSet(currentDeck));
} }
protected abstract IDeckController getController(); protected abstract IDeckController getController();
protected abstract DeckEditorPage[] getInitialPages(); protected abstract DeckEditorPage[] getInitialPages();
protected DeckSection[] getExtraSections() { public DeckSection[] getPrimarySections() {
if(getGameType() != null)
return getGameType().getPrimaryDeckSections().toArray(new DeckSection[0]);
return new DeckSection[]{DeckSection.Main, DeckSection.Sideboard};
}
public DeckSection[] getExtraSections() {
if(getGameType() != null) if(getGameType() != null)
return getGameType().getSupplimentalDeckSections().toArray(new DeckSection[0]); return getGameType().getSupplimentalDeckSections().toArray(new DeckSection[0]);
return new DeckSection[]{DeckSection.Attractions, DeckSection.Contraptions}; return new DeckSection[]{DeckSection.Attractions, DeckSection.Contraptions};
@@ -144,7 +152,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
ItemManagerConfig catalogConfig = null; ItemManagerConfig catalogConfig = null;
ItemManagerConfig mainSectionConfig = null; ItemManagerConfig mainSectionConfig = null;
ItemManagerConfig sideboardConfig = null; ItemManagerConfig sideboardConfig = null;
Function<Deck, CardEdition> fnGetBasicLandSet = null; Function<Deck, Collection<CardEdition>> fnGetBasicLandSet = null;
Supplier<ItemPool<PaperCard>> itemPoolSupplier = null; Supplier<ItemPool<PaperCard>> itemPoolSupplier = null;
String catalogCaption = null; String catalogCaption = null;
@@ -196,7 +204,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
this.sideboardConfig = sideboardConfig; this.sideboardConfig = sideboardConfig;
return this; return this;
} }
public GameTypeDeckEditorConfig setBasicLandSetFunction(Function<Deck, CardEdition> fnGetBasicLandSet) { public GameTypeDeckEditorConfig setBasicLandSetFunction(Function<Deck, Collection<CardEdition>> fnGetBasicLandSet) {
this.fnGetBasicLandSet = fnGetBasicLandSet; this.fnGetBasicLandSet = fnGetBasicLandSet;
return this; return this;
} }
@@ -296,9 +304,21 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
} }
@Override @Override
protected DeckSection[] getExtraSections() { public DeckSection[] getPrimarySections() {
return gameType.getPrimaryDeckSections().toArray(new DeckSection[0]);
}
@Override
public DeckSection[] getExtraSections() {
return gameType.getSupplimentalDeckSections().toArray(new DeckSection[0]); return gameType.getSupplimentalDeckSections().toArray(new DeckSection[0]);
} }
@Override
public List<CardEdition> getBasicLandSets(Deck currentDeck) {
if(this.fnGetBasicLandSet != null)
return List.copyOf(fnGetBasicLandSet.apply(currentDeck));
return super.getBasicLandSets(currentDeck);
}
} }
public static DeckEditorConfig EditorConfigConstructed = new GameTypeDeckEditorConfig(GameType.Constructed, public static DeckEditorConfig EditorConfigConstructed = new GameTypeDeckEditorConfig(GameType.Constructed,
@@ -348,18 +368,19 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
.setMainSectionConfig(ItemManagerConfig.QUEST_DECK_EDITOR) .setMainSectionConfig(ItemManagerConfig.QUEST_DECK_EDITOR)
.setSideboardConfig(ItemManagerConfig.QUEST_DECK_EDITOR) .setSideboardConfig(ItemManagerConfig.QUEST_DECK_EDITOR)
.setPlayerInventorySupplier(() -> FModel.getQuest().getCards().getCardpool()) .setPlayerInventorySupplier(() -> FModel.getQuest().getCards().getCardpool())
.setBasicLandSetFunction(d -> FModel.getQuest().getDefaultLandSet()); .setBasicLandSetFunction(d -> FModel.getQuest().getAvailableLandSets());
public static DeckEditorConfig EditorConfigQuestCommander = new GameTypeDeckEditorConfig(GameType.QuestCommander, DECK_CONTROLLER_QUEST) public static DeckEditorConfig EditorConfigQuestCommander = new GameTypeDeckEditorConfig(GameType.QuestCommander, DECK_CONTROLLER_QUEST)
.setCatalogConfig(ItemManagerConfig.QUEST_EDITOR_POOL) .setCatalogConfig(ItemManagerConfig.QUEST_EDITOR_POOL)
.setMainSectionConfig(ItemManagerConfig.QUEST_DECK_EDITOR) .setMainSectionConfig(ItemManagerConfig.QUEST_DECK_EDITOR)
.setSideboardConfig(ItemManagerConfig.QUEST_DECK_EDITOR) .setSideboardConfig(ItemManagerConfig.QUEST_DECK_EDITOR)
.setPlayerInventorySupplier(() -> FModel.getQuest().getCards().getCardpool()) .setPlayerInventorySupplier(() -> FModel.getQuest().getCards().getCardpool())
.setBasicLandSetFunction(d -> FModel.getQuest().getDefaultLandSet()); .setBasicLandSetFunction(d -> FModel.getQuest().getAvailableLandSets());
public static DeckEditorConfig EditorConfigQuestDraft = new GameTypeDeckEditorConfig(GameType.QuestDraft, DECK_CONTROLLER_QUEST_DRAFT); public static DeckEditorConfig EditorConfigQuestDraft = new GameTypeDeckEditorConfig(GameType.QuestDraft, DECK_CONTROLLER_QUEST_DRAFT);
public static DeckEditorConfig EditorConfigPlanarConquest = new GameTypeDeckEditorConfig(GameType.PlanarConquest, DECK_CONTROLLER_PLANAR_CONQUEST) public static DeckEditorConfig EditorConfigPlanarConquest = new GameTypeDeckEditorConfig(GameType.PlanarConquest, DECK_CONTROLLER_PLANAR_CONQUEST)
.setCatalogConfig(ItemManagerConfig.CONQUEST_COLLECTION) .setCatalogConfig(ItemManagerConfig.CONQUEST_COLLECTION)
.setMainSectionConfig(ItemManagerConfig.CONQUEST_DECK_EDITOR) .setMainSectionConfig(ItemManagerConfig.CONQUEST_DECK_EDITOR)
.setPlayerInventorySupplier(ConquestUtil::getAvailablePool); .setPlayerInventorySupplier(ConquestUtil::getAvailablePool)
.setBasicLandSetFunction(ConquestUtil::getBasicLandSets);
protected static DeckSectionPage createPageForExtraSection(DeckSection deckSection, DeckEditorConfig editorConfig) { protected static DeckSectionPage createPageForExtraSection(DeckSection deckSection, DeckEditorConfig editorConfig) {
CardManager cm = new CardManager(false); CardManager cm = new CardManager(false);
@@ -542,7 +563,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
@Override @Override
protected void buildMenu() { protected void buildMenu() {
final Localizer localizer = Forge.getLocalizer(); final Localizer localizer = Forge.getLocalizer();
if (allowsAddBasic()) if (allowAddBasic())
addItem(new FMenuItem(localizer.getMessage("lblAddBasicLands"), FSkinImage.LANDLOGO, e -> showAddBasicLandsDialog())); addItem(new FMenuItem(localizer.getMessage("lblAddBasicLands"), FSkinImage.LANDLOGO, e -> showAddBasicLandsDialog()));
if (showAddExtraSectionOption()) { if (showAddExtraSectionOption()) {
addItem(new FMenuItem(localizer.getMessage("lblAddDeckSection"), FSkinImage.CHAOS, e -> { addItem(new FMenuItem(localizer.getMessage("lblAddDeckSection"), FSkinImage.CHAOS, e -> {
@@ -558,21 +579,33 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
}); });
})); }));
} }
if (editorConfig.getGameType() != null && editorConfig.hasInfiniteCardPool()) { if (editorConfig.hasInfiniteCardPool() || editorConfig.usePlayerInventory()) {
addItem(new FMenuItem(localizer.getMessage("lblImportFromClipboard"), Forge.hdbuttons ? FSkinImage.HDIMPORT : FSkinImage.OPEN, e -> { addItem(new FMenuItem(localizer.getMessage("lblImportFromClipboard"), Forge.hdbuttons ? FSkinImage.HDIMPORT : FSkinImage.OPEN, e -> {
FDeckImportDialog dialog = new FDeckImportDialog(!deck.isEmpty(), FDeckEditor.this.editorConfig); FDeckImportDialog dialog = new FDeckImportDialog(deck, FDeckEditor.this.editorConfig);
if(editorConfig.usePlayerInventory())
dialog.setFreePrintConverter(FDeckEditor.this::supplyPrintForImporter);
dialog.setImportBannedCards(!FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY));
dialog.setCallback(importedDeck -> { dialog.setCallback(importedDeck -> {
if (deck != null && importedDeck.hasName()) { if (deck != null && importedDeck.hasName()) {
deck.setName(importedDeck.getName()); deck.setName(importedDeck.getName());
setHeaderText(importedDeck.getName()); setHeaderText(importedDeck.getName());
} }
if (dialog.createNewDeck()) { switch (dialog.getImportBehavior()) {
for (Entry<DeckSection, CardPool> section : importedDeck) { case REPLACE_CURRENT:
DeckSectionPage page = getPageForSection(section.getKey()); for(DeckSectionPage page : pagesBySection.values()) {
if (page != null) if(importedDeck.has(page.deckSection)) {
page.setCards(section.getValue()); page.setCards(importedDeck.get(page.deckSection));
if(hiddenExtraSections.contains(page.deckSection))
showExtraSectionTab(page.deckSection);
} }
} else { else
page.setCards(new CardPool());
}
break;
case CREATE_NEW:
deckController.setDeck(importedDeck);
break;
case MERGE:
for (Entry<DeckSection, CardPool> section : importedDeck) { for (Entry<DeckSection, CardPool> section : importedDeck) {
DeckSectionPage page = getPageForSection(section.getKey()); DeckSectionPage page = getPageForSection(section.getKey());
if (page != null) if (page != null)
@@ -580,6 +613,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
} }
} }
}); });
dialog.initParse();
dialog.show(); dialog.show();
setSelectedPage(getMainDeckPage()); //select main deck page if needed so main deck if visible below dialog setSelectedPage(getMainDeckPage()); //select main deck page if needed so main deck if visible below dialog
})); }));
@@ -643,6 +677,20 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
getMainDeckPage().addCards(landsToAdd); getMainDeckPage().addCards(landsToAdd);
} }
/**
* If a card is missing from a player's inventory while importing a deck, it gets run through here.
* Returning a PaperCard will let unlimited copies of that card be used as a substitute. Returning null
* will leave the card missing from the import.
*/
protected PaperCard supplyPrintForImporter(PaperCard missingCard) {
//Could support dungeons here too? Not that we really use them in the editor...
if(!missingCard.isVeryBasicLand())
return null;
List<CardEdition> basicSets = editorConfig.getBasicLandSets(deck);
String setCode = basicSets.isEmpty() ? "JMP" : basicSets.get(0).getCode();
return FModel.getMagicDb().fetchCard(missingCard.getCardName(), setCode, null);
}
protected boolean shouldEnforceConformity() { protected boolean shouldEnforceConformity() {
if(FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) if(FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY))
return true; return true;
@@ -695,6 +743,9 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
showExtraSectionTab(section); showExtraSectionTab(section);
if(pagesBySection.containsKey(section)) if(pagesBySection.containsKey(section))
setSelectedPage(pagesBySection.get(section)); setSelectedPage(pagesBySection.get(section));
else if(section == DeckSection.Main && pagesBySection.containsKey(mainDeckPage.deckSection))
//Tried to switch to the Main page in a Planar or Scheme deck.
setSelectedPage(pagesBySection.get(mainDeckPage.deckSection));
} }
public void notifyNewControllerModel() { public void notifyNewControllerModel() {
@@ -1027,7 +1078,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
protected boolean allowSaveAs() { protected boolean allowSaveAs() {
return allowSave() && allowRename(); return allowSave() && allowRename();
} }
protected boolean allowsAddBasic() { protected boolean allowAddBasic() {
return !isDrafting(); return !isDrafting();
} }

View File

@@ -18,11 +18,12 @@
package forge.deck; package forge.deck;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import forge.Forge; import forge.Forge;
import forge.Graphics; import forge.Graphics;
@@ -31,11 +32,15 @@ import forge.deck.DeckRecognizer.TokenType;
import forge.game.GameType; import forge.game.GameType;
import forge.gui.FThreads; import forge.gui.FThreads;
import forge.gui.util.SOptionPane; import forge.gui.util.SOptionPane;
import forge.item.PaperCard;
import forge.toolbox.FCheckBox; import forge.toolbox.FCheckBox;
import forge.toolbox.FComboBox; import forge.toolbox.FComboBox;
import forge.toolbox.FDialog; import forge.toolbox.FDialog;
import forge.toolbox.FOptionPane; import forge.toolbox.FOptionPane;
import forge.toolbox.FTextArea; import forge.toolbox.FTextArea;
import forge.util.ItemPool;
import forge.util.Localizer;
import org.apache.commons.lang3.StringUtils;
public class FDeckImportDialog extends FDialog { public class FDeckImportDialog extends FDialog {
@@ -45,7 +50,7 @@ public class FDeckImportDialog extends FDialog {
private final FCheckBox newEditionCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblImportLatestVersionCard"), false)); private final FCheckBox newEditionCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblImportLatestVersionCard"), false));
private final FCheckBox dateTimeCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblUseOnlySetsReleasedBefore"), false)); private final FCheckBox dateTimeCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblUseOnlySetsReleasedBefore"), false));
private final FCheckBox smartCardArtCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblUseSmartCardArt"), false)); private final FCheckBox smartCardArtCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblUseSmartCardArt"), false));
private final FCheckBox createNewDeckCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblNewDeckCheckbox"), false)); private final FCheckBox createNewDeckCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblReplaceDeckCheckbox"), false));
// private final FCheckBox importInDeck = add(new FCheckBox() // private final FCheckBox importInDeck = add(new FCheckBox()
/*setting onlyCoreExpCheck to false allow the copied cards to pass the check of deck contents /*setting onlyCoreExpCheck to false allow the copied cards to pass the check of deck contents
forge-core\src\main\java\forge\deck\Deck.javaDeck.java starting @ Line 320 which is called by forge-core\src\main\java\forge\deck\Deck.javaDeck.java starting @ Line 320 which is called by
@@ -57,100 +62,60 @@ public class FDeckImportDialog extends FDialog {
private final FComboBox<String> monthDropdown = add(new FComboBox<>()); //don't need wrappers since skin can't change while this dialog is open private final FComboBox<String> monthDropdown = add(new FComboBox<>()); //don't need wrappers since skin can't change while this dialog is open
private final FComboBox<Integer> yearDropdown = add(new FComboBox<>()); private final FComboBox<Integer> yearDropdown = add(new FComboBox<>());
private final boolean showOptions; private boolean showOptions;
private final boolean currentDeckIsEmpty; private final Deck currentDeck;
private boolean createNewDeckControl; private boolean createNewDeckControl;
private final DeckImportController controller; private final DeckImportController controller;
private final FDeckEditor.DeckEditorConfig editorConfig;
private final static ImmutableList<String> importOrCancel = ImmutableList.of(Forge.getLocalizer().getMessage("lblImport"), Forge.getLocalizer().getMessage("lblCancel")); private final static ImmutableList<String> importOrCancel = ImmutableList.of(Forge.getLocalizer().getMessage("lblImport"), Forge.getLocalizer().getMessage("lblCancel"));
public FDeckImportDialog(final boolean replacingDeck, final FDeckEditor.DeckEditorConfig editorConfig) { public FDeckImportDialog(final Deck currentDeck, final FDeckEditor.DeckEditorConfig editorConfig) {
super(Forge.getLocalizer().getMessage("lblImportFromClipboard"), 2); super(Forge.getLocalizer().getMessage("lblImportFromClipboard"), 2);
boolean usingInventory = editorConfig.usePlayerInventory();
boolean replacingDeck = !currentDeck.isEmpty() || usingInventory;
this.currentDeck = currentDeck;
this.editorConfig = editorConfig;
ItemPool<PaperCard> cardPool = editorConfig.getCardPool(false);
controller = new DeckImportController(dateTimeCheck, monthDropdown, yearDropdown, replacingDeck); controller = new DeckImportController(dateTimeCheck, monthDropdown, yearDropdown, replacingDeck);
String contents = Forge.getClipboard().getContents(); String contents = Forge.getClipboard().getContents();
if (contents == null) if (contents == null)
contents = ""; //prevent NPE contents = ""; //prevent NPE
txtInput.setText(contents); txtInput.setText(contents);
if (editorConfig.allowsCardReplacement()) {
GameType gameType = editorConfig.getGameType(); GameType gameType = editorConfig.getGameType();
controller.setGameFormat(gameType); controller.setGameFormat(gameType);
List<DeckSection> supportedSections = new ArrayList<>(); List<DeckSection> supportedSections = new ArrayList<>();
supportedSections.add(DeckSection.Main); supportedSections.addAll(List.of(editorConfig.getPrimarySections()));
supportedSections.add(DeckSection.Sideboard); supportedSections.addAll(List.of(editorConfig.getExtraSections()));
if (editorConfig.hasCommander())
supportedSections.add(DeckSection.Commander);
supportedSections.addAll(Lists.newArrayList(editorConfig.getExtraSections()));
controller.setAllowedSections(supportedSections); controller.setAllowedSections(supportedSections);
} controller.setCurrentDeckInEditor(currentDeck);
if(usingInventory)
controller.setPlayerInventory(cardPool);
onlyCoreExpCheck.setSelected(StaticData.instance().isCoreExpansionOnlyFilterSet()); onlyCoreExpCheck.setSelected(StaticData.instance().isCoreExpansionOnlyFilterSet());
newEditionCheck.setSelected(StaticData.instance().cardArtPreferenceIsLatest()); newEditionCheck.setSelected(StaticData.instance().cardArtPreferenceIsLatest());
smartCardArtCheck.setSelected(StaticData.instance().isEnabledCardArtSmartSelection()); smartCardArtCheck.setSelected(StaticData.instance().isEnabledCardArtSmartSelection());
createNewDeckCheck.setSelected(replacingDeck); createNewDeckCheck.setSelected(replacingDeck);
this.currentDeckIsEmpty = !replacingDeck;
this.createNewDeckControl = replacingDeck; this.createNewDeckControl = replacingDeck;
initButton(0, Forge.getLocalizer().getMessage("lblImport"), e -> FThreads.invokeInBackgroundThread(() -> { if(usingInventory)
List<DeckRecognizer.Token> tokens = controller.parseInput(txtInput.getText()); //ensure deck updated based on any changes to options controller.setImportBehavior(DeckImportController.ImportBehavior.REPLACE_CURRENT);
else
controller.setImportBehavior(createNewDeckControl ? DeckImportController.ImportBehavior.CREATE_NEW : DeckImportController.ImportBehavior.MERGE);
if (controller.isSmartCardArtEnabled()) initButton(0, Forge.getLocalizer().getMessage("lblImport"), e -> FThreads.invokeInBackgroundThread(this::performImport));
tokens = controller.optimiseCardArtInTokens();
//if there are any cards that cannot be imported, let user know this and give them the option to cancel
StringBuilder sb = new StringBuilder();
for (DeckRecognizer.Token token : tokens) {
if (token.getType() == TokenType.CARD_FROM_NOT_ALLOWED_SET
|| token.getType() == TokenType.CARD_FROM_INVALID_SET
|| token.getType() == TokenType.UNKNOWN_CARD
|| token.getType() == TokenType.UNSUPPORTED_CARD) {
if (sb.length() > 0)
sb.append("\n");
sb.append(token.getQuantity()).append(" ").append(token.getText());
}
}
if (sb.length() > 0) {
if (SOptionPane.showOptionDialog(Forge.getLocalizer().getMessage("lblFollowingCardsCannotBeImported") + "\n\n" + sb, Forge.getLocalizer().getMessage("lblImportRemainingCards"), SOptionPane.INFORMATION_ICON, importOrCancel) == 1) {
return;
}
}
final Deck deck = controller.accept(); //must accept in background thread in case a dialog is shown
if (deck == null) { return; }
FThreads.invokeInEdtLater(() -> {
hide();
if (callback != null)
callback.accept(deck);
});
}));
initButton(1, Forge.getLocalizer().getMessage("lblCancel"), e -> hide()); initButton(1, Forge.getLocalizer().getMessage("lblCancel"), e -> hide());
List<DeckRecognizer.Token> tokens = controller.parseInput(txtInput.getText());
if (controller.isSmartCardArtEnabled())
tokens = controller.optimiseCardArtInTokens();
//ensure at least one known card found on clipboard
for (DeckRecognizer.Token token : tokens) {
if (token.getType() == TokenType.LEGAL_CARD) {
showOptions = true;
dateTimeCheck.setCommand(e -> updateDropDownEnabled()); dateTimeCheck.setCommand(e -> updateDropDownEnabled());
newEditionCheck.setCommand(e -> setArtPreferenceInController()); newEditionCheck.setCommand(e -> setArtPreferenceInController());
onlyCoreExpCheck.setCommand(e -> setArtPreferenceInController()); onlyCoreExpCheck.setCommand(e -> setArtPreferenceInController());
smartCardArtCheck.setCommand(e -> controller.setSmartCardArtOptimisation(smartCardArtCheck.isSelected())); smartCardArtCheck.setCommand(e -> controller.setSmartCardArtOptimisation(smartCardArtCheck.isSelected()));
createNewDeckCheck.setCommand(e -> { createNewDeckCheck.setCommand(e -> {
createNewDeckControl = createNewDeckCheck.isSelected(); createNewDeckControl = createNewDeckCheck.isSelected();
controller.setCreateNewDeck(createNewDeckControl); controller.setImportBehavior(createNewDeckControl ? DeckImportController.ImportBehavior.CREATE_NEW : DeckImportController.ImportBehavior.MERGE);
}); });
updateDropDownEnabled(); setShowOptions(false);
setArtPreferenceInController();
return;
}
}
showOptions = false;
setButtonEnabled(0, false);
txtInput.setText(Forge.getLocalizer().getMessage("lblNoKnownCardsOnClipboard"));
} }
private void setArtPreferenceInController() { private void setArtPreferenceInController() {
@@ -160,16 +125,66 @@ public class FDeckImportDialog extends FDialog {
} }
private void updateDropDownEnabled() { private void updateDropDownEnabled() {
boolean enabled = dateTimeCheck.isSelected(); boolean enabled = dateTimeCheck.isSelected() && this.showOptions;
monthDropdown.setEnabled(enabled); monthDropdown.setEnabled(enabled);
yearDropdown.setEnabled(enabled); yearDropdown.setEnabled(enabled);
} }
private void setShowOptions(boolean showOptions) {
this.showOptions = showOptions;
dateTimeCheck.setEnabled(showOptions);
newEditionCheck.setEnabled(showOptions);
onlyCoreExpCheck.setEnabled(showOptions);
newEditionCheck.setEnabled(showOptions);
smartCardArtCheck.setEnabled(showOptions);
createNewDeckCheck.setEnabled(showOptions);
updateDropDownEnabled();
}
public void setCallback(Consumer<Deck> callback0){ public void setCallback(Consumer<Deck> callback0){
callback = callback0; callback = callback0;
} }
public boolean createNewDeck(){ return this.createNewDeckControl; } public void setFreePrintConverter(Function<PaperCard, PaperCard> freePrintConverter) {
this.controller.setFreePrintConverter(freePrintConverter);
}
public DeckImportController.ImportBehavior getImportBehavior() {
return controller.getImportBehavior();
}
public void setImportBannedCards(boolean importBannedCards) {
controller.importBannedAndRestrictedCards(importBannedCards);
}
public void initParse() {
boolean usingInventory = editorConfig.usePlayerInventory();
List<DeckRecognizer.Token> tokens = controller.parseInput(txtInput.getText());
if (usingInventory)
tokens = controller.constrainTokensToInventory();
else if (controller.isSmartCardArtEnabled())
tokens = controller.optimiseCardArtInTokens();
//ensure at least one known card found on clipboard
for (DeckRecognizer.Token token : tokens) {
if (token.getType() == TokenType.LEGAL_CARD || token.getType() == TokenType.FREE_CARD_NOT_IN_INVENTORY) {
if(usingInventory) {
//Settings aren't compatible with player inventories.
setShowOptions(false);
return;
}
setShowOptions(true);
updateDropDownEnabled();
setArtPreferenceInController();
return;
}
}
setButtonEnabled(0, false);
txtInput.setText(Forge.getLocalizer().getMessage("lblNoKnownCardsOnClipboard"));
}
@Override @Override
public void drawOverlay(Graphics g) { public void drawOverlay(Graphics g) {
@@ -202,7 +217,7 @@ public class FDeckImportDialog extends FDialog {
yearDropdown.setBounds(x + dropDownWidth + fieldPadding, y, dropDownWidth, h); yearDropdown.setBounds(x + dropDownWidth + fieldPadding, y, dropDownWidth, h);
y += h + fieldPadding; y += h + fieldPadding;
if (!this.currentDeckIsEmpty){ if (!this.currentDeck.isEmpty()){
smartCardArtCheck.setBounds(x, y, w/2, h); smartCardArtCheck.setBounds(x, y, w/2, h);
createNewDeckCheck.setBounds(x + w/2, y, w/2, h); createNewDeckCheck.setBounds(x + w/2, y, w/2, h);
} else } else
@@ -222,4 +237,49 @@ public class FDeckImportDialog extends FDialog {
} }
return y; return y;
} }
private static final EnumSet<TokenType> MISSING_TOKENS = EnumSet.of(TokenType.CARD_FROM_NOT_ALLOWED_SET,
TokenType.CARD_FROM_INVALID_SET, TokenType.UNKNOWN_CARD, TokenType.UNSUPPORTED_CARD,
TokenType.WARNING_MESSAGE, TokenType.CARD_NOT_IN_INVENTORY);
private void performImport() {
List<DeckRecognizer.Token> tokens = controller.parseInput(txtInput.getText()); //ensure deck updated based on any changes to options
if (editorConfig.usePlayerInventory())
tokens = controller.constrainTokensToInventory();
else if (controller.isSmartCardArtEnabled())
tokens = controller.optimiseCardArtInTokens();
//if there are any cards that cannot be imported, let user know this and give them the option to cancel
StringBuilder sb = new StringBuilder();
for (DeckRecognizer.Token token : tokens) {
if (MISSING_TOKENS.contains(token.getType())) {
if (!sb.isEmpty())
sb.append("\n");
String message = controller.getTokenMessage(token);
String statusMessage = controller.getTokenStatusMessage(token);
if(!StringUtils.isBlank(statusMessage))
sb.append(String.format("%s - (%s)", message, statusMessage));
else
sb.append(statusMessage);
}
}
if (!sb.isEmpty()) {
Localizer localizer = Forge.getLocalizer();
if (SOptionPane.showOptionDialog(localizer.getMessage("lblFollowingCardsCannotBeImported") + "\n\n" + sb, localizer.getMessage("lblImportRemainingCards"), SOptionPane.WARNING_ICON, importOrCancel) == 1) {
return;
}
}
final Deck deck = controller.accept(currentDeck.getName()); //must accept in background thread in case a dialog is shown
if (deck == null) {
return;
}
FThreads.invokeInEdtLater(() -> {
hide();
if (callback != null)
callback.accept(deck);
});
}
} }

View File

@@ -2838,6 +2838,7 @@ lblDecklistTitle=Deckliste
lblSummaryStats=Gesamt-Statistik lblSummaryStats=Gesamt-Statistik
lblDeckSection=Bereich lblDeckSection=Bereich
lblNewDeckCheckbox=Erzeuge ein neues Deck lblNewDeckCheckbox=Erzeuge ein neues Deck
lblReplaceDeckCheckbox=Replace Current Deck
lblImportCardsCmd=Importiere Karten lblImportCardsCmd=Importiere Karten
lblCreateNewCmd=Neues Deck lblCreateNewCmd=Neues Deck
lblErrNotAllowedCard=Set ist nicht erlaubt in {0} lblErrNotAllowedCard=Set ist nicht erlaubt in {0}
@@ -2845,6 +2846,7 @@ lblWarnLimitedCard={0} in {1}
lblErrCardEditionDate=Set verträgt sich nicht mit der Erscheinungsdatum-Option lblErrCardEditionDate=Set verträgt sich nicht mit der Erscheinungsdatum-Option
lblErrUnsupportedCard=Ist nicht erlaubt in {0} lblErrUnsupportedCard=Ist nicht erlaubt in {0}
lblWarnUnknownCardMsg=Unbekannte oder in Forge nicht unterstützte Karte lblWarnUnknownCardMsg=Unbekannte oder in Forge nicht unterstützte Karte
lblWarnNotInInventory=Card not found in inventory
lblWarnTooManyCommanders=Aktueller {0}-Bereich enthält {1} mögliche Commander-Karten: {2} lblWarnTooManyCommanders=Aktueller {0}-Bereich enthält {1} mögliche Commander-Karten: {2}
lblWarnCommandersInSideExtra=Bitte prüfen und, falls nötig, min. eine Karte in den Commander-Bereich verschieben. lblWarnCommandersInSideExtra=Bitte prüfen und, falls nötig, min. eine Karte in den Commander-Bereich verschieben.
lblWarnDeckSectionNotAllowedInEditor=In {1} ist der {0}-Bereich nicht erlaubt. lblWarnDeckSectionNotAllowedInEditor=In {1} ist der {0}-Bereich nicht erlaubt.
@@ -2936,6 +2938,7 @@ lblCardImportWarning=\nWarnung: Das Deck {0} wird umbenannt in {1}.
lblConfirmCreateNewDeck=Du bist dabei das neue Deck {0} zu erzeugen. {1}\n\nWillst du fortfahren?\n\n Hinweis: \ lblConfirmCreateNewDeck=Du bist dabei das neue Deck {0} zu erzeugen. {1}\n\nWillst du fortfahren?\n\n Hinweis: \
Bitte denk daran den "Save"-Knopf im Deck-Editor zu klicken, um das neue Deck dem Deck-Katalog hinzuzufügen! Bitte denk daran den "Save"-Knopf im Deck-Editor zu klicken, um das neue Deck dem Deck-Katalog hinzuzufügen!
lblNewDeckWarning=\nWarnung: Alle ungesicherten Änderungen am aktuellen Deck {0} werden verlorengehen. lblNewDeckWarning=\nWarnung: Alle ungesicherten Änderungen am aktuellen Deck {0} werden verlorengehen.
lblConfirmReplaceDeck=This will replace the contents of the current deck ({0}) with the imported cards.\n\nWould you like to proceed?
lblImportCardsDialogTitle=Importiere Karten in aktuelles Deck lblImportCardsDialogTitle=Importiere Karten in aktuelles Deck
lblNewDeckDialogTitle=Erzeuge neues Deck lblNewDeckDialogTitle=Erzeuge neues Deck
#FNetOverlay.java #FNetOverlay.java

View File

@@ -2898,6 +2898,7 @@ lblDecklistTitle=Decklist
lblSummaryStats=Summary Statistics lblSummaryStats=Summary Statistics
lblDeckSection=Section lblDeckSection=Section
lblNewDeckCheckbox=Create a New Deck lblNewDeckCheckbox=Create a New Deck
lblReplaceDeckCheckbox=Replace Current Deck
lblImportCardsCmd=Import Cards lblImportCardsCmd=Import Cards
lblCreateNewCmd=New Deck lblCreateNewCmd=New Deck
lblErrNotAllowedCard=Set not allowed in {0} lblErrNotAllowedCard=Set not allowed in {0}
@@ -2905,6 +2906,7 @@ lblWarnLimitedCard={0} in {1}
lblErrCardEditionDate=Set not compliant with Release Date option lblErrCardEditionDate=Set not compliant with Release Date option
lblErrUnsupportedCard=Not allowed in {0} lblErrUnsupportedCard=Not allowed in {0}
lblWarnUnknownCardMsg=Unknown Card or Unsupported in Forge lblWarnUnknownCardMsg=Unknown Card or Unsupported in Forge
lblWarnNotInInventory=Card not found in inventory
lblWarnTooManyCommanders=Current {0} Section contains {1} potential Commander Cards: {2} lblWarnTooManyCommanders=Current {0} Section contains {1} potential Commander Cards: {2}
lblWarnCommandersInSideExtra=Please check and move one to the Commander Section, in case. lblWarnCommandersInSideExtra=Please check and move one to the Commander Section, in case.
lblWarnDeckSectionNotAllowedInEditor={0} Section is not allowed in {1} lblWarnDeckSectionNotAllowedInEditor={0} Section is not allowed in {1}
@@ -3001,6 +3003,7 @@ lblCardImportWarning=\nWarning: The deck {0} will be renamed as {1}.
lblConfirmCreateNewDeck=You are about to create a new deck {0}. {1}\n\nWould you like to proceed?\n\n Note: \ lblConfirmCreateNewDeck=You are about to create a new deck {0}. {1}\n\nWould you like to proceed?\n\n Note: \
Please remember to click on the "Save" button in the Deck Editor to add the new deck to the Catalog! Please remember to click on the "Save" button in the Deck Editor to add the new deck to the Catalog!
lblNewDeckWarning=\nWarning: Any unsaved changes to the current deck {0} will be lost. lblNewDeckWarning=\nWarning: Any unsaved changes to the current deck {0} will be lost.
lblConfirmReplaceDeck=This will replace the contents of the current deck ({0}) with the imported cards.\n\nWould you like to proceed?
lblImportCardsDialogTitle=Import cards in the Current Deck lblImportCardsDialogTitle=Import cards in the Current Deck
lblNewDeckDialogTitle=Create a New Deck lblNewDeckDialogTitle=Create a New Deck
#FNetOverlay.java #FNetOverlay.java

View File

@@ -2847,6 +2847,7 @@ lblDecklistTitle=Decklist
lblSummaryStats=Summary Statistics lblSummaryStats=Summary Statistics
lblDeckSection=Section lblDeckSection=Section
lblNewDeckCheckbox=Create a New Deck lblNewDeckCheckbox=Create a New Deck
lblReplaceDeckCheckbox=Replace Current Deck
lblImportCardsCmd=Import Cards lblImportCardsCmd=Import Cards
lblCreateNewCmd=New Deck lblCreateNewCmd=New Deck
lblErrNotAllowedCard=Set not allowed in {0} lblErrNotAllowedCard=Set not allowed in {0}
@@ -2854,6 +2855,7 @@ lblWarnLimitedCard={0} in {1}
lblErrCardEditionDate=Set not compliant with Release Date option lblErrCardEditionDate=Set not compliant with Release Date option
lblErrUnsupportedCard=Not allowed in {0} lblErrUnsupportedCard=Not allowed in {0}
lblWarnUnknownCardMsg=Unknown Card or Unsupported in Forge lblWarnUnknownCardMsg=Unknown Card or Unsupported in Forge
lblWarnNotInInventory=Card not found in inventory
lblWarnTooManyCommanders=Current {0} Section contains {1} potential Commander Cards: {2} lblWarnTooManyCommanders=Current {0} Section contains {1} potential Commander Cards: {2}
lblWarnCommandersInSideExtra=Please check and move one to the Commander Section, in case. lblWarnCommandersInSideExtra=Please check and move one to the Commander Section, in case.
lblWarnDeckSectionNotAllowedInEditor={0} Section is not allowed in {1} lblWarnDeckSectionNotAllowedInEditor={0} Section is not allowed in {1}
@@ -2950,6 +2952,7 @@ lblCardImportWarning=\nWarning: The deck {0} will be renamed as {1}.
lblConfirmCreateNewDeck=You are about to create a new deck {0}. {1}\n\nWould you like to proceed?\n\n Note: \ lblConfirmCreateNewDeck=You are about to create a new deck {0}. {1}\n\nWould you like to proceed?\n\n Note: \
Please remember to click on the "Save" button in the Deck Editor to add the new deck to the Catalog! Please remember to click on the "Save" button in the Deck Editor to add the new deck to the Catalog!
lblNewDeckWarning=\nWarning: Any unsaved changes to the current deck {0} will be lost. lblNewDeckWarning=\nWarning: Any unsaved changes to the current deck {0} will be lost.
lblConfirmReplaceDeck=This will replace the contents of the current deck ({0}) with the imported cards.\n\nWould you like to proceed?
lblImportCardsDialogTitle=Import cards in the Current Deck lblImportCardsDialogTitle=Import cards in the Current Deck
lblNewDeckDialogTitle=Create a New Deck lblNewDeckDialogTitle=Create a New Deck
#FNetOverlay.java #FNetOverlay.java

View File

@@ -2840,6 +2840,7 @@ lblDecklistTitle=Liste de deck
lblSummaryStats=Statistiques récapitulatives lblSummaryStats=Statistiques récapitulatives
lblDeckSection=Section lblDeckSection=Section
lblNewDeckCheckbox=Créer un nouveau deck lblNewDeckCheckbox=Créer un nouveau deck
lblReplaceDeckCheckbox=Replace Current Deck
lblImportCardsCmd=Importer des cartes lblImportCardsCmd=Importer des cartes
lblCreateNewCmd=Nouveau Deck lblCreateNewCmd=Nouveau Deck
lblErrNotAllowedCard=Définir non autorisé dans {0} lblErrNotAllowedCard=Définir non autorisé dans {0}
@@ -2847,6 +2848,7 @@ lblWarnLimitedCard={0} dans {1}
lblErrCardEditionDate=Set non conforme avec l'option de date de sortie lblErrCardEditionDate=Set non conforme avec l'option de date de sortie
lblErrUnsupportedCard=Non autorisé dans {0} lblErrUnsupportedCard=Non autorisé dans {0}
lblWarnUnknownCardMsg=Carte inconnue ou non prise en charge dans Forge lblWarnUnknownCardMsg=Carte inconnue ou non prise en charge dans Forge
lblWarnNotInInventory=Card not found in inventory
lblWarnTooManyCommanders=La section {0} actuelle contient {1} cartes de commandant potentielles : {2} lblWarnTooManyCommanders=La section {0} actuelle contient {1} cartes de commandant potentielles : {2}
lblWarnCommandersInSideExtra=Veuillez vérifier et en déplacer un vers la section Commandant, au cas où. lblWarnCommandersInSideExtra=Veuillez vérifier et en déplacer un vers la section Commandant, au cas où.
lblWarnDeckSectionNotAllowedInEditor={0} La section n'est pas autorisée dans {1} lblWarnDeckSectionNotAllowedInEditor={0} La section n'est pas autorisée dans {1}
@@ -2944,6 +2946,7 @@ lblCardImportWarning=\nAttention : Le deck {0} sera renommé en {1}.
lblConfirmCreateNewDeck=Vous êtes sur le point de créer un nouveau deck {0}. {1}\n\nVoulez-vous continuer ?\n\n Remarque : \ lblConfirmCreateNewDeck=Vous êtes sur le point de créer un nouveau deck {0}. {1}\n\nVoulez-vous continuer ?\n\n Remarque : \
N'oubliez pas de cliquer sur le bouton "Enregistrer" dans l'éditeur de deck pour ajouter le nouveau deck au catalogue ! N'oubliez pas de cliquer sur le bouton "Enregistrer" dans l'éditeur de deck pour ajouter le nouveau deck au catalogue !
lblNewDeckWarning=\nAttention : Toute modification non enregistrée dans le deck actuel {0} sera perdue. lblNewDeckWarning=\nAttention : Toute modification non enregistrée dans le deck actuel {0} sera perdue.
lblConfirmReplaceDeck=This will replace the contents of the current deck ({0}) with the imported cards.\n\nWould you like to proceed?
lblImportCardsDialogTitle=Importer des cartes dans le Deck actuel lblImportCardsDialogTitle=Importer des cartes dans le Deck actuel
lblNewDeckDialogTitle=Créer un nouveau deck lblNewDeckDialogTitle=Créer un nouveau deck
#FNetOverlay.java #FNetOverlay.java

View File

@@ -2836,6 +2836,7 @@ lblDecklistTitle=Lista delle Carte da Importare
lblSummaryStats=Statistiche Generali lblSummaryStats=Statistiche Generali
lblDeckSection=Sezione lblDeckSection=Sezione
lblNewDeckCheckbox=Crea un nuovo mazzo lblNewDeckCheckbox=Crea un nuovo mazzo
lblReplaceDeckCheckbox=Replace Current Deck
lblImportCardsCmd=Importa le carte lblImportCardsCmd=Importa le carte
lblCreateNewCmd=Nuovo mazzo lblCreateNewCmd=Nuovo mazzo
lblErrNotAllowedCard=Edizione non permessa in {0} lblErrNotAllowedCard=Edizione non permessa in {0}
@@ -2843,6 +2844,7 @@ lblWarnLimitedCard={0} in {1}
lblErrCardEditionDate=Edizione non valida secondo l'opzione sulla data di pubblicazione selezionata lblErrCardEditionDate=Edizione non valida secondo l'opzione sulla data di pubblicazione selezionata
lblErrUnsupportedCard=Non Permesso in {0} lblErrUnsupportedCard=Non Permesso in {0}
lblWarnUnknownCardMsg=Carta Sconosciuta, o non supportata in Forge lblWarnUnknownCardMsg=Carta Sconosciuta, o non supportata in Forge
lblWarnNotInInventory=Card not found in inventory
lblWarnTooManyCommanders=La Sezione {0} contiene {1} potenziali carte Commander: {2} lblWarnTooManyCommanders=La Sezione {0} contiene {1} potenziali carte Commander: {2}
lblWarnCommandersInSideExtra=Per favore, controlla e nel caso spostane una nella sezione Commander. lblWarnCommandersInSideExtra=Per favore, controlla e nel caso spostane una nella sezione Commander.
lblWarnDeckSectionNotAllowedInEditor={0} Sezione non è permessa in {1} lblWarnDeckSectionNotAllowedInEditor={0} Sezione non è permessa in {1}
@@ -2942,6 +2944,7 @@ lblCardImportWarning=\nAttenzione: Il mazzo {0} sarà rinominato come {1}.
lblConfirmCreateNewDeck=Si sta per creare un nuovo mazzo {0}. {1}\n\nSi desidera procedere?\n\n Nota: \ lblConfirmCreateNewDeck=Si sta per creare un nuovo mazzo {0}. {1}\n\nSi desidera procedere?\n\n Nota: \
Non dimenticare di premere il tasto "Salva" una volta importate le carte per aggiungere il nuovo mazzo al catalogo! Non dimenticare di premere il tasto "Salva" una volta importate le carte per aggiungere il nuovo mazzo al catalogo!
lblNewDeckWarning=\nAttenzione: Qualsiasi modifica non salvata al mazzo corrente {0} sarà persa. lblNewDeckWarning=\nAttenzione: Qualsiasi modifica non salvata al mazzo corrente {0} sarà persa.
lblConfirmReplaceDeck=This will replace the contents of the current deck ({0}) with the imported cards.\n\nWould you like to proceed?
lblImportCardsDialogTitle=Importa le carte nel mazzo corrente lblImportCardsDialogTitle=Importa le carte nel mazzo corrente
lblNewDeckDialogTitle=Crea un nuovo mazzo lblNewDeckDialogTitle=Crea un nuovo mazzo
#FNetOverlay.java #FNetOverlay.java

View File

@@ -2835,6 +2835,7 @@ lblDecklistTitle=Decklist
lblSummaryStats=Summary Statistics lblSummaryStats=Summary Statistics
lblDeckSection=Section lblDeckSection=Section
lblNewDeckCheckbox=Create a New Deck lblNewDeckCheckbox=Create a New Deck
lblReplaceDeckCheckbox=Replace Current Deck
lblImportCardsCmd=Import Cards lblImportCardsCmd=Import Cards
lblCreateNewCmd=New Deck lblCreateNewCmd=New Deck
lblErrNotAllowedCard=Set not allowed in {0} lblErrNotAllowedCard=Set not allowed in {0}
@@ -2842,6 +2843,7 @@ lblWarnLimitedCard={0} in {1}
lblErrCardEditionDate=Set not compliant with Release Date option lblErrCardEditionDate=Set not compliant with Release Date option
lblErrUnsupportedCard=Not allowed in {0} lblErrUnsupportedCard=Not allowed in {0}
lblWarnUnknownCardMsg=Unknown Card or Unsupported in Forge lblWarnUnknownCardMsg=Unknown Card or Unsupported in Forge
lblWarnNotInInventory=Card not found in inventory
lblWarnTooManyCommanders=Current {0} Section contains {1} potential Commander Cards: {2} lblWarnTooManyCommanders=Current {0} Section contains {1} potential Commander Cards: {2}
lblWarnCommandersInSideExtra=Please check and move one to the Commander Section, in case. lblWarnCommandersInSideExtra=Please check and move one to the Commander Section, in case.
lblWarnDeckSectionNotAllowedInEditor={0} Section is not allowed in {1} lblWarnDeckSectionNotAllowedInEditor={0} Section is not allowed in {1}
@@ -2938,6 +2940,7 @@ lblCardImportWarning=\nWarning: The deck {0} will be renamed as {1}.
lblConfirmCreateNewDeck=You are about to create a new deck {0}. {1}\n\nWould you like to proceed?\n\n Note: \ lblConfirmCreateNewDeck=You are about to create a new deck {0}. {1}\n\nWould you like to proceed?\n\n Note: \
Please remember to click on the "Save" button in the Deck Editor to add the new deck to the Catalog! Please remember to click on the "Save" button in the Deck Editor to add the new deck to the Catalog!
lblNewDeckWarning=\nWarning: Any unsaved changes to the current deck {0} will be lost. lblNewDeckWarning=\nWarning: Any unsaved changes to the current deck {0} will be lost.
lblConfirmReplaceDeck=This will replace the contents of the current deck ({0}) with the imported cards.\n\nWould you like to proceed?
lblImportCardsDialogTitle=Import cards in the Current Deck lblImportCardsDialogTitle=Import cards in the Current Deck
lblNewDeckDialogTitle=Create a New Deck lblNewDeckDialogTitle=Create a New Deck
#FNetOverlay.java #FNetOverlay.java

View File

@@ -2917,6 +2917,7 @@ lblDecklistTitle=Lista de decks
lblSummaryStats=Estatísticas Resumidas lblSummaryStats=Estatísticas Resumidas
lblDeckSection=Seção lblDeckSection=Seção
lblNewDeckCheckbox=Criar um Novo deck lblNewDeckCheckbox=Criar um Novo deck
lblReplaceDeckCheckbox=Replace Current Deck
lblImportCardsCmd=Importar Cartas lblImportCardsCmd=Importar Cartas
lblCreateNewCmd=Novo Deck lblCreateNewCmd=Novo Deck
lblErrNotAllowedCard=Coleção não permitida em {0} lblErrNotAllowedCard=Coleção não permitida em {0}
@@ -2924,6 +2925,7 @@ lblWarnLimitedCard={0} em {1}
lblErrCardEditionDate=Coleção não compatível com a opção de Data de Lançamento lblErrCardEditionDate=Coleção não compatível com a opção de Data de Lançamento
lblErrUnsupportedCard=Não permitido em {0} lblErrUnsupportedCard=Não permitido em {0}
lblWarnUnknownCardMsg=Carta desconhecida ou não suportada no Forge lblWarnUnknownCardMsg=Carta desconhecida ou não suportada no Forge
lblWarnNotInInventory=Card not found in inventory
lblWarnTooManyCommanders=Seção {0} Atual contém {1} Cartas de Comandante em potencial\: {2} lblWarnTooManyCommanders=Seção {0} Atual contém {1} Cartas de Comandante em potencial\: {2}
lblWarnCommandersInSideExtra=Verifique e mova um para a seção do Comandante. lblWarnCommandersInSideExtra=Verifique e mova um para a seção do Comandante.
lblWarnDeckSectionNotAllowedInEditor=Seção {0} não é permitida em {1} lblWarnDeckSectionNotAllowedInEditor=Seção {0} não é permitida em {1}
@@ -3012,6 +3014,7 @@ Deseja prosseguir?\n\
Nota\: Por favor, lembre-se de clicar no botão "Salvar" no Editor do Deck para adicioná-lo ao Catálogo\! Nota\: Por favor, lembre-se de clicar no botão "Salvar" no Editor do Deck para adicioná-lo ao Catálogo\!
lblNewDeckWarning=\n\ lblNewDeckWarning=\n\
Aviso\: Qualquer alteração não salva no deck atual {0} será perdida. Aviso\: Qualquer alteração não salva no deck atual {0} será perdida.
lblConfirmReplaceDeck=This will replace the contents of the current deck ({0}) with the imported cards.\n\nWould you like to proceed?
lblImportCardsDialogTitle=Importar cartas no Deck Atual lblImportCardsDialogTitle=Importar cartas no Deck Atual
lblNewDeckDialogTitle=Criar um Novo deck lblNewDeckDialogTitle=Criar um Novo deck
#FNetOverlay.java #FNetOverlay.java

View File

@@ -2844,6 +2844,7 @@ lblDecklistTitle=套牌列表
lblSummaryStats=统计摘要 lblSummaryStats=统计摘要
lblDeckSection=部分 lblDeckSection=部分
lblNewDeckCheckbox=创建一个新套牌 lblNewDeckCheckbox=创建一个新套牌
lblReplaceDeckCheckbox=Replace Current Deck
lblImportCardsCmd=导入牌张 lblImportCardsCmd=导入牌张
lblCreateNewCmd=新建套牌 lblCreateNewCmd=新建套牌
lblErrNotAllowedCard=系列{0}不被允许 lblErrNotAllowedCard=系列{0}不被允许
@@ -2851,6 +2852,7 @@ lblWarnLimitedCard={0}中的{1}
lblErrCardEditionDate=不符合上市日期选项 lblErrCardEditionDate=不符合上市日期选项
lblErrUnsupportedCard={0}不被允许 lblErrUnsupportedCard={0}不被允许
lblWarnUnknownCardMsg=未知的牌张或未被forge支持的牌张 lblWarnUnknownCardMsg=未知的牌张或未被forge支持的牌张
lblWarnNotInInventory=Card not found in inventory
lblWarnTooManyCommanders=现在{0}部分包含{1}张潜在的指挥官牌张: {2} lblWarnTooManyCommanders=现在{0}部分包含{1}张潜在的指挥官牌张: {2}
lblWarnCommandersInSideExtra=如果确实是指挥官,请进行进行检查并将其中的一张移动到指挥官区。 lblWarnCommandersInSideExtra=如果确实是指挥官,请进行进行检查并将其中的一张移动到指挥官区。
lblWarnDeckSectionNotAllowedInEditor={0}部分中的{1}不被允许 lblWarnDeckSectionNotAllowedInEditor={0}部分中的{1}不被允许
@@ -2928,6 +2930,7 @@ lblCardImportWarning=\n警告套牌{0}将被重命名为{1}。
lblConfirmCreateNewDeck=你即将创建一个新套牌{0}。{1}\n\n你想要继续吗\n\n 注意:请记得点击套牌编辑器中的\"保存按钮\"以\ lblConfirmCreateNewDeck=你即将创建一个新套牌{0}。{1}\n\n你想要继续吗\n\n 注意:请记得点击套牌编辑器中的\"保存按钮\"以\
将新建的套牌保存到目录中! 将新建的套牌保存到目录中!
lblNewDeckWarning=\n警告对于当前套牌{0}的任何未保存更改丢将被丢弃。 lblNewDeckWarning=\n警告对于当前套牌{0}的任何未保存更改丢将被丢弃。
lblConfirmReplaceDeck=This will replace the contents of the current deck ({0}) with the imported cards.\n\nWould you like to proceed?
lblImportCardsDialogTitle=将牌张导入到当前套牌 lblImportCardsDialogTitle=将牌张导入到当前套牌
lblNewDeckDialogTitle=创建一个新套牌 lblNewDeckDialogTitle=创建一个新套牌
#FNetOverlay.java #FNetOverlay.java

View File

@@ -13,15 +13,24 @@ import forge.gui.util.SOptionPane;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.localinstance.properties.ForgePreferences; import forge.localinstance.properties.ForgePreferences;
import forge.model.FModel; import forge.model.FModel;
import forge.util.ItemPool;
import forge.util.Localizer; import forge.util.Localizer;
import forge.util.StreamUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import java.text.DateFormatSymbols; import java.text.DateFormatSymbols;
import java.util.*; import java.util.*;
import java.util.function.Function;
public class DeckImportController { public class DeckImportController {
private boolean createNewDeck; public enum ImportBehavior {
MERGE,
CREATE_NEW,
REPLACE_CURRENT
}
private ImportBehavior importBehavior;
// Date filter // Date filter
private final ICheckBox dateTimeCheck; private final ICheckBox dateTimeCheck;
private final IComboBox<String> monthDropdown; private final IComboBox<String> monthDropdown;
@@ -30,7 +39,7 @@ public class DeckImportController {
private CardDb.CardArtPreference artPreference; private CardDb.CardArtPreference artPreference;
private boolean smartCardArt; private boolean smartCardArt;
// Block Preference Filter // Block Preference Filter
private boolean inlcludeBnRInDeck = false; private boolean includeBnRInDeck = false;
private final List<Token> tokens = new ArrayList<>(); private final List<Token> tokens = new ArrayList<>();
private final Map<PaperCard, Token> cardsInTokens = new HashMap<>(); private final Map<PaperCard, Token> cardsInTokens = new HashMap<>();
@@ -38,7 +47,14 @@ public class DeckImportController {
private Deck currentDeckInEditor = null; private Deck currentDeckInEditor = null;
private DeckFormat currentDeckFormat; private DeckFormat currentDeckFormat;
private GameFormat currentGameFormat; private GameFormat currentGameFormat;
private GameType currentGameType;
private final List<DeckSection> allowedSections = new ArrayList<>(); private final List<DeckSection> allowedSections = new ArrayList<>();
private ItemPool<PaperCard> playerInventory;
/**
* If a free card is missing from a player's inventory (e.g. a basic land), it gets run through this function, which
* can handle creation of a usable print.
*/
private Function<PaperCard, PaperCard> freePrintSupplier;
public DeckImportController(ICheckBox dateTimeCheck0, public DeckImportController(ICheckBox dateTimeCheck0,
IComboBox<String> monthDropdown0, IComboBox<Integer> yearDropdown0, IComboBox<String> monthDropdown0, IComboBox<Integer> yearDropdown0,
@@ -54,13 +70,14 @@ public class DeckImportController {
*/ */
this.currentDeckNotEmpty = currentDeckNotEmpty; this.currentDeckNotEmpty = currentDeckNotEmpty;
// this option will control the "new deck" action controlled by UI widget // this option will control the "new deck" action controlled by UI widget
createNewDeck = false; this.importBehavior = ImportBehavior.MERGE;
// Init default parameters // Init default parameters
this.artPreference = StaticData.instance().getCardArtPreference(); // default this.artPreference = StaticData.instance().getCardArtPreference(); // default
this.smartCardArt = StaticData.instance().isEnabledCardArtSmartSelection(); this.smartCardArt = StaticData.instance().isEnabledCardArtSmartSelection();
this.currentDeckFormat = null; this.currentDeckFormat = null;
this.currentGameFormat = null; this.currentGameFormat = null;
this.currentGameType = null;
fillDateDropdowns(); fillDateDropdowns();
} }
@@ -68,13 +85,23 @@ public class DeckImportController {
if (gameType == null){ if (gameType == null){
this.currentGameFormat = null; this.currentGameFormat = null;
this.currentDeckFormat = null; this.currentDeckFormat = null;
this.currentGameType = null;
} else { } else {
// get the game format with the same name of current game type (if any) // get the game format with the same name of current game type (if any)
this.currentDeckFormat = gameType.getDeckFormat(); this.currentDeckFormat = gameType.getDeckFormat();
this.currentGameFormat = FModel.getFormats().get(gameType.name()); this.currentGameFormat = FModel.getFormats().get(gameType.name());
this.currentGameType = gameType;
} }
} }
public void setPlayerInventory(ItemPool<PaperCard> inventory) {
this.playerInventory = inventory;
}
public void setFreePrintConverter(Function<PaperCard, PaperCard> freePrintSupplier) {
this.freePrintSupplier = freePrintSupplier;
}
public void setCurrentDeckInEditor(Deck deckInEditor){ public void setCurrentDeckInEditor(Deck deckInEditor){
this.currentDeckInEditor = deckInEditor; this.currentDeckInEditor = deckInEditor;
} }
@@ -105,11 +132,13 @@ public class DeckImportController {
return this.smartCardArt; return this.smartCardArt;
} }
public void setCreateNewDeck(boolean createNewDeck){ public void setImportBehavior(ImportBehavior importBehavior) {
this.createNewDeck = createNewDeck; this.importBehavior = importBehavior;
} }
public boolean getCreateNewDeck() { return this.createNewDeck; } public ImportBehavior getImportBehavior() {
return importBehavior;
}
private void fillDateDropdowns() { private void fillDateDropdowns() {
DateFormatSymbols dfs = new DateFormatSymbols(); DateFormatSymbols dfs = new DateFormatSymbols();
@@ -159,10 +188,10 @@ public class DeckImportController {
} }
public void importBannedAndRestrictedCards(boolean includeBannedAndRestricted){ public void importBannedAndRestrictedCards(boolean includeBannedAndRestricted){
this.inlcludeBnRInDeck = includeBannedAndRestricted; this.includeBnRInDeck = includeBannedAndRestricted;
} }
public boolean importBannedAndRestrictedCards(){ return this.inlcludeBnRInDeck; } public boolean importBannedAndRestrictedCards(){ return this.includeBnRInDeck; }
public List<Token> parseInput(String input) { public List<Token> parseInput(String input) {
tokens.clear(); tokens.clear();
@@ -186,7 +215,7 @@ public class DeckImportController {
if (!this.allowedSections.isEmpty()) if (!this.allowedSections.isEmpty())
recognizer.setAllowedDeckSections(this.allowedSections); recognizer.setAllowedDeckSections(this.allowedSections);
// Banned and Restricted Card Policy // Banned and Restricted Card Policy
if (this.inlcludeBnRInDeck) if (this.includeBnRInDeck)
recognizer.forceImportBannedAndRestrictedCards(); recognizer.forceImportBannedAndRestrictedCards();
String[] lines = input.split("\n"); String[] lines = input.split("\n");
@@ -196,8 +225,13 @@ public class DeckImportController {
if (this.currentGameFormatAllowsCommander()) { if (this.currentGameFormatAllowsCommander()) {
List<Pair<Integer, Token>> commanderTokens = getTokensInSection(DeckSection.Commander); List<Pair<Integer, Token>> commanderTokens = getTokensInSection(DeckSection.Commander);
if (commanderTokens.isEmpty()) // Check commanders in Sideboard only if the commander section is empty if (commanderTokens.isEmpty()) {
// Check commanders in Sideboard only if the commander section is empty
if(!getTokensInSection(DeckSection.Sideboard).isEmpty())
checkAndFixCommanderIn(DeckSection.Sideboard); checkAndFixCommanderIn(DeckSection.Sideboard);
else
checkAndFixCommanderIn(DeckSection.Main);
}
checkAndFixCommanderIn(DeckSection.Commander); checkAndFixCommanderIn(DeckSection.Commander);
} }
@@ -309,7 +343,7 @@ public class DeckImportController {
} }
public boolean currentGameFormatAllowsCommander(){ public boolean currentGameFormatAllowsCommander(){
return this.allowedSections.contains(DeckSection.Commander); return this.allowedSections.contains(DeckSection.Commander) || this.currentGameType == GameType.PlanarConquest;
} }
public List<Token> optimiseCardArtInTokens(){ public List<Token> optimiseCardArtInTokens(){
@@ -332,12 +366,7 @@ public class DeckImportController {
else else
refTokenMap = tokensPerSectionWithSet; refTokenMap = tokensPerSectionWithSet;
List<Token> tokensInSection = refTokenMap.getOrDefault(tokenSection, null); List<Token> tokensInSection = refTokenMap.computeIfAbsent(tokenSection, e -> new ArrayList<>());
if (tokensInSection == null) {
tokensInSection = new ArrayList<>();
tokensInSection.add(token);
refTokenMap.put(tokenSection, tokensInSection);
} else
tokensInSection.add(token); tokensInSection.add(token);
} }
@@ -356,7 +385,7 @@ public class DeckImportController {
Map<DeckSection, CardPool> referencePoolPerSection = new HashMap<>(); Map<DeckSection, CardPool> referencePoolPerSection = new HashMap<>();
if (this.currentDeckNotEmpty && !this.createNewDeck && this.currentDeckInEditor != null){ if (this.currentDeckNotEmpty && this.importBehavior == ImportBehavior.MERGE && this.currentDeckInEditor != null){
// We will always consider ONLY sections for cards needing art optimisation // We will always consider ONLY sections for cards needing art optimisation
for (DeckSection section : tokensPerSectionWithNoSet.keySet()){ for (DeckSection section : tokensPerSectionWithNoSet.keySet()){
CardPool cardsInDeck = this.currentDeckInEditor.get(section); CardPool cardsInDeck = this.currentDeckInEditor.get(section);
@@ -436,6 +465,100 @@ public class DeckImportController {
return tokens; return tokens;
} }
public List<Token> constrainTokensToInventory() {
if(this.playerInventory == null)
return tokens;
CardPool availableInventory = new CardPool(this.playerInventory);
//Map of tokens to the things we're gonna replace them with.
Map<Token, List<Token>> tokenReplacers = new LinkedHashMap<>();
//If we're adding to our existing deck, ensure we aren't counting the cards already in it.
if(this.importBehavior == ImportBehavior.MERGE && this.currentDeckInEditor != null)
availableInventory.removeAll(this.currentDeckInEditor.getAllCardsInASinglePool(true, true));
if(this.currentGameType == GameType.PlanarConquest && currentDeckInEditor != null)
availableInventory.removeAllFlat(this.currentDeckInEditor.getCommanders());
//Step 1: For each token, if it's asking for more copies of a print than we can supply, split the difference out
//into a token that's indifferent to the edition. Reduce available inventory accordingly.
for (Token token : this.tokens) {
if (!token.isCardToken())
continue;
PaperCard card = token.getCard();
int requestedAmount = token.getQuantity();
if (card == null)
continue;
if (token.cardRequestHasNoCode()) {
List<Token> list = new ArrayList<>();
tokenReplacers.put(token, list);
continue;
}
int available = availableInventory.count(card);
if (available <= 0) {
List<Token> list = new ArrayList<>();
tokenReplacers.put(token, list);
continue;
}
int numTaken = Math.min(requestedAmount, available);
availableInventory.remove(card, numTaken);
if (available >= requestedAmount)
continue;
List<Token> list = new ArrayList<>();
list.add(Token.LegalCard(card, numTaken, token.getTokenSection(), true));
tokenReplacers.put(token, list);
}
if(tokenReplacers.isEmpty())
return tokens; //We have every card that was requested.
//Step 2: Try to find alternative prints for the ones that do not request an edition.
int capacity = tokens.size();
for(Map.Entry<Token, List<Token>> tokenReplacer : tokenReplacers.entrySet()) {
Token token = tokenReplacer.getKey();
DeckSection tokenSection = token.getTokenSection();
List<Token> replacementList = tokenReplacer.getValue();
PaperCard card = token.getCard();
String cardName = card.getName();
CardPool substitutes = availableInventory.getFilteredPool(c -> c.getName().equals(cardName));
List<Map.Entry<PaperCard, Integer>> sortedSubstitutes = StreamUtil.stream(substitutes).sorted(Comparator.comparingInt(Map.Entry::getValue)).toList();
int neededQuantity = token.getQuantity();
for(Token found : replacementList) {
//If there's an item in the replacement list already it means we've already found some of the needed copies.
neededQuantity -= found.getQuantity();
}
for(int i = 0; i < sortedSubstitutes.size() && neededQuantity > 0; i++) {
Map.Entry<PaperCard, Integer> item = sortedSubstitutes.get(i);
PaperCard replacement = item.getKey();
int toMove = Math.min(neededQuantity, item.getValue());
replacementList.add(Token.LegalCard(replacement, toMove, tokenSection, true));
availableInventory.remove(replacement, toMove);
neededQuantity -= toMove;
capacity++;
}
if(neededQuantity > 0) {
PaperCard freePrint = getInfiniteSupplyPrinting(card);
if(freePrint != null)
replacementList.add(Token.NotInInventoryFree(freePrint, neededQuantity, tokenSection));
else
replacementList.add(Token.NotInInventory(card, neededQuantity, tokenSection));
capacity++;
}
}
//Step 3: Apply the replacement list.
List<Token> newList = new ArrayList<>(capacity);
for(Token t : this.tokens) {
if(tokenReplacers.containsKey(t))
newList.addAll(tokenReplacers.get(t));
else
newList.add(t);
}
this.tokens.clear();
this.tokens.addAll(newList);
return tokens;
}
private PaperCard getInfiniteSupplyPrinting(PaperCard card) {
if(this.freePrintSupplier == null)
return null;
return freePrintSupplier.apply(card);
}
private int countTokens(List<Token> tokensInSection){ private int countTokens(List<Token> tokensInSection){
if (tokensInSection == null || tokensInSection.isEmpty()) if (tokensInSection == null || tokensInSection.isEmpty())
return 0; return 0;
@@ -487,27 +610,31 @@ public class DeckImportController {
if (tokens.isEmpty()) { return null; } if (tokens.isEmpty()) { return null; }
String deckName = ""; String deckName = "";
if (currentDeckName != null && currentDeckName.trim().length() > 0) if (currentDeckName != null && !currentDeckName.trim().isEmpty())
deckName = String.format("\"%s\"", currentDeckName.trim()); deckName = String.format("\"%s\"", currentDeckName.trim());
String tokenDeckName = getTokenDeckNameIfAny(); String tokenDeckName = getTokenDeckNameIfAny();
if (tokenDeckName.length() > 0) if (!tokenDeckName.isEmpty())
tokenDeckName = String.format("\"%s\"", tokenDeckName); tokenDeckName = String.format("\"%s\"", tokenDeckName);
if (createNewDeck){ if(this.currentDeckNotEmpty) {
String extraWarning = currentDeckNotEmpty ? localizer.getMessage("lblNewDeckWarning", deckName) : ""; final String warning;
final String warning = localizer.getMessage("lblConfirmCreateNewDeck", tokenDeckName, extraWarning); final String title;
if (!SOptionPane.showConfirmDialog(warning, localizer.getMessage("lblNewDeckDialogTitle"), if (this.importBehavior == ImportBehavior.CREATE_NEW) {
localizer.getMessage("lblYes"), localizer.getMessage("lblNo"))) { String extraWarning = localizer.getMessage("lblNewDeckWarning", deckName);
return null; warning = localizer.getMessage("lblConfirmCreateNewDeck", tokenDeckName, extraWarning);
} title = localizer.getMessage("lblNewDeckDialogTitle");
} } else if (this.importBehavior == ImportBehavior.MERGE){
else if (this.currentDeckNotEmpty){ String extraWarning = (!tokenDeckName.isEmpty() && !tokenDeckName.equals(deckName)) ?
String extraWarning = (tokenDeckName.length() > 0 && !tokenDeckName.equals(deckName)) ?
localizer.getMessage("lblCardImportWarning", deckName, tokenDeckName) : ""; localizer.getMessage("lblCardImportWarning", deckName, tokenDeckName) : "";
final String warning = localizer.getMessage("lblConfirmCardImport", deckName, extraWarning); warning = localizer.getMessage("lblConfirmCardImport", deckName, extraWarning);
if (!SOptionPane.showConfirmDialog(warning, title = localizer.getMessage("lblImportCardsDialogTitle");
localizer.getMessage("lblImportCardsDialogTitle"), }
else {
warning = localizer.getMessage("lblConfirmReplaceDeck", deckName);
title = localizer.getMessage("lblNewDeckDialogTitle");
}
if (!SOptionPane.showConfirmDialog(warning, title,
localizer.getMessage("lblYes"), localizer.getMessage("lblNo"))) localizer.getMessage("lblYes"), localizer.getMessage("lblNo")))
return null; return null;
} }
@@ -516,7 +643,7 @@ public class DeckImportController {
final TokenType type = t.getType(); final TokenType type = t.getType();
// only Deck Name, legal card and limited card tokens will be analysed! // only Deck Name, legal card and limited card tokens will be analysed!
if (!t.isTokenForDeck() || if (!t.isTokenForDeck() ||
(type == TokenType.LIMITED_CARD && !this.inlcludeBnRInDeck)) (type == TokenType.LIMITED_CARD && !this.includeBnRInDeck))
continue; // SKIP token continue; // SKIP token
if (type == TokenType.DECK_NAME) { if (type == TokenType.DECK_NAME) {
@@ -547,4 +674,66 @@ public class DeckImportController {
} }
return ""; // no deck name return ""; // no deck name
} }
public String getTokenMessage(DeckRecognizer.Token token) {
return switch (token.getType()) {
case LEGAL_CARD, LIMITED_CARD, CARD_FROM_NOT_ALLOWED_SET, CARD_FROM_INVALID_SET,
CARD_NOT_IN_INVENTORY, FREE_CARD_NOT_IN_INVENTORY ->
String.format("%s x %s %s", token.getQuantity(), token.getText(), getTokenFoilLabel(token));
// Card Warning Msgs
case UNKNOWN_CARD, UNSUPPORTED_CARD ->
token.getQuantity() > 0 ? String.format("%s x %s", token.getQuantity(), token.getText())
: token.getText();
case UNSUPPORTED_DECK_SECTION ->
String.format("%s: %s", Localizer.getInstance().getMessage("lblWarningMsgPrefix"),
Localizer.getInstance()
.getMessage("lblWarnDeckSectionNotAllowedInEditor", token.getText(),
this.currentGameType.name()));
// Special Case of Card moved into another section (e.g. Commander from Sideboard)
case WARNING_MESSAGE -> String.format("%s: %s", Localizer.getInstance()
.getMessage("lblWarningMsgPrefix"), token.getText());
// Placeholders
case DECK_SECTION_NAME -> String.format("%s: %s", Localizer.getInstance().getMessage("lblDeckSection"),
token.getText());
case CARD_RARITY -> String.format("%s: %s", Localizer.getInstance().getMessage("lblRarity"),
token.getText());
case CARD_TYPE, CARD_CMC, MANA_COLOUR, COMMENT, UNKNOWN_TEXT -> token.getText();
case DECK_NAME -> String.format("%s: %s", Localizer.getInstance().getMessage("lblDeckName"),
token.getText());
};
}
public String getTokenStatusMessage(DeckRecognizer.Token token) {
if (token == null)
return "";
final Localizer localizer = Localizer.getInstance();
return switch (token.getType()) {
case LIMITED_CARD -> String.format("%s: %s", localizer.getMessage("lblWarningMsgPrefix"),
localizer.getMessage("lblWarnLimitedCard",
StringUtils.capitalize(token.getLimitedCardType().name()), getGameFormatLabel()));
case CARD_FROM_NOT_ALLOWED_SET ->
localizer.getMessage("lblErrNotAllowedCard", getGameFormatLabel());
case CARD_FROM_INVALID_SET -> localizer.getMessage("lblErrCardEditionDate");
case UNSUPPORTED_CARD -> localizer.getMessage("lblErrUnsupportedCard", this.currentGameType);
case UNKNOWN_CARD -> String.format("%s: %s", localizer.getMessage("lblWarningMsgPrefix"),
localizer.getMessage("lblWarnUnknownCardMsg"));
case CARD_NOT_IN_INVENTORY -> localizer.getMessage("lblWarnNotInInventory");
default -> "";
};
}
private String getTokenFoilLabel(DeckRecognizer.Token token) {
if (!token.isCardToken())
return "";
final String foilMarker = "- (Foil)";
return token.getCard().isFoil() ? foilMarker : "";
}
private String getGameFormatLabel() {
return String.format("\"%s\"", this.getCurrentGameFormatName());
}
} }

View File

@@ -774,7 +774,7 @@ public class DeckProxy implements InventoryItem {
for (PaperCard c : deck.getAllCardsInASinglePool().toFlatList()) { for (PaperCard c : deck.getAllCardsInASinglePool().toFlatList()) {
CardEdition edition = FModel.getMagicDb().getEditions().get(c.getEdition()); CardEdition edition = FModel.getMagicDb().getEditions().get(c.getEdition());
if (edition == null) if (edition == null || !edition.hasBasicLands())
continue; continue;
availableEditions.add(edition); availableEditions.add(edition);
} }

View File

@@ -18,15 +18,11 @@
package forge.gamemodes.planarconquest; package forge.gamemodes.planarconquest;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import forge.card.CardDb; import forge.card.CardDb;
import forge.gamemodes.planarconquest.ConquestPreferences.CQPref; import forge.gamemodes.planarconquest.ConquestPreferences.CQPref;
@@ -199,14 +195,11 @@ public final class ConquestData {
} }
public int getAccessiblePlaneCount() { public int getAccessiblePlaneCount() {
// TODO: Java 8 stream implementation of filtering return (int) FModel.getPlanes().stream().filter(Predicate.not(ConquestPlane::isUnreachable)).count();
int i = 0;
for (ConquestPlane plane : FModel.getPlanes()) {
if (!plane.isUnreachable()) {
i++;
} }
}
return i; public Set<ConquestPlane> getUnlockedPlanes() {
return planeDataMap.values().stream().map(ConquestPlaneData::getLocation).map(ConquestLocation::getPlane).collect(Collectors.toSet());
} }
public void unlockPlane(ConquestPlane plane) { public void unlockPlane(ConquestPlane plane) {
@@ -302,7 +295,7 @@ public final class ConquestData {
} }
} }
if (commandersUsingCard.length() > 0) { if (!commandersUsingCard.isEmpty()) {
SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblCommandersCardCannotBeExiledByCard", CardTranslation.getTranslatedName(card.getName()), commandersUsingCard), title, SOptionPane.INFORMATION_ICON); SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblCommandersCardCannotBeExiledByCard", CardTranslation.getTranslatedName(card.getName()), commandersUsingCard), title, SOptionPane.INFORMATION_ICON);
return false; return false;
} }

View File

@@ -50,6 +50,7 @@ public class ConquestPlane {
private FCollection<PaperCard> commanders; private FCollection<PaperCard> commanders;
private ConquestAwardPool awardPool; private ConquestAwardPool awardPool;
private ConquestEvent[] events; private ConquestEvent[] events;
private final Set<CardEdition> editions = new HashSet<>();
private ConquestPlane(String name0, String description0, int regionSize0, boolean unreachable0) { private ConquestPlane(String name0, String description0, int regionSize0, boolean unreachable0) {
name = name0; name = name0;
@@ -153,6 +154,10 @@ public class ConquestPlane {
return planeCards; return planeCards;
} }
public Set<CardEdition> getEditions() {
return editions;
}
private void ensureRegionsLoaded() { private void ensureRegionsLoaded() {
if (regions != null) { return; } if (regions != null) { return; }
@@ -193,6 +198,8 @@ public class ConquestPlane {
if (edition == null) if (edition == null)
continue; continue;
editions.add(edition);
for (EditionEntry card : edition.getObtainableCards()) { for (EditionEntry card : edition.getObtainableCards()) {
if (bannedCardSet == null || !bannedCardSet.contains(card.name())) { if (bannedCardSet == null || !bannedCardSet.contains(card.name())) {
addCard(commonCards.getCard(card.name(), setCode)); addCard(commonCards.getCard(card.name(), setCode));

View File

@@ -1,22 +1,16 @@
package forge.gamemodes.planarconquest; package forge.gamemodes.planarconquest;
import java.util.EnumSet; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import forge.card.CardRarity; import forge.card.*;
import forge.card.CardRules;
import forge.card.CardType;
import forge.card.CardType.CoreType; import forge.card.CardType.CoreType;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.card.mana.ManaCostShard; import forge.card.mana.ManaCostShard;
import forge.deck.CardPool; import forge.deck.CardPool;
import forge.deck.Deck; import forge.deck.Deck;
@@ -143,6 +137,25 @@ public class ConquestUtil {
return pool; return pool;
} }
public static List<CardEdition> getBasicLandSets(Deck currentDeck) {
ConquestData model = FModel.getConquest().getModel();
List<ConquestPlane> planes = new ArrayList<>(model.getUnlockedPlanes());
ConquestPlane currentPlane = model.getCurrentPlane();
//Move the current plane to the front.
if(currentPlane != null && planes.contains(currentPlane)) {
planes.remove(currentPlane);
planes.add(0, currentPlane);
}
//Move editions of cards already in the deck to the front.
Map<CardEdition, Integer> editionStats = currentDeck.getAllCardsInASinglePool().getCardEditionStatistics(true);
List<CardEdition> out = planes.stream()
.<CardEdition>mapMulti((p, c) -> p.getEditions().forEach(c))
.filter(CardEdition::hasBasicLands)
.sorted(Comparator.comparing(e -> editionStats.getOrDefault(e, 0)))
.collect(Collectors.toList());
return out;
}
public static ConquestPlane getPlaneByName(String planeName) { public static ConquestPlane getPlaneByName(String planeName) {
for (ConquestPlane plane : FModel.getPlanes()) { for (ConquestPlane plane : FModel.getPlanes()) {
if (plane.getName().equals(planeName)) { if (plane.getName().equals(planeName)) {
@@ -189,19 +202,16 @@ public class ConquestUtil {
public static int getShardValue(CardRarity rarity, CQPref baseValuePref) { public static int getShardValue(CardRarity rarity, CQPref baseValuePref) {
ConquestPreferences prefs = FModel.getConquestPreferences(); ConquestPreferences prefs = FModel.getConquestPreferences();
int baseValue = prefs.getPrefInt(baseValuePref); int baseValue = prefs.getPrefInt(baseValuePref);
switch (rarity) { return switch (rarity) {
case Common: case Common -> baseValue;
return baseValue; case Uncommon ->
case Uncommon: Math.round((float) baseValue * (float) prefs.getPrefInt(CQPref.AETHER_UNCOMMON_MULTIPLIER));
return Math.round((float)baseValue * (float)prefs.getPrefInt(CQPref.AETHER_UNCOMMON_MULTIPLIER)); case Rare, Special ->
case Rare: Math.round((float) baseValue * (float) prefs.getPrefInt(CQPref.AETHER_RARE_MULTIPLIER));
case Special: case MythicRare ->
return Math.round((float)baseValue * (float)prefs.getPrefInt(CQPref.AETHER_RARE_MULTIPLIER)); Math.round((float) baseValue * (float) prefs.getPrefInt(CQPref.AETHER_MYTHIC_MULTIPLIER));
case MythicRare: default -> 0;
return Math.round((float)baseValue * (float)prefs.getPrefInt(CQPref.AETHER_MYTHIC_MULTIPLIER)); };
default:
return 0;
}
} }
public enum AEtherFilter implements IHasSkinProp { public enum AEtherFilter implements IHasSkinProp {

View File

@@ -19,6 +19,7 @@ package forge.gamemodes.quest;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
@@ -620,17 +621,21 @@ public class QuestController {
} }
public CardEdition getDefaultLandSet() { public CardEdition getDefaultLandSet() {
List<String> availableEditionCodes = questFormat != null ? questFormat.getAllowedSetCodes() : Lists.newArrayList(FModel.getMagicDb().getEditions().getItemNames()); List<CardEdition> availableEditions = getAvailableLandSets();
List<CardEdition> availableEditions = new ArrayList<>();
for (String s : availableEditionCodes) {
availableEditions.add(FModel.getMagicDb().getEditions().get(s));
}
CardEdition randomLandSet = CardEdition.Predicates.getRandomSetWithAllBasicLands(availableEditions); CardEdition randomLandSet = CardEdition.Predicates.getRandomSetWithAllBasicLands(availableEditions);
return randomLandSet == null ? FModel.getMagicDb().getEditions().get("ZEN") : randomLandSet; return randomLandSet == null ? FModel.getMagicDb().getEditions().get("ZEN") : randomLandSet;
} }
public List<CardEdition> getAvailableLandSets() {
List<String> availableEditionCodes = questFormat != null ? questFormat.getAllowedSetCodes() : Lists.newArrayList(FModel.getMagicDb().getEditions().getItemNames());
CardEdition.Collection editions = FModel.getMagicDb().getEditions();
return availableEditionCodes.stream()
.map(editions::get)
.filter(CardEdition::hasBasicLands)
.collect(Collectors.toList());
}
public String getCurrentDeck() { public String getCurrentDeck() {
return model.currentDeck; return model.currentDeck;
} }