mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +00:00
1740 lines
69 KiB
Java
1740 lines
69 KiB
Java
package forge.deck;
|
|
|
|
import com.badlogic.gdx.Input.Keys;
|
|
import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
|
|
import com.badlogic.gdx.math.Vector2;
|
|
import com.google.common.base.Predicate;
|
|
import com.google.common.base.Predicates;
|
|
import com.google.common.base.Supplier;
|
|
import com.google.common.collect.ImmutableList;
|
|
import forge.Forge;
|
|
import forge.Forge.KeyInputAdapter;
|
|
import forge.Graphics;
|
|
import forge.assets.*;
|
|
import forge.card.*;
|
|
import forge.deck.io.DeckPreferences;
|
|
import forge.item.IPaperCard;
|
|
import forge.item.PaperCard;
|
|
import forge.itemmanager.CardManager;
|
|
import forge.itemmanager.ColumnDef;
|
|
import forge.itemmanager.ItemColumn;
|
|
import forge.itemmanager.ItemManager.ContextMenuBuilder;
|
|
import forge.itemmanager.ItemManagerConfig;
|
|
import forge.itemmanager.filters.ItemFilter;
|
|
import forge.limited.BoosterDraft;
|
|
import forge.menu.FCheckBoxMenuItem;
|
|
import forge.menu.FDropDownMenu;
|
|
import forge.menu.FMenuItem;
|
|
import forge.menu.FPopupMenu;
|
|
import forge.model.FModel;
|
|
import forge.planarconquest.ConquestUtil;
|
|
import forge.properties.ForgePreferences.FPref;
|
|
import forge.quest.data.QuestPreferences.QPref;
|
|
import forge.screens.FScreen;
|
|
import forge.screens.TabPageScreen;
|
|
import forge.toolbox.*;
|
|
import forge.toolbox.FEvent.FEventHandler;
|
|
import forge.toolbox.FEvent.FEventType;
|
|
import forge.util.Callback;
|
|
import forge.util.ItemPool;
|
|
import forge.util.Lang;
|
|
import forge.util.Utils;
|
|
import forge.util.storage.IStorage;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
import java.util.*;
|
|
import java.util.Map.Entry;
|
|
|
|
public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
|
public static FSkinImage MAIN_DECK_ICON = FSkinImage.DECKLIST;
|
|
public static FSkinImage SIDEBOARD_ICON = FSkinImage.FLASHBACK;
|
|
private static final float HEADER_HEIGHT = Math.round(Utils.AVG_FINGER_HEIGHT * 0.8f);
|
|
|
|
public enum EditorType {
|
|
Constructed(new DeckController<Deck>(FModel.getDecks().getConstructed(), new Supplier<Deck>() {
|
|
@Override
|
|
public Deck get() {
|
|
return new Deck();
|
|
}
|
|
}), null),
|
|
Draft(new DeckController<DeckGroup>(FModel.getDecks().getDraft(), new Supplier<DeckGroup>() {
|
|
@Override
|
|
public DeckGroup get() {
|
|
return new DeckGroup("");
|
|
}
|
|
}), null),
|
|
Sealed(new DeckController<DeckGroup>(FModel.getDecks().getSealed(), new Supplier<DeckGroup>() {
|
|
@Override
|
|
public DeckGroup get() {
|
|
return new DeckGroup("");
|
|
}
|
|
}), null),
|
|
Winston(new DeckController<DeckGroup>(FModel.getDecks().getWinston(), new Supplier<DeckGroup>() {
|
|
@Override
|
|
public DeckGroup get() {
|
|
return new DeckGroup("");
|
|
}
|
|
}), null),
|
|
Commander(new DeckController<Deck>(FModel.getDecks().getCommander(), new Supplier<Deck>() {
|
|
@Override
|
|
public Deck get() {
|
|
return new Deck();
|
|
}
|
|
}), null),
|
|
TinyLeaders(new DeckController<Deck>(FModel.getDecks().getTinyLeaders(), new Supplier<Deck>() {
|
|
@Override
|
|
public Deck get() {
|
|
return new Deck();
|
|
}
|
|
}), DeckFormat.TinyLeaders.isLegalCardPredicate()),
|
|
Brawl(new DeckController<Deck>(FModel.getDecks().getBrawl(), new Supplier<Deck>() {
|
|
@Override
|
|
public Deck get() {
|
|
return new Deck();
|
|
}
|
|
}), DeckFormat.Brawl.isLegalCardPredicate()),
|
|
Archenemy(new DeckController<Deck>(FModel.getDecks().getScheme(), new Supplier<Deck>() {
|
|
@Override
|
|
public Deck get() {
|
|
return new Deck();
|
|
}
|
|
}), null),
|
|
Planechase(new DeckController<Deck>(FModel.getDecks().getPlane(), new Supplier<Deck>() {
|
|
@Override
|
|
public Deck get() {
|
|
return new Deck();
|
|
}
|
|
}), null),
|
|
Quest(new DeckController<Deck>(null, new Supplier<Deck>() { //delay setting root folder until quest loaded
|
|
@Override
|
|
public Deck get() {
|
|
return new Deck();
|
|
}
|
|
}), null),
|
|
QuestDraft(new DeckController<DeckGroup>(null, new Supplier<DeckGroup>() { //delay setting root folder until quest loaded
|
|
@Override
|
|
public DeckGroup get() {
|
|
return new DeckGroup("");
|
|
}
|
|
}), null),
|
|
PlanarConquest(new DeckController<Deck>(null, new Supplier<Deck>() { //delay setting root folder until conquest loaded
|
|
@Override
|
|
public Deck get() {
|
|
return new Deck();
|
|
}
|
|
}), null);
|
|
|
|
private final DeckController<? extends DeckBase> controller;
|
|
private final Predicate<PaperCard> cardFilter;
|
|
|
|
public DeckController<? extends DeckBase> getController() {
|
|
return controller;
|
|
}
|
|
|
|
private EditorType(DeckController<? extends DeckBase> controller0, Predicate<PaperCard> cardFilter0) {
|
|
controller = controller0;
|
|
cardFilter = cardFilter0;
|
|
}
|
|
|
|
private ItemPool<PaperCard> applyCardFilter(ItemPool<PaperCard> cardPool, Predicate<PaperCard> additionalFilter) {
|
|
Predicate<PaperCard> filter = cardFilter;
|
|
if (filter == null) {
|
|
filter = additionalFilter;
|
|
if (filter == null) {
|
|
return cardPool;
|
|
}
|
|
}
|
|
else if (additionalFilter != null) {
|
|
filter = Predicates.and(filter, additionalFilter);
|
|
}
|
|
|
|
ItemPool<PaperCard> filteredPool = new ItemPool<PaperCard>(PaperCard.class);
|
|
for (Entry<PaperCard, Integer> entry : cardPool) {
|
|
if (filter.apply(entry.getKey())) {
|
|
filteredPool.add(entry.getKey(), entry.getValue());
|
|
}
|
|
}
|
|
return filteredPool;
|
|
}
|
|
}
|
|
|
|
private static DeckEditorPage[] getPages(EditorType editorType) {
|
|
switch (editorType) {
|
|
default:
|
|
case Constructed:
|
|
return new DeckEditorPage[] {
|
|
new CatalogPage(ItemManagerConfig.CARD_CATALOG),
|
|
new DeckSectionPage(DeckSection.Main),
|
|
new DeckSectionPage(DeckSection.Sideboard)
|
|
};
|
|
case Draft:
|
|
return new DeckEditorPage[] {
|
|
new DraftPackPage(),
|
|
new DeckSectionPage(DeckSection.Main),
|
|
new DeckSectionPage(DeckSection.Sideboard, ItemManagerConfig.DRAFT_POOL)
|
|
};
|
|
case Sealed:
|
|
return new DeckEditorPage[] {
|
|
new DeckSectionPage(DeckSection.Main),
|
|
new DeckSectionPage(DeckSection.Sideboard, ItemManagerConfig.SEALED_POOL)
|
|
};
|
|
case Commander:
|
|
case TinyLeaders:
|
|
case Brawl:
|
|
return new DeckEditorPage[] {
|
|
new CatalogPage(ItemManagerConfig.CARD_CATALOG),
|
|
new DeckSectionPage(DeckSection.Main),
|
|
new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.COMMANDER_SECTION),
|
|
new DeckSectionPage(DeckSection.Sideboard)
|
|
};
|
|
case Archenemy:
|
|
return new DeckEditorPage[] {
|
|
new CatalogPage(ItemManagerConfig.SCHEME_POOL),
|
|
new DeckSectionPage(DeckSection.Schemes, ItemManagerConfig.SCHEME_DECK_EDITOR)
|
|
};
|
|
case Planechase:
|
|
return new DeckEditorPage[] {
|
|
new CatalogPage(ItemManagerConfig.PLANAR_POOL),
|
|
new DeckSectionPage(DeckSection.Planes, ItemManagerConfig.PLANAR_DECK_EDITOR)
|
|
};
|
|
case Quest:
|
|
return new DeckEditorPage[] {
|
|
new CatalogPage(ItemManagerConfig.QUEST_EDITOR_POOL, "Inventory", FSkinImage.QUEST_BOX),
|
|
new DeckSectionPage(DeckSection.Main, ItemManagerConfig.QUEST_DECK_EDITOR),
|
|
new DeckSectionPage(DeckSection.Sideboard, ItemManagerConfig.QUEST_DECK_EDITOR)
|
|
};
|
|
case QuestDraft:
|
|
return new DeckEditorPage[] {
|
|
new DraftPackPage(),
|
|
new DeckSectionPage(DeckSection.Main),
|
|
new DeckSectionPage(DeckSection.Sideboard, ItemManagerConfig.DRAFT_POOL)
|
|
};
|
|
case PlanarConquest:
|
|
return new DeckEditorPage[] {
|
|
new CatalogPage(ItemManagerConfig.CONQUEST_COLLECTION, "Collection", FSkinImage.SPELLBOOK),
|
|
new DeckSectionPage(DeckSection.Main, ItemManagerConfig.CONQUEST_DECK_EDITOR, "Deck", FSkinImage.DECKLIST),
|
|
new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.COMMANDER_SECTION)
|
|
};
|
|
}
|
|
}
|
|
|
|
private final EditorType editorType;
|
|
private Deck deck;
|
|
private CatalogPage catalogPage;
|
|
private DeckSectionPage mainDeckPage;
|
|
private DeckSectionPage sideboardPage;
|
|
private DeckSectionPage commanderPage;
|
|
private FEventHandler saveHandler;
|
|
|
|
protected final DeckHeader deckHeader = add(new DeckHeader());
|
|
protected final FLabel lblName = deckHeader.add(new FLabel.Builder().font(FSkinFont.get(16)).insets(new Vector2(Utils.scale(5), 0)).build());
|
|
private final FLabel btnSave = deckHeader.add(new FLabel.Builder().icon(FSkinImage.SAVE).align(HAlignment.CENTER).pressedColor(Header.BTN_PRESSED_COLOR).build());
|
|
private final FLabel btnMoreOptions = deckHeader.add(new FLabel.Builder().text("...").font(FSkinFont.get(20)).align(HAlignment.CENTER).pressedColor(Header.BTN_PRESSED_COLOR).build());
|
|
|
|
public FDeckEditor(EditorType editorType0, DeckProxy editDeck, boolean showMainDeck) {
|
|
this(editorType0, editDeck.getName(), editDeck.getPath(), null, showMainDeck);
|
|
}
|
|
public FDeckEditor(EditorType editorType0, String editDeckName, boolean showMainDeck) {
|
|
this(editorType0, editDeckName, "", null, showMainDeck);
|
|
}
|
|
public FDeckEditor(EditorType editorType0, Deck newDeck, boolean showMainDeck) {
|
|
this(editorType0, "", "", newDeck, showMainDeck);
|
|
}
|
|
private FDeckEditor(EditorType editorType0, String editDeckName, String editDeckPath, Deck newDeck, boolean showMainDeck) {
|
|
super(getPages(editorType0));
|
|
|
|
editorType = editorType0;
|
|
editorType.getController().editor = this;
|
|
|
|
//cache specific pages
|
|
for (TabPage<FDeckEditor> tabPage : tabPages) {
|
|
if (tabPage instanceof CatalogPage) {
|
|
catalogPage = (CatalogPage) tabPage;
|
|
}
|
|
else if (tabPage instanceof DeckSectionPage) {
|
|
DeckSectionPage deckSectionPage = (DeckSectionPage) tabPage;
|
|
switch (deckSectionPage.deckSection) {
|
|
case Main:
|
|
case Schemes:
|
|
case Planes:
|
|
mainDeckPage = deckSectionPage;
|
|
break;
|
|
case Sideboard:
|
|
sideboardPage = deckSectionPage;
|
|
break;
|
|
case Commander:
|
|
commanderPage = deckSectionPage;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (editorType) {
|
|
case Sealed:
|
|
//if opening brand new sealed deck, show sideboard (card pool) by default
|
|
if (!showMainDeck) {
|
|
setSelectedPage(sideboardPage);
|
|
}
|
|
break;
|
|
case Draft:
|
|
case QuestDraft:
|
|
break;
|
|
default:
|
|
//if editing existing non-limited deck, show main deck by default
|
|
if (showMainDeck) {
|
|
setSelectedPage(mainDeckPage);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (StringUtils.isEmpty(editDeckName)) {
|
|
if (editorType == EditorType.Draft || editorType == EditorType.QuestDraft) {
|
|
//hide deck header on while drafting
|
|
setDeck(new Deck());
|
|
deckHeader.setVisible(false);
|
|
}
|
|
else {
|
|
if (newDeck == null) {
|
|
editorType.getController().newModel();
|
|
}
|
|
else {
|
|
editorType.getController().setDeck(newDeck);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (editorType == EditorType.Draft || editorType == EditorType.QuestDraft) {
|
|
tabPages[0].hideTab(); //hide Draft Pack page if editing existing draft deck
|
|
}
|
|
editorType.getController().load(editDeckPath, editDeckName);
|
|
}
|
|
|
|
btnSave.setCommand(new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
save(null);
|
|
}
|
|
});
|
|
btnMoreOptions.setCommand(new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
FPopupMenu menu = new FPopupMenu() {
|
|
@Override
|
|
protected void buildMenu() {
|
|
addItem(new FMenuItem("Add Basic Lands", FSkinImage.LAND, new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
CardEdition defaultLandSet;
|
|
switch (editorType) {
|
|
case Draft:
|
|
case Sealed:
|
|
case QuestDraft:
|
|
//suggest a random set from the ones used in the limited card pool that have all basic lands
|
|
Set<CardEdition> availableEditionCodes = new HashSet<>();
|
|
for (PaperCard p : deck.getAllCardsInASinglePool().toFlatList()) {
|
|
availableEditionCodes.add(FModel.getMagicDb().getEditions().get(p.getEdition()));
|
|
}
|
|
defaultLandSet = CardEdition.Predicates.getRandomSetWithAllBasicLands(availableEditionCodes);
|
|
break;
|
|
case Quest:
|
|
defaultLandSet = FModel.getQuest().getDefaultLandSet();
|
|
break;
|
|
default:
|
|
defaultLandSet = DeckProxy.getDefaultLandSet(deck);
|
|
break;
|
|
}
|
|
AddBasicLandsDialog dialog = new AddBasicLandsDialog(deck, defaultLandSet, new Callback<CardPool>() {
|
|
@Override
|
|
public void run(CardPool landsToAdd) {
|
|
getMainDeckPage().addCards(landsToAdd);
|
|
}
|
|
});
|
|
dialog.show();
|
|
setSelectedPage(getMainDeckPage()); //select main deck page if needed so main deck is visible below dialog
|
|
}
|
|
}));
|
|
if (!isLimitedEditor()) {
|
|
addItem(new FMenuItem("Import from Clipboard", FSkinImage.OPEN, new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
FDeckImportDialog dialog = new FDeckImportDialog(!deck.isEmpty(), new Callback<Deck>() {
|
|
@Override
|
|
public void run(Deck importedDeck) {
|
|
getMainDeckPage().setCards(importedDeck.getMain());
|
|
if (getSideboardPage() != null) {
|
|
getSideboardPage().setCards(importedDeck.getOrCreate(DeckSection.Sideboard));
|
|
}
|
|
if (getCommanderPage() != null) {
|
|
getCommanderPage().setCards(importedDeck.getOrCreate(DeckSection.Commander));
|
|
}
|
|
}
|
|
});
|
|
dialog.show();
|
|
setSelectedPage(getMainDeckPage()); //select main deck page if needed so main deck if visible below dialog
|
|
}
|
|
}));
|
|
addItem(new FMenuItem("Save As...", FSkinImage.SAVEAS, new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
String defaultName = editorType.getController().getNextAvailableName();
|
|
FOptionPane.showInputDialog("Enter name for new copy of deck", defaultName, new Callback<String>() {
|
|
@Override
|
|
public void run(String result) {
|
|
if (!StringUtils.isEmpty(result)) {
|
|
editorType.getController().saveAs(result);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}));
|
|
}
|
|
if (allowRename()) {
|
|
addItem(new FMenuItem("Rename Deck", FSkinImage.EDIT, new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
FOptionPane.showInputDialog("Enter new name for deck", deck.getName(), new Callback<String>() {
|
|
@Override
|
|
public void run(String result) {
|
|
editorType.getController().rename(result);
|
|
}
|
|
});
|
|
}
|
|
}));
|
|
}
|
|
if (allowDelete()) {
|
|
addItem(new FMenuItem("Delete Deck", FSkinImage.DELETE, new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
FOptionPane.showConfirmDialog(
|
|
"Are you sure you want to delete '" + deck.getName() + "'?",
|
|
"Delete Deck", "Delete", "Cancel", false, new Callback<Boolean>() {
|
|
@Override
|
|
public void run(Boolean result) {
|
|
if (result) {
|
|
editorType.getController().delete();
|
|
Forge.back();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}));
|
|
}
|
|
addItem(new FMenuItem("Copy to Clipboard", new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
FDeckViewer.copyDeckToClipboard(deck);
|
|
}
|
|
}));
|
|
((DeckEditorPage)getSelectedPage()).buildDeckMenu(this);
|
|
}
|
|
};
|
|
menu.show(btnMoreOptions, 0, btnMoreOptions.getHeight());
|
|
}
|
|
});
|
|
}
|
|
|
|
protected boolean allowRename() {
|
|
return true;
|
|
}
|
|
protected boolean allowDelete() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected void doLayout(float startY, float width, float height) {
|
|
if (deckHeader.isVisible()) {
|
|
deckHeader.setBounds(0, startY, width, HEADER_HEIGHT);
|
|
startY += HEADER_HEIGHT;
|
|
}
|
|
super.doLayout(startY, width, height);
|
|
}
|
|
|
|
public EditorType getEditorType() {
|
|
return editorType;
|
|
}
|
|
|
|
public Deck getDeck() {
|
|
return deck;
|
|
}
|
|
public void setDeck(Deck deck0) {
|
|
if (deck == deck0) { return; }
|
|
deck = deck0;
|
|
if (deck == null) { return; }
|
|
|
|
//reinitialize tab pages when deck changes
|
|
for (TabPage<FDeckEditor> tabPage : tabPages) {
|
|
((DeckEditorPage)tabPage).initialize();
|
|
}
|
|
}
|
|
|
|
protected CatalogPage getCatalogPage() {
|
|
return catalogPage;
|
|
}
|
|
|
|
protected DeckSectionPage getMainDeckPage() {
|
|
return mainDeckPage;
|
|
}
|
|
|
|
protected DeckSectionPage getSideboardPage() {
|
|
return sideboardPage;
|
|
}
|
|
|
|
protected DeckSectionPage getCommanderPage() {
|
|
return commanderPage;
|
|
}
|
|
|
|
protected BoosterDraft getDraft() {
|
|
return null;
|
|
}
|
|
|
|
private enum CardLimit {
|
|
Singleton,
|
|
Default,
|
|
None
|
|
}
|
|
private CardLimit getCardLimit() {
|
|
switch (editorType) {
|
|
case Constructed:
|
|
case Planechase:
|
|
case Archenemy:
|
|
case Quest:
|
|
default:
|
|
if (FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
|
return CardLimit.Default;
|
|
}
|
|
return CardLimit.None; //if not enforcing deck legality, don't enforce default limit
|
|
case Draft:
|
|
case Sealed:
|
|
case Winston:
|
|
case QuestDraft:
|
|
return CardLimit.None;
|
|
case Commander:
|
|
case TinyLeaders:
|
|
case Brawl:
|
|
case PlanarConquest:
|
|
return CardLimit.Singleton;
|
|
}
|
|
}
|
|
|
|
public void setSaveHandler(FEventHandler saveHandler0) {
|
|
saveHandler = saveHandler0;
|
|
}
|
|
|
|
protected void save(final Callback<Boolean> callback) {
|
|
if (StringUtils.isEmpty(deck.getName())) {
|
|
List<PaperCard> commanders = deck.getCommanders(); //use commander name as default deck name
|
|
String initialInput = Lang.joinHomogenous(commanders);
|
|
FOptionPane.showInputDialog("Enter name for new deck", initialInput, new Callback<String>() {
|
|
@Override
|
|
public void run(String result) {
|
|
if (StringUtils.isEmpty(result)) { return; }
|
|
|
|
editorType.getController().saveAs(result);
|
|
if (callback != null) {
|
|
callback.run(true);
|
|
}
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
editorType.getController().save();
|
|
if (callback != null) {
|
|
callback.run(true);
|
|
}
|
|
}
|
|
|
|
private final static ImmutableList<String> onCloseOptions = ImmutableList.of("Save", "Don't Save", "Cancel");
|
|
|
|
@Override
|
|
public void onClose(final Callback<Boolean> canCloseCallback) {
|
|
if (editorType.getController().isSaved() || canCloseCallback == null) {
|
|
super.onClose(canCloseCallback); //can skip prompt if draft saved
|
|
return;
|
|
}
|
|
FOptionPane.showOptionDialog("Save changes to current deck?", "",
|
|
FOptionPane.QUESTION_ICON, onCloseOptions, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == 0) {
|
|
save(canCloseCallback);
|
|
}
|
|
else if (result == 1) {
|
|
editorType.getController().reload(); //reload if not saving changes
|
|
canCloseCallback.run(true);
|
|
}
|
|
else {
|
|
canCloseCallback.run(false);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public boolean keyDown(int keyCode) {
|
|
switch (keyCode) {
|
|
case Keys.BACK:
|
|
return true; //suppress Back button so it's not bumped while editing deck
|
|
case Keys.S: //save deck on Ctrl+S
|
|
if (KeyInputAdapter.isCtrlKeyDown()) {
|
|
save(null);
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
return super.keyDown(keyCode);
|
|
}
|
|
|
|
@Override
|
|
public FScreen getLandscapeBackdropScreen() {
|
|
return null; //never use backdrop for editor
|
|
}
|
|
|
|
private boolean isLimitedEditor() {
|
|
switch (editorType) {
|
|
case Draft:
|
|
case Sealed:
|
|
case Winston:
|
|
case QuestDraft:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected Map<ColumnDef, ItemColumn> getColOverrides(ItemManagerConfig config) {
|
|
return null;
|
|
}
|
|
|
|
protected class DeckHeader extends FContainer {
|
|
private DeckHeader() {
|
|
setHeight(HEADER_HEIGHT);
|
|
}
|
|
|
|
@Override
|
|
public void drawBackground(Graphics g) {
|
|
g.fillRect(Header.BACK_COLOR, 0, 0, getWidth(), HEADER_HEIGHT);
|
|
}
|
|
|
|
@Override
|
|
public void drawOverlay(Graphics g) {
|
|
float y = HEADER_HEIGHT - Header.LINE_THICKNESS / 2;
|
|
g.drawLine(Header.LINE_THICKNESS, Header.LINE_COLOR, 0, y, getWidth(), y);
|
|
}
|
|
|
|
@Override
|
|
protected void doLayout(float width, float height) {
|
|
float x = 0;
|
|
lblName.setBounds(0, 0, width - 2 * height, height);
|
|
x += lblName.getWidth();
|
|
btnSave.setBounds(x, 0, height, height);
|
|
x += height;
|
|
btnMoreOptions.setBounds(x, 0, height, height);
|
|
}
|
|
}
|
|
|
|
protected static abstract class DeckEditorPage extends TabPage<FDeckEditor> {
|
|
protected DeckEditorPage(String caption0, FImage icon0) {
|
|
super(caption0, icon0);
|
|
}
|
|
|
|
protected void buildDeckMenu(FPopupMenu menu) {
|
|
}
|
|
|
|
protected abstract void initialize();
|
|
|
|
@Override
|
|
public boolean fling(float velocityX, float velocityY) {
|
|
return false; //prevent left/right swipe to change tabs since it doesn't play nice with item managers
|
|
}
|
|
}
|
|
|
|
protected static abstract class CardManagerPage extends DeckEditorPage {
|
|
private final ItemManagerConfig config;
|
|
protected final CardManager cardManager = add(new CardManager(false));
|
|
|
|
protected CardManagerPage(ItemManagerConfig config0, String caption0, FImage icon0) {
|
|
super(caption0, icon0);
|
|
config = config0;
|
|
cardManager.setItemActivateHandler(new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
onCardActivated(cardManager.getSelectedItem());
|
|
}
|
|
});
|
|
cardManager.setContextMenuBuilder(new ContextMenuBuilder<PaperCard>() {
|
|
@Override
|
|
public void buildMenu(final FDropDownMenu menu, final PaperCard card) {
|
|
CardManagerPage.this.buildMenu(menu, card);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected void initialize() {
|
|
cardManager.setup(config, parentScreen.getColOverrides(config));
|
|
}
|
|
|
|
protected boolean canAddCards() {
|
|
return true;
|
|
}
|
|
|
|
public void addCard(PaperCard card) {
|
|
addCard(card, 1);
|
|
}
|
|
public void addCard(PaperCard card, int qty) {
|
|
if (canAddCards()) {
|
|
cardManager.addItem(card, qty);
|
|
parentScreen.getEditorType().getController().notifyModelChanged();
|
|
updateCaption();
|
|
}
|
|
}
|
|
|
|
public void addCards(Iterable<Entry<PaperCard, Integer>> cards) {
|
|
if (canAddCards()) {
|
|
cardManager.addItems(cards);
|
|
parentScreen.getEditorType().getController().notifyModelChanged();
|
|
updateCaption();
|
|
}
|
|
}
|
|
|
|
public void removeCard(PaperCard card) {
|
|
removeCard(card, 1);
|
|
}
|
|
public void removeCard(PaperCard card, int qty) {
|
|
cardManager.removeItem(card, qty);
|
|
parentScreen.getEditorType().getController().notifyModelChanged();
|
|
updateCaption();
|
|
}
|
|
|
|
public void setCards(CardPool cards) {
|
|
cardManager.setItems(cards);
|
|
parentScreen.getEditorType().getController().notifyModelChanged();
|
|
updateCaption();
|
|
}
|
|
|
|
protected void updateCaption() {
|
|
}
|
|
|
|
protected abstract void onCardActivated(PaperCard card);
|
|
protected abstract void buildMenu(final FDropDownMenu menu, final PaperCard card);
|
|
|
|
private static final List<String> limitExceptions = Arrays.asList(
|
|
new String[]{"Relentless Rats", "Shadowborn Apostle"});
|
|
|
|
private ItemPool<PaperCard> getAllowedAdditions(Iterable<Entry<PaperCard, Integer>> itemsToAdd, boolean isAddSource) {
|
|
ItemPool<PaperCard> additions = new ItemPool<PaperCard>(cardManager.getGenericType());
|
|
CardLimit limit = parentScreen.getCardLimit();
|
|
Deck deck = parentScreen.getDeck();
|
|
|
|
for (Entry<PaperCard, Integer> itemEntry : itemsToAdd) {
|
|
PaperCard card = itemEntry.getKey();
|
|
|
|
int max;
|
|
if (deck == null || card == null) {
|
|
max = Integer.MAX_VALUE;
|
|
}
|
|
else if (limit == CardLimit.None || card.getRules().getType().isBasic() || limitExceptions.contains(card.getName())) {
|
|
max = Integer.MAX_VALUE;
|
|
if (parentScreen.isLimitedEditor() && !isAddSource) {
|
|
//prevent adding more than is in other pool when editing limited decks
|
|
if (parentScreen.getMainDeckPage() == this) {
|
|
max = deck.get(DeckSection.Sideboard).count(card);
|
|
}
|
|
else if (parentScreen.getSideboardPage() == this) {
|
|
max = deck.get(DeckSection.Main).count(card);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
max = (limit == CardLimit.Singleton ? 1 : FModel.getPreferences().getPrefInt(FPref.DECK_DEFAULT_CARD_LIMIT));
|
|
max -= deck.getMain().count(card);
|
|
if (deck.has(DeckSection.Sideboard)) {
|
|
max -= deck.get(DeckSection.Sideboard).count(card);
|
|
}
|
|
if (deck.has(DeckSection.Commander)) {
|
|
max -= deck.get(DeckSection.Commander).count(card);
|
|
}
|
|
if (deck.has(DeckSection.Planes)) {
|
|
max -= deck.get(DeckSection.Planes).count(card);
|
|
}
|
|
if (deck.has(DeckSection.Schemes)) {
|
|
max -= deck.get(DeckSection.Schemes).count(card);
|
|
}
|
|
}
|
|
|
|
int qty;
|
|
if (isAddSource) {
|
|
qty = itemEntry.getValue();
|
|
}
|
|
else if (parentScreen.getEditorType() == EditorType.Quest) {
|
|
//prevent adding more than is in quest inventory
|
|
qty = parentScreen.getCatalogPage().cardManager.getItemCount(card);
|
|
}
|
|
else {
|
|
//if not source of items being added, use max directly if unlimited pool
|
|
qty = max;
|
|
}
|
|
if (qty > max) {
|
|
qty = max;
|
|
}
|
|
if (qty > 0) {
|
|
additions.add(card, qty);
|
|
}
|
|
}
|
|
|
|
return additions;
|
|
}
|
|
|
|
private int getMaxMoveQuantity(boolean isAddMenu, boolean isAddSource) {
|
|
ItemPool<PaperCard> selectedItemPool = cardManager.getSelectedItemPool();
|
|
if (isAddMenu) {
|
|
selectedItemPool = getAllowedAdditions(selectedItemPool, isAddSource);
|
|
}
|
|
if (selectedItemPool.isEmpty()) {
|
|
return 0;
|
|
}
|
|
int max = Integer.MAX_VALUE;
|
|
for (Entry<PaperCard, Integer> itemEntry : selectedItemPool) {
|
|
if (itemEntry.getValue() < max) {
|
|
max = itemEntry.getValue();
|
|
}
|
|
}
|
|
return max;
|
|
}
|
|
|
|
protected void addItem(FDropDownMenu menu, final String verb, String dest, FImage icon, boolean isAddMenu, boolean isAddSource, final Callback<Integer> callback) {
|
|
final int max = getMaxMoveQuantity(isAddMenu, isAddSource);
|
|
if (max == 0) { return; }
|
|
|
|
String label = verb;
|
|
if (!StringUtils.isEmpty(dest)) {
|
|
label += " " + dest;
|
|
}
|
|
menu.addItem(new FMenuItem(label, icon, new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
if (max == 1) {
|
|
callback.run(max);
|
|
}
|
|
else {
|
|
GuiChoose.getInteger(cardManager.getSelectedItem() + " - " + verb + " how many?", 1, max, 20, callback);
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
|
|
@Override
|
|
protected void doLayout(float width, float height) {
|
|
float x = 0;
|
|
if (Forge.isLandscapeMode()) { //add some horizontal padding in landscape mode
|
|
x = ItemFilter.PADDING;
|
|
width -= 2 * x;
|
|
}
|
|
cardManager.setBounds(x, 0, width, height);
|
|
}
|
|
}
|
|
|
|
protected static class CatalogPage extends CardManagerPage {
|
|
private boolean initialized, needRefreshWhenShown;
|
|
|
|
protected CatalogPage(ItemManagerConfig config) {
|
|
this(config, "Catalog", FSkinImage.FOLDER);
|
|
}
|
|
protected CatalogPage(ItemManagerConfig config, String caption0, FImage icon0) {
|
|
super(config, caption0, icon0);
|
|
}
|
|
|
|
@Override
|
|
protected void initialize() {
|
|
if (initialized) { return; } //prevent initializing more than once if deck changes
|
|
initialized = true;
|
|
|
|
super.initialize();
|
|
cardManager.setCaption(getItemManagerCaption());
|
|
|
|
if (!isVisible() && parentScreen.getEditorType() != EditorType.Quest) {
|
|
needRefreshWhenShown = true;
|
|
return; //delay refreshing while hidden unless for quest inventory
|
|
}
|
|
refresh();
|
|
}
|
|
|
|
@Override
|
|
protected boolean canAddCards() {
|
|
if (needRefreshWhenShown) { //ensure refreshed before cards added if hasn't been refreshed yet
|
|
needRefreshWhenShown = false;
|
|
refresh();
|
|
}
|
|
return !cardManager.isInfinite();
|
|
}
|
|
|
|
protected String getItemManagerCaption() {
|
|
switch (parentScreen.getEditorType()) {
|
|
case Archenemy:
|
|
return "Schemes";
|
|
case Planechase:
|
|
return "Planes";
|
|
default:
|
|
return "Cards";
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setVisible(boolean visible0) {
|
|
if (isVisible() == visible0) { return; }
|
|
|
|
super.setVisible(visible0);
|
|
if (visible0 && needRefreshWhenShown) {
|
|
needRefreshWhenShown = false;
|
|
refresh();
|
|
}
|
|
}
|
|
|
|
public void refresh() {
|
|
Predicate<PaperCard> additionalFilter = null;
|
|
final EditorType editorType = parentScreen.getEditorType();
|
|
switch (editorType) {
|
|
case Archenemy:
|
|
cardManager.setPool(ItemPool.createFrom(FModel.getMagicDb().getVariantCards().getAllCards(Predicates.compose(CardRulesPredicates.Presets.IS_SCHEME, PaperCard.FN_GET_RULES)), PaperCard.class), true);
|
|
break;
|
|
case Planechase:
|
|
cardManager.setPool(ItemPool.createFrom(FModel.getMagicDb().getVariantCards().getAllCards(Predicates.compose(CardRulesPredicates.Presets.IS_PLANE_OR_PHENOMENON, PaperCard.FN_GET_RULES)), PaperCard.class), true);
|
|
break;
|
|
case Quest:
|
|
final ItemPool<PaperCard> questPool = new ItemPool<PaperCard>(PaperCard.class);
|
|
questPool.addAll(FModel.getQuest().getCards().getCardpool());
|
|
// remove bottom cards that are in the deck from the card pool
|
|
questPool.removeAll(parentScreen.getDeck().getMain());
|
|
// remove sideboard cards from the catalog
|
|
questPool.removeAll(parentScreen.getDeck().getOrCreate(DeckSection.Sideboard));
|
|
cardManager.setPool(questPool);
|
|
break;
|
|
case PlanarConquest:
|
|
cardManager.setPool(ConquestUtil.getAvailablePool(parentScreen.getDeck()));
|
|
break;
|
|
case Commander:
|
|
case TinyLeaders:
|
|
case Brawl:
|
|
final List<PaperCard> commanders = parentScreen.getDeck().getCommanders();
|
|
if (commanders.isEmpty()) {
|
|
//if no commander set for deck, only show valid commanders
|
|
switch (editorType) {
|
|
case TinyLeaders:
|
|
case Commander:
|
|
additionalFilter = DeckFormat.Commander.isLegalCommanderPredicate();
|
|
break;
|
|
case Brawl:
|
|
additionalFilter = DeckFormat.Brawl.isLegalCommanderPredicate();
|
|
}
|
|
cardManager.setCaption("Commanders");
|
|
}
|
|
else {
|
|
//if a commander has been set, only show cards that match its color identity
|
|
switch (editorType) {
|
|
case TinyLeaders:
|
|
case Commander:
|
|
additionalFilter = DeckFormat.Commander.isLegalCardForCommanderOrLegalPartnerPredicate(commanders);
|
|
break;
|
|
case Brawl:
|
|
additionalFilter = DeckFormat.Brawl.isLegalCardForCommanderOrLegalPartnerPredicate(commanders);
|
|
}
|
|
cardManager.setCaption("Cards");
|
|
}
|
|
//fall through to below
|
|
default:
|
|
if (cardManager.getWantUnique()) {
|
|
cardManager.setPool(editorType.applyCardFilter(ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getUniqueCards(), PaperCard.class), additionalFilter), true);
|
|
}
|
|
else {
|
|
cardManager.setPool(editorType.applyCardFilter(ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(), PaperCard.class), additionalFilter), true);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onCardActivated(PaperCard card) {
|
|
if (canOnlyBePartnerCommander(card)) {
|
|
return; // don't auto-change commander unexpectedly
|
|
}
|
|
if (needsCommander()) {
|
|
setCommander(card); //handle special case of setting commander
|
|
return;
|
|
}
|
|
if (!cardManager.isInfinite()) {
|
|
removeCard(card);
|
|
}
|
|
parentScreen.getMainDeckPage().addCard(card);
|
|
}
|
|
|
|
private boolean needsCommander() {
|
|
return parentScreen.getCommanderPage() != null && parentScreen.getDeck().getCommanders().isEmpty();
|
|
}
|
|
|
|
private boolean canHavePartnerCommander() {
|
|
return parentScreen.getCommanderPage() != null && parentScreen.getDeck().getCommanders().size() == 1
|
|
&& parentScreen.getDeck().getCommanders().get(0).getRules().canBePartnerCommander();
|
|
}
|
|
|
|
private boolean canOnlyBePartnerCommander(final PaperCard card) {
|
|
if (parentScreen.getCommanderPage() == null) {
|
|
return false;
|
|
}
|
|
|
|
byte cmdCI = 0;
|
|
for (final PaperCard p : parentScreen.getDeck().getCommanders()) {
|
|
cmdCI |= p.getRules().getColorIdentity().getColor();
|
|
}
|
|
|
|
return !card.getRules().getColorIdentity().hasNoColorsExcept(cmdCI);
|
|
}
|
|
|
|
private void setCommander(PaperCard card) {
|
|
if (!cardManager.isInfinite()) {
|
|
removeCard(card);
|
|
}
|
|
CardPool newPool = new CardPool();
|
|
newPool.add(card);
|
|
parentScreen.getCommanderPage().setCards(newPool);
|
|
refresh(); //refresh so cards shown that match commander's color identity
|
|
}
|
|
|
|
private void setPartnerCommander(PaperCard card) {
|
|
if (!cardManager.isInfinite()) {
|
|
removeCard(card);
|
|
}
|
|
parentScreen.getCommanderPage().addCard(card, 1);
|
|
refresh(); //refresh so cards shown that match commander's color identity
|
|
}
|
|
|
|
@Override
|
|
protected void buildMenu(final FDropDownMenu menu, final PaperCard card) {
|
|
if (!needsCommander() && !canOnlyBePartnerCommander(card)) {
|
|
addItem(menu, "Add", "to " + parentScreen.getMainDeckPage().cardManager.getCaption(), parentScreen.getMainDeckPage().getIcon(), true, true, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
if (!cardManager.isInfinite()) {
|
|
removeCard(card, result);
|
|
}
|
|
parentScreen.getMainDeckPage().addCard(card, result);
|
|
}
|
|
});
|
|
if (parentScreen.getSideboardPage() != null) {
|
|
addItem(menu, "Add", "to Sideboard", parentScreen.getSideboardPage().getIcon(), true, true, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
if (!cardManager.isInfinite()) {
|
|
removeCard(card, result);
|
|
}
|
|
parentScreen.getSideboardPage().addCard(card, result);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
if (parentScreen.getCommanderPage() != null) {
|
|
boolean isLegalCommander;
|
|
if(parentScreen.editorType.equals(EditorType.Brawl)){
|
|
isLegalCommander = card.getRules().canBeBrawlCommander();
|
|
}else{
|
|
isLegalCommander = DeckFormat.Commander.isLegalCommander(card.getRules());
|
|
}
|
|
if (parentScreen.editorType != EditorType.PlanarConquest && isLegalCommander && !parentScreen.getCommanderPage().cardManager.getPool().contains(card)) {
|
|
addItem(menu, "Set", "as Commander", parentScreen.getCommanderPage().getIcon(), true, true, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
setCommander(card);
|
|
}
|
|
});
|
|
}
|
|
if (canHavePartnerCommander() && card.getRules().canBePartnerCommander()) {
|
|
addItem(menu, "Set", "as Partner Commander", parentScreen.getCommanderPage().getIcon(), true, true, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
setPartnerCommander(card);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
if (parentScreen.getEditorType() == EditorType.Constructed) {
|
|
//add option to add or remove card from favorites
|
|
final CardPreferences prefs = CardPreferences.getPrefs(card);
|
|
if (prefs.getStarCount() == 0) {
|
|
menu.addItem(new FMenuItem("Add to Favorites", FSkinImage.STAR_FILLED, new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
prefs.setStarCount(1);
|
|
CardPreferences.save();
|
|
}
|
|
}));
|
|
}
|
|
else {
|
|
menu.addItem(new FMenuItem("Remove from Favorites", FSkinImage.STAR_OUTINE, new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
prefs.setStarCount(0);
|
|
CardPreferences.save();
|
|
}
|
|
}));
|
|
}
|
|
|
|
//if card has more than one art option, add item to change user's preferred art
|
|
final List<PaperCard> artOptions = FModel.getMagicDb().getCommonCards().getAllCards(card.getName());
|
|
if (artOptions != null && artOptions.size() > 1) {
|
|
menu.addItem(new FMenuItem("Change Preferred Art", FSkinImage.SETTINGS, new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
//sort options so current option is on top and selected by default
|
|
List<PaperCard> sortedOptions = new ArrayList<PaperCard>();
|
|
sortedOptions.add(card);
|
|
for (PaperCard option : artOptions) {
|
|
if (option != card) {
|
|
sortedOptions.add(option);
|
|
}
|
|
}
|
|
GuiChoose.oneOrNone("Select preferred art for " + card.getName(), sortedOptions, new Callback<PaperCard>() {
|
|
@Override
|
|
public void run(PaperCard result) {
|
|
if (result != null) {
|
|
if (result != card) {
|
|
cardManager.replaceAll(card, result);
|
|
}
|
|
prefs.setPreferredArt(result.getEdition() + CardDb.NameSetSeparator + result.getArtIndex());
|
|
CardPreferences.save();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void buildDeckMenu(FPopupMenu menu) {
|
|
if (cardManager.getConfig().getShowUniqueCardsOption()) {
|
|
menu.addItem(new FCheckBoxMenuItem("Unique Cards Only", cardManager.getWantUnique(), new FEventHandler() {
|
|
@Override
|
|
public void handleEvent(FEvent e) {
|
|
boolean wantUnique = !cardManager.getWantUnique();
|
|
cardManager.setWantUnique(wantUnique);
|
|
refresh();
|
|
cardManager.getConfig().setUniqueCardsOnly(wantUnique);
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static class DeckSectionPage extends CardManagerPage {
|
|
private String captionPrefix;
|
|
private final DeckSection deckSection;
|
|
|
|
protected DeckSectionPage(DeckSection deckSection0) {
|
|
this(deckSection0, ItemManagerConfig.DECK_EDITOR);
|
|
}
|
|
protected DeckSectionPage(DeckSection deckSection0, ItemManagerConfig config) {
|
|
super(config, null, null);
|
|
|
|
deckSection = deckSection0;
|
|
switch (deckSection) {
|
|
default:
|
|
case Main:
|
|
captionPrefix = "Main";
|
|
cardManager.setCaption("Main Deck");
|
|
icon = MAIN_DECK_ICON;
|
|
break;
|
|
case Sideboard:
|
|
captionPrefix = "Side";
|
|
cardManager.setCaption("Sideboard");
|
|
icon = SIDEBOARD_ICON;
|
|
break;
|
|
case Commander:
|
|
captionPrefix = "Commander";
|
|
cardManager.setCaption("Commander");
|
|
icon = FSkinImage.COMMANDER;
|
|
break;
|
|
case Avatar:
|
|
captionPrefix = "Avatar";
|
|
cardManager.setCaption("Avatar");
|
|
icon = new FTextureRegionImage(FSkin.getAvatars().get(0));
|
|
break;
|
|
case Planes:
|
|
captionPrefix = "Planes";
|
|
cardManager.setCaption("Planes");
|
|
icon = FSkinImage.CHAOS;
|
|
break;
|
|
case Schemes:
|
|
captionPrefix = "Schemes";
|
|
cardManager.setCaption("Schemes");
|
|
icon = FSkinImage.POISON;
|
|
break;
|
|
}
|
|
}
|
|
protected DeckSectionPage(DeckSection deckSection0, ItemManagerConfig config, String caption0, FImage icon0) {
|
|
super(config, null, icon0);
|
|
|
|
deckSection = deckSection0;
|
|
captionPrefix = caption0;
|
|
cardManager.setCaption(caption0);
|
|
}
|
|
|
|
@Override
|
|
protected void initialize() {
|
|
super.initialize();
|
|
cardManager.setPool(parentScreen.getDeck().getOrCreate(deckSection));
|
|
updateCaption();
|
|
}
|
|
|
|
@Override
|
|
protected void updateCaption() {
|
|
if (deckSection == DeckSection.Commander) {
|
|
caption = captionPrefix; //don't display count for commander section since it won't be more than 1
|
|
}
|
|
else {
|
|
caption = captionPrefix + " (" + parentScreen.getDeck().get(deckSection).countAll() + ")";
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onCardActivated(PaperCard card) {
|
|
switch (deckSection) {
|
|
case Main:
|
|
case Planes:
|
|
case Schemes:
|
|
removeCard(card);
|
|
switch (parentScreen.getEditorType()) {
|
|
case Draft:
|
|
case Sealed:
|
|
case QuestDraft:
|
|
parentScreen.getSideboardPage().addCard(card);
|
|
break;
|
|
default:
|
|
if (parentScreen.getCatalogPage() != null) {
|
|
parentScreen.getCatalogPage().addCard(card);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case Sideboard:
|
|
removeCard(card);
|
|
parentScreen.getMainDeckPage().addCard(card);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void buildMenu(final FDropDownMenu menu, final PaperCard card) {
|
|
switch (deckSection) {
|
|
default:
|
|
case Main:
|
|
addItem(menu, "Add", null, FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
if (parentScreen.isLimitedEditor()) { //ensure card removed from sideboard before adding to main
|
|
parentScreen.getSideboardPage().removeCard(card, result);
|
|
}
|
|
else if (parentScreen.getEditorType() == EditorType.Quest) {
|
|
parentScreen.getCatalogPage().removeCard(card, result);
|
|
}
|
|
addCard(card, result);
|
|
}
|
|
});
|
|
if (!parentScreen.isLimitedEditor()) {
|
|
addItem(menu, "Remove", null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
removeCard(card, result);
|
|
if (parentScreen.getCatalogPage() != null) {
|
|
parentScreen.getCatalogPage().addCard(card, result);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if (parentScreen.getSideboardPage() != null) {
|
|
addItem(menu, "Move", "to Sideboard", parentScreen.getSideboardPage().getIcon(), false, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
removeCard(card, result);
|
|
parentScreen.getSideboardPage().addCard(card, result);
|
|
}
|
|
});
|
|
}
|
|
break;
|
|
case Sideboard:
|
|
addItem(menu, "Add", null, FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
if (parentScreen.isLimitedEditor()) { //ensure card removed from main deck before adding to sideboard
|
|
parentScreen.getMainDeckPage().removeCard(card, result);
|
|
}
|
|
else if (parentScreen.getEditorType() == EditorType.Quest) {
|
|
parentScreen.getCatalogPage().removeCard(card, result);
|
|
}
|
|
addCard(card, result);
|
|
}
|
|
});
|
|
if (!parentScreen.isLimitedEditor()) {
|
|
addItem(menu, "Remove", null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
removeCard(card, result);
|
|
if (parentScreen.getCatalogPage() != null) {
|
|
parentScreen.getCatalogPage().addCard(card, result);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
addItem(menu, "Move", "to Main Deck", parentScreen.getMainDeckPage().getIcon(), false, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
removeCard(card, result);
|
|
parentScreen.getMainDeckPage().addCard(card, result);
|
|
}
|
|
});
|
|
break;
|
|
case Commander:
|
|
if (parentScreen.editorType != EditorType.PlanarConquest || isPartnerCommander(card)) {
|
|
addItem(menu, "Remove", null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) {
|
|
return;
|
|
}
|
|
|
|
removeCard(card, result);
|
|
parentScreen.getCatalogPage().refresh(); //refresh so commander options shown again
|
|
parentScreen.setSelectedPage(parentScreen.getCatalogPage());
|
|
}
|
|
});
|
|
}
|
|
break;
|
|
case Avatar:
|
|
addItem(menu, "Remove", null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
removeCard(card, result);
|
|
}
|
|
});
|
|
break;
|
|
case Schemes:
|
|
addItem(menu, "Add", null, FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
addCard(card, result);
|
|
}
|
|
});
|
|
addItem(menu, "Remove", null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
removeCard(card, result);
|
|
}
|
|
});
|
|
break;
|
|
case Planes:
|
|
addItem(menu, "Add", null, FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
addCard(card, result);
|
|
}
|
|
});
|
|
addItem(menu, "Remove", null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
removeCard(card, result);
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
|
|
if (parentScreen.editorType != EditorType.PlanarConquest && parentScreen.getCommanderPage() != null && deckSection != DeckSection.Commander) {
|
|
if (card.getRules().getType().isLegendary() && card.getRules().getType().isCreature() && !parentScreen.getCommanderPage().cardManager.getPool().contains(card)) {
|
|
addItem(menu, "Set", "as Commander", parentScreen.getCommanderPage().getIcon(), false, false, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) {
|
|
if (result == null || result <= 0) { return; }
|
|
|
|
removeCard(card, result);
|
|
|
|
CardPool newPool = new CardPool();
|
|
newPool.add(card, result);
|
|
parentScreen.getCommanderPage().setCards(newPool);
|
|
parentScreen.getCatalogPage().refresh(); //ensure available cards updated based on color identity
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isPartnerCommander(final PaperCard card) {
|
|
if (parentScreen.getCommanderPage() == null || parentScreen.getDeck().getCommanders().isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
PaperCard firstCmdr = parentScreen.getDeck().getCommanders().get(0);
|
|
return !card.getName().equals(firstCmdr.getName());
|
|
}
|
|
|
|
}
|
|
|
|
private static class DraftPackPage extends CatalogPage {
|
|
protected DraftPackPage() {
|
|
super(ItemManagerConfig.DRAFT_PACK, "Pack 1", FSkinImage.PACK);
|
|
}
|
|
|
|
@Override
|
|
public void refresh() {
|
|
BoosterDraft draft = parentScreen.getDraft();
|
|
if (draft == null) { return; }
|
|
|
|
CardPool pool = draft.nextChoice();
|
|
int packNumber = draft.getCurrentBoosterIndex() + 1;
|
|
caption = "Pack " + packNumber;
|
|
cardManager.setPool(pool);
|
|
}
|
|
|
|
@Override
|
|
protected void onCardActivated(PaperCard card) {
|
|
super.onCardActivated(card);
|
|
afterCardPicked(card);
|
|
}
|
|
|
|
private void afterCardPicked(PaperCard card) {
|
|
BoosterDraft draft = parentScreen.getDraft();
|
|
draft.setChoice(card);
|
|
|
|
// TODO Implement handling of extra boosters
|
|
|
|
if (draft.hasNextChoice()) {
|
|
refresh();
|
|
}
|
|
else {
|
|
hideTab(); //hide this tab page when finished drafting
|
|
parentScreen.save(null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void buildMenu(final FDropDownMenu menu, final PaperCard card) {
|
|
addItem(menu, "Add", "to Main Deck", parentScreen.getMainDeckPage().getIcon(), true, true, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) { //ignore quantity
|
|
parentScreen.getMainDeckPage().addCard(card);
|
|
afterCardPicked(card);
|
|
}
|
|
});
|
|
addItem(menu, "Add", "to Sideboard", parentScreen.getSideboardPage().getIcon(), true, true, new Callback<Integer>() {
|
|
@Override
|
|
public void run(Integer result) { //ignore quantity
|
|
parentScreen.getSideboardPage().addCard(card);
|
|
afterCardPicked(card);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
public static class DeckController<T extends DeckBase> {
|
|
private T model;
|
|
private boolean saved;
|
|
private boolean modelInStorage;
|
|
private IStorage<T> rootFolder;
|
|
private IStorage<T> currentFolder;
|
|
private String modelPath;
|
|
private FDeckEditor editor;
|
|
private final Supplier<T> newModelCreator;
|
|
|
|
protected DeckController(final IStorage<T> folder0, final Supplier<T> newModelCreator0) {
|
|
setRootFolder(folder0);
|
|
newModelCreator = newModelCreator0;
|
|
}
|
|
|
|
public void setRootFolder(IStorage<T> folder0) {
|
|
rootFolder = folder0;
|
|
currentFolder = folder0;
|
|
model = null;
|
|
saved = true;
|
|
modelInStorage = false;
|
|
modelPath = "";
|
|
}
|
|
|
|
public Deck getDeck() {
|
|
if (model == null) { return null; }
|
|
|
|
if (model instanceof Deck) {
|
|
return (Deck) model;
|
|
}
|
|
return ((DeckGroup) model).getHumanDeck();
|
|
}
|
|
|
|
public T getModel() {
|
|
return model;
|
|
}
|
|
|
|
public String getModelPath() {
|
|
return modelPath;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public void setDeck(final Deck deck) {
|
|
modelInStorage = false;
|
|
model = (T)deck;
|
|
currentFolder = rootFolder;
|
|
modelPath = "";
|
|
setSaved(false);
|
|
editor.setDeck(deck);
|
|
}
|
|
|
|
public void setModel(final T document) {
|
|
setModel(document, false);
|
|
}
|
|
public void setModel(final T document, final boolean isStored) {
|
|
modelInStorage = isStored;
|
|
model = document;
|
|
|
|
if (isStored) {
|
|
if (isModelInSyncWithFolder()) {
|
|
setSaved(true);
|
|
}
|
|
else {
|
|
notifyModelChanged();
|
|
}
|
|
}
|
|
else { //TODO: Make this smarter
|
|
currentFolder = rootFolder;
|
|
modelPath = "";
|
|
setSaved(true);
|
|
}
|
|
editor.setDeck(getDeck());
|
|
}
|
|
|
|
private boolean isModelInSyncWithFolder() {
|
|
if (model.getName().isEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
final T modelStored = currentFolder.get(model.getName());
|
|
// checks presence in dictionary only.
|
|
if (modelStored == model) {
|
|
return true;
|
|
}
|
|
if (modelStored == null) {
|
|
return false;
|
|
}
|
|
|
|
return modelStored.equals(model);
|
|
}
|
|
|
|
public void notifyModelChanged() {
|
|
if (saved) {
|
|
setSaved(false);
|
|
}
|
|
}
|
|
|
|
private void setSaved(boolean val) {
|
|
saved = val;
|
|
|
|
if (editor != null) {
|
|
String name = this.getModelName();
|
|
if (name.isEmpty()) {
|
|
name = "[New Deck]";
|
|
}
|
|
if (!saved) {
|
|
name = "*" + name;
|
|
}
|
|
editor.lblName.setText(name);
|
|
editor.btnSave.setEnabled(!saved);
|
|
}
|
|
}
|
|
|
|
public void reload() {
|
|
String name = getModelName();
|
|
if (name.isEmpty()) {
|
|
newModel();
|
|
}
|
|
else {
|
|
load(name);
|
|
}
|
|
}
|
|
|
|
public void load(final String path, final String name) {
|
|
if (StringUtils.isBlank(path)) {
|
|
currentFolder = rootFolder;
|
|
}
|
|
else {
|
|
currentFolder = rootFolder.tryGetFolder(path);
|
|
}
|
|
modelPath = path;
|
|
load(name);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private void load(final String name) {
|
|
T newModel = currentFolder.get(name);
|
|
if (newModel != null) {
|
|
setModel((T) newModel.copyTo(name), true);
|
|
}
|
|
else {
|
|
setSaved(true);
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public void save() {
|
|
if (model == null) {
|
|
return;
|
|
}
|
|
|
|
// copy to new instance before adding to current folder so further changes are auto-saved
|
|
currentFolder.add((T) model.copyTo(model.getName()));
|
|
model.setDirectory(DeckProxy.getDeckDirectory(currentFolder));
|
|
modelInStorage = true;
|
|
setSaved(true);
|
|
|
|
//update saved deck names
|
|
String deckStr = DeckProxy.getDeckString(getModelPath(), getModelName());
|
|
switch (editor.getEditorType()) {
|
|
case Constructed:
|
|
DeckPreferences.setCurrentDeck(deckStr);
|
|
break;
|
|
case Commander:
|
|
DeckPreferences.setCommanderDeck(deckStr);
|
|
break;
|
|
case TinyLeaders:
|
|
DeckPreferences.setTinyLeadersDeck(deckStr);
|
|
break;
|
|
case Brawl:
|
|
DeckPreferences.setBrawlDeck(deckStr);
|
|
break;
|
|
case Archenemy:
|
|
DeckPreferences.setSchemeDeck(deckStr);
|
|
break;
|
|
case Planechase:
|
|
DeckPreferences.setPlanarDeck(deckStr);
|
|
break;
|
|
case Draft:
|
|
DeckPreferences.setDraftDeck(deckStr);
|
|
break;
|
|
case Sealed:
|
|
DeckPreferences.setSealedDeck(deckStr);
|
|
break;
|
|
case Quest:
|
|
FModel.getQuest().setCurrentDeck(model.toString());
|
|
FModel.getQuest().save();
|
|
break;
|
|
case QuestDraft:
|
|
FModel.getQuest().setCurrentDeck(model.toString());
|
|
FModel.getQuest().save();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
editor.setDeck(getDeck());
|
|
if (editor.saveHandler != null) {
|
|
editor.saveHandler.handleEvent(new FEvent(editor, FEventType.SAVE));
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public void saveAs(final String name0) {
|
|
model = (T)model.copyTo(name0);
|
|
modelInStorage = false;
|
|
save();
|
|
}
|
|
|
|
public void rename(final String name0) {
|
|
if (StringUtils.isEmpty(name0)) { return; }
|
|
|
|
String oldName = model.getName();
|
|
if (name0.equals(oldName)) { return; }
|
|
|
|
saveAs(name0);
|
|
currentFolder.delete(oldName); //delete deck with old name
|
|
}
|
|
|
|
public String getNextAvailableName() {
|
|
String name = model.getName();
|
|
int idx = name.lastIndexOf('(');
|
|
if (idx != -1) {
|
|
name = name.substring(0, idx).trim(); //strip old number
|
|
}
|
|
|
|
String baseName = name;
|
|
int number = 2;
|
|
do {
|
|
name = baseName + " (" + number + ")";
|
|
number++;
|
|
} while (fileExists(name));
|
|
|
|
return name;
|
|
}
|
|
|
|
public boolean isSaved() {
|
|
return saved;
|
|
}
|
|
|
|
public boolean fileExists(final String deckName) {
|
|
return currentFolder.contains(deckName);
|
|
}
|
|
|
|
public void importDeck(final T newDeck) {
|
|
setModel(newDeck);
|
|
}
|
|
|
|
public void refreshModel() {
|
|
if (model == null) {
|
|
newModel();
|
|
}
|
|
else {
|
|
setModel(model, modelInStorage);
|
|
}
|
|
}
|
|
|
|
public void newModel() {
|
|
setModel(newModelCreator.get());
|
|
}
|
|
|
|
public String getModelName() {
|
|
return model != null ? model.getName() : "";
|
|
}
|
|
|
|
public boolean delete() {
|
|
if (model == null) { return false; }
|
|
currentFolder.delete(model.getName());
|
|
setModel(null);
|
|
return true;
|
|
}
|
|
}
|
|
}
|