mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Added Commander quest mode and world
-CardPool Added getFilteredPool() to easily get a Predicate applied copy of a CardPool. -GameRules Minor formatting change. -worlds.txt Added Random Commander to the list. -DeckConstructionRules New enum for defining the subformat a quest is using. -QuestAssets getLife() now has a switch for modifying the life for sub-formats. -QuestData New data save version. Includes a DeckConstructionRules enum. -QuestDataIO updateSaveFile will update old saves to have a default DeckConstructionRules complying with the new QuestData save version. -QuestController Updated to include support for DeckConstructionRules and specialized duel managers -QuestEvent Now have boolean to define if this is a "random" match for the duel list. Currently only QuestEventCommanderDuelManager makes use of this feature for Commander quests. -QuestEventCommanderDuel New QuestEventDuel used in the QuestEventCommanderDuelManager which contains a DeckProxy for use in generating random commander decks. -QuestEventCommanderDuelManager New duel manager to generate duels by difficulty for a Commander quest. Currently uses random generation to generate the decks of each opponent. -QuestSpellShop Sell Extras button now has a switch for taking into account special deck construction rules such as Commander only allowing singletons. -QuestUtil Starting a game now checks for various sub-format specific changes including a switch case for which variety of registered player to use. -QuestUtilCards Starting cardpool size is now modified by a switch case for sub-formats such as Commander. -QuestWinLoseController QuestEvents marked as random matches will now award a "Random Opponent Bonus" equal to the credit base. Currently only QuestEventCommanderDuelManager creates QuestEvents marked as such. -QuestWorld Added support for the Commander quest format and world. -CEditorQuest Many changes to add support for Commander in a style that, hopefully, also paths the way for future format support. -CSubmenuQuestData Support for Commander quests. -VSubmenuQuestData Support for Commander quests.
This commit is contained in:
@@ -18,11 +18,20 @@
|
||||
package forge.screens.deckeditor.controllers;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.UiCommand;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.deck.generation.DeckGeneratorBase;
|
||||
import forge.gui.GuiUtils;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.FScreen;
|
||||
@@ -35,6 +44,7 @@ import forge.itemmanager.views.ItemTableColumn;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.quest.QuestController;
|
||||
import forge.quest.data.DeckConstructionRules;
|
||||
import forge.screens.deckeditor.AddBasicLandsDialog;
|
||||
import forge.screens.deckeditor.SEditorIO;
|
||||
import forge.screens.deckeditor.views.VAllDecks;
|
||||
@@ -48,6 +58,7 @@ import forge.util.ItemPool;
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.print.Paper;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -103,6 +114,14 @@ public final class CEditorQuest extends CDeckEditor<Deck> {
|
||||
allSections.add(DeckSection.Main);
|
||||
allSections.add(DeckSection.Sideboard);
|
||||
|
||||
//Add sub-format specific sections
|
||||
switch(FModel.getQuest().getDeckConstructionRules()){
|
||||
case Default: break;
|
||||
case Commander:
|
||||
allSections.add(DeckSection.Commander);
|
||||
break;
|
||||
}
|
||||
|
||||
this.questData = questData0;
|
||||
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture, false, true);
|
||||
@@ -158,6 +177,10 @@ public final class CEditorQuest extends CDeckEditor<Deck> {
|
||||
@Override
|
||||
protected CardLimit getCardLimit() {
|
||||
if (FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
||||
//If this is a commander quest, only allow single copies of cards
|
||||
if(FModel.getQuest().getDeckConstructionRules() == DeckConstructionRules.Commander){
|
||||
return CardLimit.Singleton;
|
||||
}
|
||||
return CardLimit.Default;
|
||||
}
|
||||
return CardLimit.None; //if not enforcing deck legality, don't enforce default limit
|
||||
@@ -245,16 +268,98 @@ public final class CEditorQuest extends CDeckEditor<Deck> {
|
||||
public void resetTables() {
|
||||
this.sectionMode = DeckSection.Main;
|
||||
|
||||
final Deck deck = this.controller.getModel();
|
||||
|
||||
final CardPool cardpool = getInitialCatalog();
|
||||
// remove bottom cards that are in the deck from the card pool
|
||||
cardpool.removeAll(deck.getMain());
|
||||
// remove sideboard cards from the catalog
|
||||
cardpool.removeAll(deck.getOrCreate(DeckSection.Sideboard));
|
||||
// show cards, makes this user friendly
|
||||
this.getCatalogManager().setPool(cardpool);
|
||||
this.getDeckManager().setPool(deck.getMain());
|
||||
this.getCatalogManager().setPool(getRemainingCardPool());
|
||||
this.getDeckManager().setPool(getDeck().getMain());
|
||||
}
|
||||
|
||||
/***
|
||||
* Provides the pool of cards the player has available to add to his or her deck. Also manages showing available cards
|
||||
* to choose from for special deck construction rules, e.g.: Commander.
|
||||
* @return CardPool of cards available to add to the player's deck.
|
||||
*/
|
||||
private CardPool getRemainingCardPool(){
|
||||
final CardPool cardpool = getInitialCatalog();
|
||||
|
||||
// remove bottom cards that are in the deck from the card pool
|
||||
cardpool.removeAll(getDeck().getMain());
|
||||
|
||||
// remove sideboard cards from the catalog
|
||||
cardpool.removeAll(getDeck().getOrCreate(DeckSection.Sideboard));
|
||||
|
||||
switch(FModel.getQuest().getDeckConstructionRules()){
|
||||
case Default: break;
|
||||
case Commander:
|
||||
//remove this deck's currently selected commander(s) from the catalog
|
||||
cardpool.removeAll(getDeck().getOrCreate(DeckSection.Commander));
|
||||
|
||||
//TODO: Only thin if deck conformance is being applied
|
||||
if(getDeck().getOrCreate(DeckSection.Commander).toFlatList().size() > 0) {
|
||||
Predicate<PaperCard> identityPredicate = new MatchCommanderColorIdentity(getDeckColorIdentity());
|
||||
CardPool filteredPool = cardpool.getFilteredPool(identityPredicate);
|
||||
|
||||
return filteredPool;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return cardpool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate that filters out based on a color identity provided upon instantiation. Used to filter the card
|
||||
* list when a commander is chosen so the user can more easily see what cards are available for his or her deck
|
||||
* and avoid making additions that are not legal.
|
||||
*/
|
||||
public static class MatchCommanderColorIdentity implements Predicate<PaperCard> {
|
||||
private final ColorSet allowedColor;
|
||||
|
||||
public MatchCommanderColorIdentity(ColorSet color) {
|
||||
allowedColor = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(PaperCard subject) {
|
||||
CardRules cr = subject.getRules();
|
||||
ManaCost mc = cr.getManaCost();
|
||||
return !mc.isPureGeneric() && allowedColor.containsAllColorsFrom(cr.getColorIdentity().getColor());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the color identity of the loaded deck based on the commanders.
|
||||
* @return A ColorSet containing the color identity of the currently loaded deck.
|
||||
*/
|
||||
public ColorSet getDeckColorIdentity(){
|
||||
|
||||
List<PaperCard> commanders = getDeck().getOrCreate(DeckSection.Commander).toFlatList();
|
||||
List<String> colors = new ArrayList<>();
|
||||
|
||||
//Return early if there are no current commanders
|
||||
if(commanders.size() == 0) return ColorSet.fromNames(colors);
|
||||
|
||||
//For each commander,add each color of its color identity if not already added
|
||||
for(PaperCard pc : commanders){
|
||||
if(!colors.contains("w") && pc.getRules().getColorIdentity().hasWhite()) colors.add("w");
|
||||
if(!colors.contains("u") && pc.getRules().getColorIdentity().hasBlue()) colors.add("u");
|
||||
if(!colors.contains("b") && pc.getRules().getColorIdentity().hasBlack()) colors.add("b");
|
||||
if(!colors.contains("r") && pc.getRules().getColorIdentity().hasRed()) colors.add("r");
|
||||
if(!colors.contains("g") && pc.getRules().getColorIdentity().hasGreen()) colors.add("g");
|
||||
}
|
||||
|
||||
return ColorSet.fromNames(colors);
|
||||
}
|
||||
|
||||
/*
|
||||
Used to make the code more readable in game terms.
|
||||
*/
|
||||
private Deck getDeck(){
|
||||
return this.controller.getModel();
|
||||
}
|
||||
|
||||
private ItemPool<PaperCard> getCommanderCardPool(){
|
||||
Predicate<PaperCard> commanderPredicate = Predicates.compose(CardRulesPredicates.Presets.CAN_BE_COMMANDER, PaperCard.FN_GET_RULES);
|
||||
return getRemainingCardPool().getFilteredPool(commanderPredicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -280,14 +385,30 @@ public final class CEditorQuest extends CDeckEditor<Deck> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between the main deck and the sideboard editor.
|
||||
* Switch between the main deck and the sideboard/Command Zone editor.
|
||||
*/
|
||||
public void setEditorMode(DeckSection sectionMode) {
|
||||
if (sectionMode == DeckSection.Sideboard) {
|
||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Sideboard));
|
||||
}
|
||||
else {
|
||||
this.getDeckManager().setPool(this.controller.getModel().getMain());
|
||||
//Fixes null pointer error on switching tabs while quest deck editor is open. TODO: Find source of bug possibly?
|
||||
if(sectionMode == null) sectionMode = DeckSection.Main;
|
||||
|
||||
//Based on which section the editor is in, display the remaining card pool (or applicable card pool if in
|
||||
//Commander) and the current section's cards
|
||||
switch(sectionMode){
|
||||
case Main :
|
||||
this.getCatalogManager().setup(ItemManagerConfig.CARD_CATALOG);
|
||||
this.getCatalogManager().setPool(getRemainingCardPool());
|
||||
this.getDeckManager().setPool(this.controller.getModel().getMain());
|
||||
break;
|
||||
case Sideboard :
|
||||
this.getCatalogManager().setup(ItemManagerConfig.CARD_CATALOG);
|
||||
this.getCatalogManager().setPool(getRemainingCardPool());
|
||||
this.getDeckManager().setPool(getDeck().getOrCreate(DeckSection.Sideboard));
|
||||
break;
|
||||
case Commander :
|
||||
this.getCatalogManager().setup(ItemManagerConfig.COMMANDER_POOL);
|
||||
this.getCatalogManager().setPool(getCommanderCardPool());
|
||||
this.getDeckManager().setPool(getDeck().getOrCreate(DeckSection.Commander));
|
||||
break;
|
||||
}
|
||||
|
||||
this.sectionMode = sectionMode;
|
||||
|
||||
@@ -10,6 +10,7 @@ import forge.model.FModel;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.quest.*;
|
||||
import forge.quest.StartingPoolPreferences.PoolType;
|
||||
import forge.quest.data.DeckConstructionRules;
|
||||
import forge.quest.data.GameFormatQuest;
|
||||
import forge.quest.data.QuestData;
|
||||
import forge.quest.data.QuestPreferences.QPref;
|
||||
@@ -340,9 +341,16 @@ public enum CSubmenuQuestData implements ICDoc {
|
||||
break;
|
||||
}
|
||||
|
||||
//Apply the appropriate deck construction rules for this quest
|
||||
DeckConstructionRules dcr = DeckConstructionRules.Default;
|
||||
|
||||
if(VSubmenuQuestData.SINGLETON_INSTANCE.isCommander()){
|
||||
dcr = DeckConstructionRules.Commander;
|
||||
}
|
||||
|
||||
final QuestController qc = FModel.getQuest();
|
||||
|
||||
qc.newGame(questName, difficulty, mode, fmtPrizes, view.isUnlockSetsAllowed(), dckStartPool, fmtStartPool, view.getStartingWorldName(), userPrefs);
|
||||
qc.newGame(questName, difficulty, mode, fmtPrizes, view.isUnlockSetsAllowed(), dckStartPool, fmtStartPool, view.getStartingWorldName(), userPrefs, dcr);
|
||||
FModel.getQuest().save();
|
||||
|
||||
// Save in preferences.
|
||||
|
||||
@@ -62,6 +62,7 @@ public enum VSubmenuQuestData implements IVSubmenu<CSubmenuQuestData> {
|
||||
private final FRadioButton radHard = new FRadioButton("Hard");
|
||||
private final FRadioButton radExpert = new FRadioButton("Expert");
|
||||
private final FCheckBox boxFantasy = new FCheckBox("Fantasy Mode");
|
||||
private final FCheckBox boxCommander = new FCheckBox("Commander Subformat");
|
||||
|
||||
private final FLabel lblStartingWorld = new FLabel.Builder().text("Starting world:").build();
|
||||
private final FComboBoxWrapper<QuestWorld> cbxStartingWorld = new FComboBoxWrapper<>();
|
||||
@@ -274,9 +275,25 @@ public enum VSubmenuQuestData implements IVSubmenu<CSubmenuQuestData> {
|
||||
}
|
||||
});
|
||||
|
||||
// Fantasy box enabled by Default
|
||||
// Fantasy box selected by Default
|
||||
boxFantasy.setSelected(true);
|
||||
boxFantasy.setEnabled(true);
|
||||
|
||||
// Commander box unselected by Default
|
||||
boxCommander.setSelected(false);
|
||||
boxCommander.setEnabled(true);
|
||||
|
||||
boxCommander.addActionListener(
|
||||
new ActionListener(){
|
||||
public void actionPerformed(ActionEvent e){
|
||||
if(!isCommander()) return; //do nothing if unselecting Commander Subformat
|
||||
//Otherwise, set the starting world to Random Commander
|
||||
cbxStartingWorld.setSelectedItem(FModel.getWorlds().get("Random Commander"));
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
boxCompleteSet.setEnabled(true);
|
||||
boxAllowDuplicates.setEnabled(true);
|
||||
|
||||
@@ -286,6 +303,7 @@ public enum VSubmenuQuestData implements IVSubmenu<CSubmenuQuestData> {
|
||||
final JPanel pnlDifficultyMode = new JPanel(new MigLayout("insets 0, gap 1%, flowy"));
|
||||
pnlDifficultyMode.add(difficultyPanel, "gapright 4%");
|
||||
pnlDifficultyMode.add(boxFantasy, "h 25px!, gapbottom 15, gapright 4%");
|
||||
pnlDifficultyMode.add(boxCommander, "h 25px!, gapbottom 15, gapright 4%");
|
||||
pnlDifficultyMode.add(lblStartingWorld, "h 25px!, hidemode 3");
|
||||
cbxStartingWorld.addTo(pnlDifficultyMode, "h 27px!, w 40%, pushx, gapbottom 7");
|
||||
pnlDifficultyMode.setOpaque(false);
|
||||
@@ -487,6 +505,14 @@ public enum VSubmenuQuestData implements IVSubmenu<CSubmenuQuestData> {
|
||||
return boxFantasy.isSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth. Imakuni
|
||||
* @return True if the "Commander Subformat" check box is selected.
|
||||
*/
|
||||
public boolean isCommander() {
|
||||
return boxCommander.isSelected();
|
||||
}
|
||||
|
||||
public boolean startWithCompleteSet() {
|
||||
return boxCompleteSet.isSelected();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user