Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions forge-core/src/main/java/forge/deck/Deck.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
private final Map<String, String> draftNotes = new HashMap<>();
private Map<String, List<String>> deferredSections = null;
private Map<String, List<String>> loadedSections = null;
private DeckFormat deckFormat;
private String sourceUrl;
private String lastCardArtPreferenceUsed = "";
private Boolean lastCardArtOptimisationOptionUsed = null;
private boolean includeCardsFromUnspecifiedSet = false;
Expand Down Expand Up @@ -253,6 +255,8 @@ protected void cloneFieldsTo(final DeckBase clone) {
}
result.setAiHints(StringUtils.join(aiHints, " | "));
result.setDraftNotes(draftNotes);
result.setDeckFormat(deckFormat);
result.setSourceUrl(sourceUrl);
//noinspection ConstantValue
if(tags != null) //Can happen deserializing old Decks.
result.tags.addAll(this.tags);
Expand Down Expand Up @@ -613,6 +617,22 @@ public Map<String, String> getDraftNotes() {
return draftNotes;
}

public void setDeckFormat(DeckFormat deckFormat0) {
deckFormat = deckFormat0;
}

public DeckFormat getDeckFormat() {
return deckFormat;
}

public void setSourceUrl(String sourceUrl0) {
sourceUrl = sourceUrl0;
}

public String getSourceUrl() {
return sourceUrl;
}

public void setAiHints(String aiHintsInfo) {
if (aiHintsInfo == null || aiHintsInfo.trim().isEmpty()) {
return;
Expand Down
7 changes: 7 additions & 0 deletions forge-core/src/main/java/forge/deck/io/DeckFileHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class DeckFileHeader {

/** The Constant DECK_TYPE. */
public static final String DECK_TYPE = "Deck Type";
public static final String SOURCE_URL = "Source URL";
public static final String TAGS = "Tags";

public static final String TAGS_SEPARATOR = ",";
Expand All @@ -52,6 +53,7 @@ public class DeckFileHeader {
public static final String AI_HINTS = "AiHints";

private final DeckFormat deckType;
private final String sourceUrl;
private final boolean customPool;

private final String name;
Expand All @@ -76,6 +78,7 @@ public DeckFileHeader(final FileSection kvPairs) {
this.name = kvPairs.get(DeckFileHeader.NAME);
this.comment = kvPairs.get(DeckFileHeader.COMMENT);
this.deckType = DeckFormat.smartValueOf(kvPairs.get(DeckFileHeader.DECK_TYPE), DeckFormat.Constructed);
this.sourceUrl = kvPairs.get(DeckFileHeader.SOURCE_URL);
this.customPool = kvPairs.getBoolean(DeckFileHeader.CSTM_POOL);
this.intendedForAi = "computer".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER)) || "ai".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER_TYPE));
this.aiHints = kvPairs.get(DeckFileHeader.AI_HINTS);
Expand Down Expand Up @@ -136,6 +139,10 @@ public final DeckFormat getDeckType() {
return this.deckType;
}

public String getSourceUrl() {
return sourceUrl;
}

public final Set<String> getTags() {
return tags;
}
Expand Down
10 changes: 9 additions & 1 deletion forge-core/src/main/java/forge/deck/io/DeckSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ private static List<String> serializeDeck(Deck d) {
out.add(TextUtil.enclosedBracket("metadata"));

out.add(TextUtil.concatNoSpace(DeckFileHeader.NAME,"=", d.getName().replaceAll("\n", "")));
if (d.getDeckFormat() != null) {
out.add(TextUtil.concatNoSpace(DeckFileHeader.DECK_TYPE, "=", d.getDeckFormat().name()));
}
if (d.getSourceUrl() != null) {
out.add(TextUtil.concatNoSpace(DeckFileHeader.SOURCE_URL, "=", d.getSourceUrl().replaceAll("\n", "")));
}
// these are optional
if (d.getComment() != null) {
out.add(TextUtil.concatNoSpace(DeckFileHeader.COMMENT,"=", d.getComment().replaceAll("\n", "")));
Expand Down Expand Up @@ -100,6 +106,8 @@ public static Deck fromSections(final Map<String, List<String>> sections) {

Deck d = new Deck(dh.getName());
d.setComment(dh.getComment());
d.setDeckFormat(dh.getDeckType());
d.setSourceUrl(dh.getSourceUrl());
d.setAiHints(dh.getAiHints());
d.getTags().addAll(dh.getTags());
d.setDraftNotes(dh.getDraftNotes());
Expand All @@ -109,4 +117,4 @@ public static Deck fromSections(final Map<String, List<String>> sections) {
d.setDeferredSections(sections);
return d;
}
}
}
143 changes: 132 additions & 11 deletions forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
import forge.screens.match.controllers.CDetailPicture;
import forge.toolbox.FLabel;
import forge.toolbox.FOptionPane;
import forge.toolbox.FTextField;
import forge.util.Localizer;
import net.miginfocom.swing.MigLayout;
import org.apache.commons.lang3.StringUtils;

import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -54,6 +56,12 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {

private final FLabel btnViewDeck = new FLabel.ButtonBuilder().text(localizer.getMessage("lblViewDeck")).fontSize(14).build();
private final FLabel btnRandom = new FLabel.ButtonBuilder().fontSize(14).build();
private JPanel pnlDeckUrl;
private FTextField txtDeckUrl;
private FLabel btnReloadUrl;
private String lastLoadedUrlDeckName;
private UiCommand deckSelectionCommand;
private boolean updatingDeckPool;

private boolean isAi;

Expand Down Expand Up @@ -96,6 +104,7 @@ public FDeckChooser(final CDetailPicture cDetailPicture, final boolean forAi, Ga
}
};
lstDecks.setItemActivateCommand(cmdViewDeck);
lstDecks.setSelectCommand(this::handleDeckSelection);
btnViewDeck.setCommand(cmdViewDeck);
}

Expand All @@ -117,11 +126,14 @@ public void setSelectedDeckType(final DeckType selectedDeckType0) {

public DeckManager getLstDecks() { return lstDecks; }

public void setDeckSelectionCommand(final UiCommand command) {
deckSelectionCommand = command;
}

private void updateDecks(final Iterable<DeckProxy> decks, final ItemManagerConfig config) {
lstDecks.setAllowMultipleSelections(false);

lstDecks.setPool(decks);
lstDecks.setup(config);
setDeckPoolWithConfig(decks, config);

btnRandom.setText(localizer.getMessage("lblRandomDeck"));
btnRandom.setCommand((UiCommand) () -> DeckgenUtil.randomSelect(lstDecks));
Expand Down Expand Up @@ -153,8 +165,7 @@ private void updateCustom() {
private void updateColors(Predicate<PaperCard> formatFilter) {
lstDecks.setAllowMultipleSelections(true);

lstDecks.setPool(ColorDeckGenerator.getColorDecks(lstDecks, formatFilter, isAi));
lstDecks.setup(ItemManagerConfig.STRING_ONLY);
setDeckPoolWithConfig(ColorDeckGenerator.getColorDecks(lstDecks, formatFilter, isAi), ItemManagerConfig.STRING_ONLY);

btnRandom.setText(localizer.getMessage("lblRandomColors"));
btnRandom.setCommand((UiCommand) () -> DeckgenUtil.randomSelectColors(lstDecks));
Expand All @@ -166,8 +177,7 @@ private void updateColors(Predicate<PaperCard> formatFilter) {
private void updateMatrix(GameFormat format) {
lstDecks.setAllowMultipleSelections(false);

lstDecks.setPool(ArchetypeDeckGenerator.getMatrixDecks(format, isAi));
lstDecks.setup(ItemManagerConfig.STRING_ONLY);
setDeckPoolWithConfig(ArchetypeDeckGenerator.getMatrixDecks(format, isAi), ItemManagerConfig.STRING_ONLY);

btnRandom.setText("Random");
btnRandom.setCommand((UiCommand) () -> DeckgenUtil.randomSelect(lstDecks));
Expand All @@ -183,8 +193,7 @@ private void updateRandomCommander() {
}

lstDecks.setAllowMultipleSelections(false);
lstDecks.setPool(CommanderDeckGenerator.getCommanderDecks(deckFormat, isAi, false));
lstDecks.setup(ItemManagerConfig.STRING_ONLY);
setDeckPoolWithConfig(CommanderDeckGenerator.getCommanderDecks(deckFormat, isAi, false), ItemManagerConfig.STRING_ONLY);

btnRandom.setText("Random");
btnRandom.setCommand((UiCommand) () -> DeckgenUtil.randomSelect(lstDecks));
Expand All @@ -200,8 +209,7 @@ private void updateRandomCardGenCommander() {
}

lstDecks.setAllowMultipleSelections(false);
lstDecks.setPool(CommanderDeckGenerator.getCommanderDecks(deckFormat, isAi, true));
lstDecks.setup(ItemManagerConfig.STRING_ONLY);
setDeckPoolWithConfig(CommanderDeckGenerator.getCommanderDecks(deckFormat, isAi, true), ItemManagerConfig.STRING_ONLY);

btnRandom.setText("Random");
btnRandom.setCommand((UiCommand) () -> DeckgenUtil.randomSelect(lstDecks));
Expand Down Expand Up @@ -289,6 +297,33 @@ private void updateNetArchiveBlockDecks() {
updateDecks(DeckProxy.getNetArchiveBlockDecks(NetDeckArchiveBlock), ItemManagerConfig.NET_DECKS);
}

private void updateProvidedDeckUrl() {
lstDecks.setAllowMultipleSelections(false);
setDeckPoolWithConfig(DeckUrlLoader.getUrlDecks(), ItemManagerConfig.NET_DECKS);

btnRandom.setText(localizer.getMessage("lblReload"));
btnRandom.setCommand(this::loadDeckFromUrl);

if (lastLoadedUrlDeckName != null) {
lstDecks.setSelectedString(lastLoadedUrlDeckName);
}
if (lstDecks.getSelectedIndex() < 0) {
lstDecks.setSelectedIndex(0);
}
syncUrlFieldWithSelectedDeck();
}

private void setDeckPoolWithConfig(final Iterable<DeckProxy> decks, final ItemManagerConfig config) {
updatingDeckPool = true;
try {
lstDecks.setPool(ImmutableList.of());
lstDecks.setup(config);
lstDecks.setPool(decks);
} finally {
updatingDeckPool = false;
}
}

public Deck getDeck() {
final DeckProxy proxy = lstDecks.getSelectedItem();
if (proxy == null) {
Expand Down Expand Up @@ -319,22 +354,104 @@ public void populate() {
if (decksComboBox == null) { //initialize components with delayed initialization the first time this is populated
decksComboBox = new DecksComboBox();
lstDecksContainer = new ItemManagerContainer(lstDecks);
initializeDeckUrlPanel();
decksComboBox.addListener(this);
restoreSavedState();
} else {
removeAll();
}
this.setLayout(new MigLayout("insets 0, gap 0"));
this.setLayout(new MigLayout("insets 0, gap 0, hidemode 3"));
decksComboBox.addTo(this, "w 100%, h 30px!, gapbottom 5px, spanx 2, wrap");
this.add(pnlDeckUrl, "w 100%, h 30px!, gapbottom 5px, spanx 2, wrap");
this.add(lstDecksContainer, "w 100%, growy, pushy, spanx 2, wrap");
this.add(btnViewDeck, "w 50%-3px, h 30px!, gaptop 5px, gapright 6px");
this.add(btnRandom, "w 50%-3px, h 30px!, gaptop 5px");
updateDeckUrlPanelVisibility();
if (isShowing()) {
revalidate();
repaint();
}
}

private void initializeDeckUrlPanel() {
pnlDeckUrl = new JPanel(new MigLayout("insets 0, gap 0"));
pnlDeckUrl.setOpaque(false);
pnlDeckUrl.add(new FLabel.Builder().text(localizer.getMessage("lblDeckUrlLabel")).fontSize(12).fontStyle(Font.BOLD).build(),
"h " + FTextField.HEIGHT + "px!, gapright 6px");
txtDeckUrl = new FTextField.Builder().build();
txtDeckUrl.addActionListener(e -> loadDeckFromUrl());
pnlDeckUrl.add(txtDeckUrl, "growx, pushx, h " + FTextField.HEIGHT + "px!, gapright 6px");
btnReloadUrl = new FLabel.ButtonBuilder().text(localizer.getMessage("lblReload")).fontSize(14).build();
btnReloadUrl.setCommand(this::loadDeckFromUrl);
pnlDeckUrl.add(btnReloadUrl, "h " + FTextField.HEIGHT + "px!, w pref!");
}

private void updateDeckUrlPanelVisibility() {
if (pnlDeckUrl != null) {
pnlDeckUrl.setVisible(selectedDeckType == DeckType.PROVIDED_DECK_URL);
}
}

private void syncUrlFieldWithSelectedDeck() {
if (txtDeckUrl == null || selectedDeckType != DeckType.PROVIDED_DECK_URL) {
return;
}
final DeckProxy selected = lstDecks.getSelectedItem();
if (selected != null && selected.getSourceUrl() != null) {
txtDeckUrl.setText(selected.getSourceUrl());
}
}

private void handleDeckSelection() {
if (updatingDeckPool) {
return;
}
syncUrlFieldWithSelectedDeck();
if (deckSelectionCommand != null) {
deckSelectionCommand.run();
}
}

private void loadDeckFromUrl() {
if (txtDeckUrl == null) {
return;
}
final String deckUrl = txtDeckUrl.getText().trim();
if (deckUrl.isBlank()) {
return;
}

setDeckUrlLoading(true);
FThreads.invokeInBackgroundThread(() -> {
try {
final DeckProxy deck = DeckUrlLoader.load(deckUrl);
FThreads.invokeInEdtLater(() -> {
lastLoadedUrlDeckName = deck.toString();
if (selectedDeckType == DeckType.PROVIDED_DECK_URL) {
refreshDecksList(DeckType.PROVIDED_DECK_URL, true, null);
}
setDeckUrlLoading(false);
});
} catch (final IOException ex) {
FThreads.invokeInEdtLater(() -> {
setDeckUrlLoading(false);
FOptionPane.showErrorDialog(ex.getMessage(), localizer.getMessage("lblUnableToLoadDeckUrl"));
});
}
});
}

private void setDeckUrlLoading(final boolean loading) {
txtDeckUrl.setEnabled(!loading);
btnReloadUrl.setEnabled(!loading);
btnRandom.setEnabled(!loading);
if (loading) {
btnReloadUrl.setText(localizer.getMessage("lblLoadingEllipsis"));
} else {
btnReloadUrl.setText(localizer.getMessage("lblReload"));
}
}

public final boolean isAi() {
return isAi;
}
Expand Down Expand Up @@ -650,9 +767,13 @@ private void refreshDecksList(final DeckType deckType, final boolean forceRefres
case NET_ARCHIVE_BLOCK_DECK:
updateNetArchiveBlockDecks();
break;
case PROVIDED_DECK_URL:
updateProvidedDeckUrl();
break;
default:
break; //other deck types not currently supported here
}
updateDeckUrlPanelVisibility();
}

private final String SELECTED_DECK_DELIMITER = "::";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ private FDeckChooser createDeckChooser(final GameType type, final int iSlot, fin
final GameType gameType = forCommander ? type : GameType.Constructed;
final FDeckChooser fdc = new FDeckChooser(null, ai, gameType, forCommander);
fdc.initialize(prefKey, deckType);
fdc.getLstDecks().setSelectCommand(() -> selectMainDeck(fdc, iSlot, forCommander));
fdc.setDeckSelectionCommand(() -> selectMainDeck(fdc, iSlot, forCommander));
return fdc;
});
}
Expand Down
21 changes: 21 additions & 0 deletions forge-gui/res/languages/de-DE.properties
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,27 @@ lblNetArchivePioneerDecks=Netz-Archiv Pioneer-Decks
lblNetArchiveLegacyDecks=Netz-Archiv Legacy-Decks
lblNetArchiveVintageDecks=Netz-Archiv Vintage-Decks
lblNetArchiveBlockDecks=Netz-Archiv Block-Decks
lblProvideDeckUrl=Deck-URL angeben
lblDeckUrlLabel=URL:
lblReload=Neu laden
lblLoadingEllipsis=Lädt...
lblUnableToLoadDeckUrl=Deck-URL konnte nicht geladen werden
lblOnlySupportedDeckUrls=Derzeit werden nur Moxfield- und Archidekt-Deck-URLs unterstützt.
lblOnlyMoxfieldDeckUrlsSupported=Derzeit werden nur Moxfield-Deck-URLs unterstützt.
lblMoxfieldUnexpectedResponse=Moxfield hat eine unerwartete Antwort zurückgegeben.
lblNoPlayableCardsInMoxfieldDeck=Im Moxfield-Deck wurden keine spielbaren Karten gefunden.
lblUrlDeck=URL-Deck
lblMoxfieldDeck=Moxfield-Deck
lblMoxfieldCardNotFound=Forge konnte diese Moxfield-Karte nicht in der Datenbank finden: {0}
lblCouldNotFindMoxfieldDeckId=In der URL konnte keine Moxfield-Deck-ID gefunden werden.
lblArchidektUnexpectedResponse=Archidekt hat eine unerwartete Antwort zurückgegeben.
lblNoPlayableCardsInArchidektDeck=Im Archidekt-Deck wurden keine spielbaren Karten gefunden.
lblArchidektDeck=Archidekt-Deck
lblArchidektCardNotFound=Forge konnte diese Archidekt-Karte nicht in der Datenbank finden: {0}
lblCouldNotFindArchidektDeckId=In der URL konnte keine Archidekt-Deck-ID gefunden werden.
lblInvalidDeckUrl=Ungültige Deck-URL.
lblDeckUrlHttpRequestFailed=Die Anfrage an {0} ist mit HTTP {1} fehlgeschlagen.
lblMoxfieldHttpRequestFailed=Die Moxfield-Anfrage ist mit HTTP {0} fehlgeschlagen.
lblNetArchivePauperDecks=Netz-Archiv Pauper-Decks
#VSubmenuTutorial
lblTutorial=Tutorial
Expand Down
Loading