From 37d9dc753f915853ecf1e17e1fea1b5feb7480bd Mon Sep 17 00:00:00 2001 From: Jeremy Pelkala Date: Fri, 2 Nov 2018 18:57:17 -0400 Subject: [PATCH 1/2] -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. --- .../src/main/java/forge/deck/CardPool.java | 14 ++ .../src/main/java/forge/game/GameRules.java | 3 +- .../deckeditor/controllers/CEditorQuest.java | 151 +++++++++++-- .../screens/home/quest/CSubmenuQuestData.java | 10 +- .../screens/home/quest/VSubmenuQuestData.java | 28 ++- forge-gui/res/quest/world/worlds.txt | 1 + .../java/forge/quest/QuestController.java | 14 +- .../src/main/java/forge/quest/QuestEvent.java | 4 + .../forge/quest/QuestEventCommanderDuel.java | 19 ++ .../quest/QuestEventCommanderDuelManager.java | 205 ++++++++++++++++++ .../main/java/forge/quest/QuestSpellShop.java | 17 +- .../src/main/java/forge/quest/QuestUtil.java | 60 ++++- .../main/java/forge/quest/QuestUtilCards.java | 12 +- .../forge/quest/QuestWinLoseController.java | 5 + .../src/main/java/forge/quest/QuestWorld.java | 8 +- .../quest/data/DeckConstructionRules.java | 17 ++ .../java/forge/quest/data/QuestAssets.java | 9 +- .../main/java/forge/quest/data/QuestData.java | 12 +- .../main/java/forge/quest/io/QuestDataIO.java | 8 +- 19 files changed, 561 insertions(+), 36 deletions(-) create mode 100644 forge-gui/src/main/java/forge/quest/QuestEventCommanderDuel.java create mode 100644 forge-gui/src/main/java/forge/quest/QuestEventCommanderDuelManager.java create mode 100644 forge-gui/src/main/java/forge/quest/data/DeckConstructionRules.java diff --git a/forge-core/src/main/java/forge/deck/CardPool.java b/forge-core/src/main/java/forge/deck/CardPool.java index 72ab8efb851..b484f31ab98 100644 --- a/forge-core/src/main/java/forge/deck/CardPool.java +++ b/forge-core/src/main/java/forge/deck/CardPool.java @@ -17,6 +17,7 @@ */ package forge.deck; +import com.google.common.base.Predicate; import com.google.common.collect.Lists; import forge.StaticData; import forge.card.CardDb; @@ -216,4 +217,17 @@ public class CardPool extends ItemPool { } return sb.toString(); } + + /** + * Applies a predicate to this CardPool's cards. + * @param predicate the Predicate to apply to this CardPool + * @return a new CardPool made from this CardPool with only the cards that agree with the provided Predicate + */ + public CardPool getFilteredPool(Predicate predicate){ + CardPool filteredPool = new CardPool(); + for(PaperCard pc : this.items.keySet()){ + if(predicate.apply(pc)) filteredPool.add(pc); + } + return filteredPool; + } } diff --git a/forge-game/src/main/java/forge/game/GameRules.java b/forge-game/src/main/java/forge/game/GameRules.java index dc7ed4cddf1..5e6876e9801 100644 --- a/forge-game/src/main/java/forge/game/GameRules.java +++ b/forge-game/src/main/java/forge/game/GameRules.java @@ -78,7 +78,8 @@ public class GameRules { } public boolean hasCommander() { - return appliedVariants.contains(GameType.Commander) || appliedVariants.contains(GameType.TinyLeaders) + return appliedVariants.contains(GameType.Commander) + || appliedVariants.contains(GameType.TinyLeaders) || appliedVariants.contains(GameType.Brawl); } diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuest.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuest.java index c33da12cda7..b7842272e42 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuest.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuest.java @@ -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 { 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 { @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 { 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 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 { + 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 commanders = getDeck().getOrCreate(DeckSection.Commander).toFlatList(); + List 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 getCommanderCardPool(){ + Predicate 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 { } /** - * 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; diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuQuestData.java b/forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuQuestData.java index 131805b738b..8aa76293b55 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuQuestData.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuQuestData.java @@ -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. diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/quest/VSubmenuQuestData.java b/forge-gui-desktop/src/main/java/forge/screens/home/quest/VSubmenuQuestData.java index 9eee9e58ca3..70e284faf3d 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/quest/VSubmenuQuestData.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/quest/VSubmenuQuestData.java @@ -62,6 +62,7 @@ public enum VSubmenuQuestData implements IVSubmenu { 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 cbxStartingWorld = new FComboBoxWrapper<>(); @@ -274,9 +275,25 @@ public enum VSubmenuQuestData implements IVSubmenu { } }); - // 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 { 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 { 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(); } diff --git a/forge-gui/res/quest/world/worlds.txt b/forge-gui/res/quest/world/worlds.txt index b08931cde59..5d99427d535 100644 --- a/forge-gui/res/quest/world/worlds.txt +++ b/forge-gui/res/quest/world/worlds.txt @@ -1,5 +1,6 @@ Name:Main world Name:Random Standard +Name:Random Commander Name:Amonkhet|Dir:Amonkhet|Sets:AKH, HOU Name:Jamuraa|Dir:jamuraa|Sets:5ED, ARN, MIR, VIS, WTH|Banned:Chaos Orb; Falling Star Name:Kamigawa|Dir:2004 Kamigawa|Sets:CHK, BOK, SOK diff --git a/forge-gui/src/main/java/forge/quest/QuestController.java b/forge-gui/src/main/java/forge/quest/QuestController.java index 0d306f4431a..5366f7200bc 100644 --- a/forge-gui/src/main/java/forge/quest/QuestController.java +++ b/forge-gui/src/main/java/forge/quest/QuestController.java @@ -276,9 +276,10 @@ public class QuestController { public void newGame(final String name, final int difficulty, final QuestMode mode, final GameFormat formatPrizes, final boolean allowSetUnlocks, final Deck startingCards, final GameFormat formatStartingPool, - final String startingWorld, final StartingPoolPreferences userPrefs) { + final String startingWorld, final StartingPoolPreferences userPrefs, + DeckConstructionRules dcr) { - this.load(new QuestData(name, difficulty, mode, formatPrizes, allowSetUnlocks, startingWorld)); // pass awards and unlocks here + this.load(new QuestData(name, difficulty, mode, formatPrizes, allowSetUnlocks, startingWorld, dcr)); // pass awards and unlocks here if (startingCards != null) { this.myCards.addDeck(startingCards); @@ -435,6 +436,12 @@ public class QuestController { QuestWorld world = getWorld(); String path = ForgeConstants.DEFAULT_CHALLENGES_DIR; + //Use a variant specialized duel manager if this is a variant quest + switch(FModel.getQuest().getDeckConstructionRules()){ + case Default: break; + case Commander: this.duelManager = new QuestEventCommanderDuelManager(); return; + } + if (world != null) { if (world.getName().equals(QuestWorld.STANDARDWORLDNAME)) { @@ -449,7 +456,6 @@ public class QuestController { } this.duelManager = new QuestEventDuelManager(new File(path)); - } public HashSet GetRating() { @@ -607,4 +613,6 @@ public class QuestController { public void setCurrentDeck(String s) { model.currentDeck = s; } + + public DeckConstructionRules getDeckConstructionRules(){return model.deckConstructionRules;} } diff --git a/forge-gui/src/main/java/forge/quest/QuestEvent.java b/forge-gui/src/main/java/forge/quest/QuestEvent.java index 00a140c80e2..2a9bf6c1b63 100644 --- a/forge-gui/src/main/java/forge/quest/QuestEvent.java +++ b/forge-gui/src/main/java/forge/quest/QuestEvent.java @@ -48,6 +48,7 @@ public abstract class QuestEvent implements IQuestEvent { private String profile = "Default"; // Opponent name if different from the challenge name private String opponentName = null; + private boolean isRandomMatch = false; public static final Function FN_GET_NAME = new Function() { @@ -174,4 +175,7 @@ public abstract class QuestEvent implements IQuestEvent { this.showDifficulty = showDifficulty; } + public boolean getIsRandomMatch(){return isRandomMatch;} + + public void setIsRandomMatch(boolean b){isRandomMatch = b;} } diff --git a/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuel.java b/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuel.java new file mode 100644 index 00000000000..1d7924a04d7 --- /dev/null +++ b/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuel.java @@ -0,0 +1,19 @@ +package forge.quest; + +import forge.deck.DeckProxy; + +/** + * A QuestEventDuel with a CommanderDeckGenerator used exclusively within QuestEventCommanderDuelManager for the + * creation of randomly generated Commander decks in a Commander variant quest. + * Auth. Imakuni & Forge + */ +public class QuestEventCommanderDuel extends QuestEventDuel{ + /** + * The CommanderDeckGenerator for this duel. + */ + private DeckProxy deckProxy; + + public DeckProxy getDeckProxy() {return deckProxy;} + + public void setDeckProxy(DeckProxy dp) {deckProxy = dp;} +} diff --git a/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuelManager.java b/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuelManager.java new file mode 100644 index 00000000000..384d9d11d77 --- /dev/null +++ b/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuelManager.java @@ -0,0 +1,205 @@ +package forge.quest; + +import forge.deck.*; +import forge.item.PaperCard; +import forge.model.FModel; +import forge.quest.data.QuestPreferences; +import forge.util.CollectionSuppliers; +import forge.util.MyRandom; +import forge.util.maps.EnumMapOfLists; +import forge.util.maps.MapOfLists; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Manages the creation of random Commander duels for a Commander variant quest. Random generation is handled via + * the CommanderDeckGenerator class. + * Auth. Forge & Imakuni#8015 + */ +public class QuestEventCommanderDuelManager implements QuestEventDuelManagerInterface { + /** + * The list of all possible Commander variant duels. + */ + private ArrayList commanderDuels = new ArrayList<>(); + + /** + * Contains the expert deck lists for the commanders. + */ + private List expertCommanderDecks; + + /** + * Immediately calls assembleDuels() to setup the commanderDuels variable. + */ + public QuestEventCommanderDuelManager(){ + assembleDuels(); + } + + /** + * Assembles the list of all possible Commander duels via CommanderDeckGenerator. Should be done within constructor. + */ + private void assembleDuels(){ + //isCardGen = true seemed to make slightly more difficult decks based purely on experience with a very small sample size. + //Gotta work on this more, its making pretty average decks after further testing. + expertCommanderDecks = CommanderDeckGenerator.getCommanderDecks(DeckFormat.Commander, true, true); + + List generatedDuels = CommanderDeckGenerator.getCommanderDecks(DeckFormat.Commander, true, false); + + for(DeckProxy dp : generatedDuels){ + QuestEventCommanderDuel duel = new QuestEventCommanderDuel(); + + duel.setDescription("Randomly generated " + dp.getName() + " commander deck."); + duel.setName(dp.getName()); + duel.setTitle(dp.getName()); + duel.setOpponentName(dp.getName()); + duel.setDifficulty(QuestEventDifficulty.EASY); + duel.setDeckProxy(dp); + + //Setting a blank deck avoids a null pointer exception. The deck is generated in generateDuels() to avoid long load times. + duel.setEventDeck(new Deck()); + + commanderDuels.add(duel); + } + } + + /** + * Retrieve list of all possible Commander duels. + * @return ArrayList containing all possible Commander duels. + */ + public Iterable getAllDuels() { + return commanderDuels; + } + + /** + * Retrieve list of all possible Commander duels. + * @param difficulty Currently unused + * @return ArrayList containing all possible Commander duels. + */ + public Iterable getDuels(QuestEventDifficulty difficulty){ + return commanderDuels; + } + + /** + * Composes an ArrayList containing 4 QuestEventDuels composed with Commander variant decks. One duel will have its + * title replaced as Random. + * @return ArrayList of QuestEventDuels containing 4 duels. + */ + public List generateDuels(){ + final List duelOpponents = new ArrayList<>(); + + //While there are less than 4 duels chosen + while(duelOpponents.size() < 4){ + //Get a random duel from the possible duels list + QuestEventCommanderDuel duel = (QuestEventCommanderDuel)commanderDuels.get(((int) (commanderDuels.size() * MyRandom.getRandom().nextDouble()))); + + //If the chosen duels list already contains this duel, get a different duel to prevent duplicate duels + if(duelOpponents.contains(duel)) continue; + + //Add the randomly chosen duel to the duel list + duelOpponents.add(duel); + + //Here the actual deck for this commander is generated by calling .getDeck() on the saved DeckProxy + duel.setEventDeck(duel.getDeckProxy().getDeck()); + + //Modify deck for difficulty + modifyDuelForDifficulty(duel); + + + } + + //Modify the stats of the final duel to hide the opponent, creating a "random" duel. + //We make a copy of the final duel and overwrite it in the duelOpponents to avoid changing the variables in + //the original duel, which gets reused. + QuestEventCommanderDuel duel = (QuestEventCommanderDuel)duelOpponents.get(duelOpponents.size() - 1); + QuestEventCommanderDuel randomDuel = new QuestEventCommanderDuel(); + + randomDuel.setName(duel.getName()); + randomDuel.setOpponentName(duel.getName()); + randomDuel.setDeckProxy(duel.getDeckProxy()); + randomDuel.setTitle("Random Opponent"); + randomDuel.setShowDifficulty(false); + randomDuel.setDescription("Fight a random generated commander opponent."); + randomDuel.setIsRandomMatch(true); + randomDuel.setEventDeck(duel.getEventDeck()); + + //Replace the final duel with this newly modified "random" duel + duelOpponents.set(duelOpponents.size()-1, randomDuel); + + return duelOpponents; + } + + /** + * Retrieves the expert level deck generation of a deck with the same commander as the provided DeckProxy. + * @param dp The easy generation commander deck + * @return The same commander's expert generation DeckProxy + */ + private Deck getExpertGenDeck(DeckProxy dp){ + for(QuestEventDuel qed : commanderDuels){ + QuestEventCommanderDuel cmdQED = (QuestEventCommanderDuel)qed; + if(cmdQED.getDeckProxy().getName().equals(dp.getName())){ + return cmdQED.getDeckProxy().getDeck(); + } + } + return null; + } + + /** + * Modifies a given duel by replacing a percentage of the deck with random cards from the more difficult generated version + * of the same commander's deck. Medium replaces 30%, Hard replaces 60%, Expert replaces 100%. + * @param duel The QuestEventCommanderDuel to modify + */ + private void modifyDuelForDifficulty(QuestEventCommanderDuel duel){ + final QuestPreferences questPreferences = FModel.getQuestPreferences(); + final int index = FModel.getQuest().getAchievements().getDifficulty(); + final int numberOfWins = FModel.getQuest().getAchievements().getWin(); + Deck expertDeck = getExpertGenDeck(duel.getDeckProxy()); + + int difficultyReplacementPercent = 0; + + //Note: The code is ordered to make the least number of comparisons I could think of at the time for speed reasons. + //In reality, it shouldn't really make much difference, but why not? + if (numberOfWins >= questPreferences.getPrefInt(QuestPreferences.DifficultyPrefs.WINS_EXPERTAI, index)) { + //At expert, the deck is replaced with the entire expert deck, and we can return immediately + duel.setEventDeck(expertDeck); + duel.setDifficulty(QuestEventDifficulty.EXPERT); + return; + } + + if (numberOfWins >= questPreferences.getPrefInt(QuestPreferences.DifficultyPrefs.WINS_MEDIUMAI, index)) { + difficultyReplacementPercent += 30; + duel.setDifficulty(QuestEventDifficulty.MEDIUM); + } else return; //return early here since it would be an easy opponent with no changes + + if (numberOfWins >= questPreferences.getPrefInt(QuestPreferences.DifficultyPrefs.WINS_HARDAI, index)) { + difficultyReplacementPercent += 30; + duel.setDifficulty(QuestEventDifficulty.HARD); + } + + CardPool easyMain = duel.getEventDeck().getMain(); + CardPool expertMain = expertDeck.getMain(); + + List easyList = easyMain.toFlatList(); + List expertList = expertMain.toFlatList(); + + //Replace cards in the easy deck with cards from the expert deck up to the difficulty replacement percent + for(int i = 0; i < difficultyReplacementPercent; i++){ + if(!easyMain.contains(expertList.get(i))) { //ensure that the card being copied over isn't already in the deck + easyMain.remove(easyList.get(i)); + easyMain.add(expertList.get(i)); + } + else{ + expertList.remove(expertList.get(i)); + i--; + if(expertList.size() == 0) break; //break if there are no more cards to copy over + } + } + } + + /** + * Randomizes the list of Commander Duels. + */ + public void randomizeOpponents(){ + Collections.shuffle(commanderDuels); + } +} diff --git a/forge-gui/src/main/java/forge/quest/QuestSpellShop.java b/forge-gui/src/main/java/forge/quest/QuestSpellShop.java index e9196e417df..96eae768963 100644 --- a/forge-gui/src/main/java/forge/quest/QuestSpellShop.java +++ b/forge-gui/src/main/java/forge/quest/QuestSpellShop.java @@ -344,11 +344,24 @@ public class QuestSpellShop { List> cardsToRemove = new LinkedList<>(); for (Entry item : inventoryManager.getPool()) { PaperCard card = (PaperCard)item.getKey(); - int numToKeep = card.getRules().getType().isBasic() ? - FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_BASIC_LAND_SIZE) : FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_SIZE); + //Number of a particular card to keep + int numToKeep = 4; + + if(card.getRules().getType().isBasic()){ + numToKeep = FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_BASIC_LAND_SIZE); + } else{ + //Choose card limit restrictions based on deck construction rules, e.g.: Commander allows only singletons + switch(FModel.getQuest().getDeckConstructionRules()){ + case Default: numToKeep = FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_SIZE); break; + case Commander: numToKeep = 1; + } + } + + //If this card has an exception to the card limit, e.g.: Relentless Rats, get the quest preference if (DeckFormat.getLimitExceptions().contains(card.getName())) { numToKeep = FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_ANY_NUMBER_SIZE); } + if (numToKeep < item.getValue()) { cardsToRemove.add(Pair.of(item.getKey(), item.getValue() - numToKeep)); } diff --git a/forge-gui/src/main/java/forge/quest/QuestUtil.java b/forge-gui/src/main/java/forge/quest/QuestUtil.java index c2fe796ef7b..7ad6b6cd335 100644 --- a/forge-gui/src/main/java/forge/quest/QuestUtil.java +++ b/forge-gui/src/main/java/forge/quest/QuestUtil.java @@ -41,6 +41,7 @@ import forge.properties.ForgePreferences.FPref; import forge.quest.bazaar.IQuestBazaarItem; import forge.quest.bazaar.QuestItemType; import forge.quest.bazaar.QuestPetController; +import forge.quest.data.DeckConstructionRules; import forge.quest.data.QuestAchievements; import forge.quest.data.QuestAssets; import forge.util.gui.SGuiChoose; @@ -51,6 +52,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; +import java.util.TreeSet; /** *

@@ -531,7 +533,17 @@ public class QuestUtil { Integer lifeHuman = null; boolean useBazaar = true; Boolean forceAnte = null; - int lifeAI = 20; + + //Generate a life modifier based on this quest's variant as held in the Quest Controller's DeckConstructionRules + int variantLifeModifier = 0; + + switch(FModel.getQuest().getDeckConstructionRules()){ + case Default: break; + case Commander: variantLifeModifier = 20; break; + } + + int lifeAI = 20 + variantLifeModifier; + if (event instanceof QuestEventChallenge) { final QuestEventChallenge qc = ((QuestEventChallenge) event); lifeAI = qc.getAILife(); @@ -545,8 +557,9 @@ public class QuestUtil { forceAnte = qc.isForceAnte(); } - final RegisteredPlayer humanStart = new RegisteredPlayer(getDeckForNewGame()); - final RegisteredPlayer aiStart = new RegisteredPlayer(event.getEventDeck()); + final RegisteredPlayer humanStart = getRegisteredPlayerByVariant(getDeckForNewGame()); + + final RegisteredPlayer aiStart = getRegisteredPlayerByVariant(event.getEventDeck()); if (lifeHuman != null) { humanStart.setStartingLife(lifeHuman); @@ -581,17 +594,39 @@ public class QuestUtil { rules.setGamesPerMatch(qData.getMatchLength()); rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN)); rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE)); + + TreeSet variant = new TreeSet(); + if(FModel.getQuest().getDeckConstructionRules() == DeckConstructionRules.Commander){ + variant.add(GameType.Commander); + } + final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch(); final IGuiGame gui = GuiBase.getInterface().getNewGuiGame(); gui.setPlayerAvatar(aiPlayer, event); FThreads.invokeInEdtNowOrLater(new Runnable(){ @Override public void run() { - hostedMatch.startMatch(rules, null, starter, ImmutableMap.of(humanStart, gui)); + hostedMatch.startMatch(rules, variant, starter, ImmutableMap.of(humanStart, gui)); } }); } + /** + * Uses the appropriate RegisteredPlayer command for generating a RegisteredPlayer based on this quest's variant as + * held by the QuestController's DeckConstructionRules. + * @param deck The deck to generate the RegisteredPlayer with + * @return A newly made RegisteredPlayer specific to the quest's variant + */ + private static RegisteredPlayer getRegisteredPlayerByVariant(Deck deck){ + switch (FModel.getQuest().getDeckConstructionRules()) { + case Default: + return new RegisteredPlayer(deck); + case Commander: + return RegisteredPlayer.forCommander(deck); + } + return null; + } + private static Deck getDeckForNewGame() { Deck deck = null; if (event instanceof QuestEventChallenge) { @@ -623,7 +658,7 @@ public class QuestUtil { } if (FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) { - final String errorMessage = GameType.Quest.getDeckFormat().getDeckConformanceProblem(deck); + final String errorMessage = getDeckConformanceProblems(deck); if (null != errorMessage) { SOptionPane.showErrorDialog("Your deck " + errorMessage + " Please edit or choose a different deck.", "Invalid Deck"); return false; @@ -633,6 +668,21 @@ public class QuestUtil { return true; } + public static String getDeckConformanceProblems(Deck deck){ + String errorMessage = GameType.Quest.getDeckFormat().getDeckConformanceProblem(deck);; + + if(errorMessage != null) return errorMessage; //return immediately if the deck does not conform to quest requirements + + //Check for all applicable deck construction rules per this quests's saved DeckConstructionRules enum + switch(FModel.getQuest().getDeckConstructionRules()){ + case Commander: + errorMessage = GameType.Commander.getDeckFormat().getDeckConformanceProblem(deck); + break; + } + + return errorMessage; + } + /** Duplicate in DeckEditorQuestMenu and * probably elsewhere...can streamline at some point * (probably shouldn't be here). diff --git a/forge-gui/src/main/java/forge/quest/QuestUtilCards.java b/forge-gui/src/main/java/forge/quest/QuestUtilCards.java index c8d77d863ad..54a4887ba8a 100644 --- a/forge-gui/src/main/java/forge/quest/QuestUtilCards.java +++ b/forge-gui/src/main/java/forge/quest/QuestUtilCards.java @@ -308,10 +308,16 @@ public final class QuestUtilCards { * user preferences */ public void setupNewGameCardPool(final GameFormat formatStartingPool, final int idxDifficulty, final StartingPoolPreferences userPrefs) { + //Add additional cards to the starter card pool based on variant if applicable + double variantModifier = 0; + switch(FModel.getQuest().getDeckConstructionRules()){ + case Default: break; + case Commander: variantModifier = 2; break; + } - final int nC = questPreferences.getPrefInt(DifficultyPrefs.STARTING_COMMONS, idxDifficulty); - final int nU = questPreferences.getPrefInt(DifficultyPrefs.STARTING_UNCOMMONS, idxDifficulty); - final int nR = questPreferences.getPrefInt(DifficultyPrefs.STARTING_RARES, idxDifficulty); + final int nC = (int)(questPreferences.getPrefInt(DifficultyPrefs.STARTING_COMMONS, idxDifficulty) * variantModifier); + final int nU = (int)(questPreferences.getPrefInt(DifficultyPrefs.STARTING_UNCOMMONS, idxDifficulty) * variantModifier); + final int nR = (int)(questPreferences.getPrefInt(DifficultyPrefs.STARTING_RARES, idxDifficulty) * variantModifier); addAllCards(BoosterUtils.getQuestStarterDeck(formatStartingPool, nC, nU, nR, userPrefs)); diff --git a/forge-gui/src/main/java/forge/quest/QuestWinLoseController.java b/forge-gui/src/main/java/forge/quest/QuestWinLoseController.java index 2e93e7d4fec..265ea61bb58 100644 --- a/forge-gui/src/main/java/forge/quest/QuestWinLoseController.java +++ b/forge-gui/src/main/java/forge/quest/QuestWinLoseController.java @@ -226,6 +226,11 @@ public class QuestWinLoseController { sb.append(StringUtils.capitalize(qEvent.getDifficulty().getTitle())); sb.append(" opponent: ").append(credBase).append(" credits.\n"); + if(qEvent.getIsRandomMatch()){ + sb.append("Random Opponent Bonus: " + credBase + " credit" + (credBase > 1 ? "s." : ".") + "\n"); + credBase += credBase; + } + final int winMultiplier = Math.min(qData.getAchievements().getWin(), FModel.getQuestPreferences().getPrefInt(QPref.REWARDS_WINS_MULTIPLIER_MAX)); final int creditsForPreviousWins = (int) ((Double.parseDouble(FModel.getQuestPreferences() .getPref(QPref.REWARDS_WINS_MULTIPLIER)) * winMultiplier)); diff --git a/forge-gui/src/main/java/forge/quest/QuestWorld.java b/forge-gui/src/main/java/forge/quest/QuestWorld.java index e20dcf98ce8..4c77b0ec515 100644 --- a/forge-gui/src/main/java/forge/quest/QuestWorld.java +++ b/forge-gui/src/main/java/forge/quest/QuestWorld.java @@ -40,6 +40,7 @@ public class QuestWorld implements Comparable{ private final String dir; private final GameFormatQuest format; public static final String STANDARDWORLDNAME = "Random Standard"; + public static final String RANDOMCOMMANDERWORLDNAME = "Random Commander"; private boolean isCustom; @@ -129,7 +130,6 @@ public class QuestWorld implements Comparable{ /** * TODO: Write javadoc for Constructor. * @param file0 - * @param keySelector0 */ public Reader(String file0) { super(file0, QuestWorld.FN_GET_NAME); @@ -194,6 +194,12 @@ public class QuestWorld implements Comparable{ FModel.getFormats().getStandard().getBannedCardNames(),false); } + if (useName.equalsIgnoreCase(QuestWorld.RANDOMCOMMANDERWORLDNAME)){ + useFormat = new GameFormatQuest(QuestWorld.RANDOMCOMMANDERWORLDNAME, + FModel.getFormats().getFormat("Commander").getAllowedSetCodes(), + FModel.getFormats().getFormat("Commander").getBannedCardNames(),false); + } + // System.out.println("Creating quest world " + useName + " (index " + useIdx + ", dir: " + useDir); // if (useFormat != null) { System.out.println("SETS: " + sets + "\nBANNED: " + bannedCards); } diff --git a/forge-gui/src/main/java/forge/quest/data/DeckConstructionRules.java b/forge-gui/src/main/java/forge/quest/data/DeckConstructionRules.java new file mode 100644 index 00000000000..3744beea09d --- /dev/null +++ b/forge-gui/src/main/java/forge/quest/data/DeckConstructionRules.java @@ -0,0 +1,17 @@ +package forge.quest.data; + +/** + * Used to clarify which subformat a quest is using e.g. Commander. + * Auth. Imakuni + */ +public enum DeckConstructionRules { + /** + * Typically has no effect on Quest gameplay. + */ + Default, + + /** + * Commander ruleset. 99 card deck, no copies other than basic lands, commander(s) in Command zone + */ + Commander +} diff --git a/forge-gui/src/main/java/forge/quest/data/QuestAssets.java b/forge-gui/src/main/java/forge/quest/data/QuestAssets.java index 616c49d937b..8f7b644641b 100644 --- a/forge-gui/src/main/java/forge/quest/data/QuestAssets.java +++ b/forge-gui/src/main/java/forge/quest/data/QuestAssets.java @@ -200,7 +200,14 @@ public class QuestAssets { * @return the life */ public int getLife(final QuestMode mode) { - final int base = mode.equals(QuestMode.Fantasy) ? 15 : 20; + int base = mode.equals(QuestMode.Fantasy) ? 15 : 20; + + //Modify life for the quest's sub-format, e.g.: Commander adds 20 + switch(FModel.getQuest().getDeckConstructionRules()){ + case Default: break; + case Commander: base += 20; + } + return (base + this.getItemLevel(QuestItemType.ELIXIR_OF_LIFE)) - this.getItemLevel(QuestItemType.POUND_FLESH); } diff --git a/forge-gui/src/main/java/forge/quest/data/QuestData.java b/forge-gui/src/main/java/forge/quest/data/QuestData.java index e6a75adef7d..41574634113 100644 --- a/forge-gui/src/main/java/forge/quest/data/QuestData.java +++ b/forge-gui/src/main/java/forge/quest/data/QuestData.java @@ -42,7 +42,7 @@ import java.util.Map; */ public final class QuestData { /** Holds the latest version of the Quest Data. */ - public static final int CURRENT_VERSION_NUMBER = 12; + public static final int CURRENT_VERSION_NUMBER = 13; // This field places the version number into QD instance, // but only when the object is created through the constructor @@ -70,6 +70,11 @@ public final class QuestData { public String currentDeck = "DEFAULT"; + /** + * Holds the subformat for this quest. Defaults to DeckConstructionRules.Default. + */ + public DeckConstructionRules deckConstructionRules = DeckConstructionRules.Default; + public QuestData() { //needed for XML serialization } @@ -87,9 +92,11 @@ public final class QuestData { * allow set unlocking during quest * @param startingWorld * starting world + * @param dcr + * deck construction rules e.g. Commander */ public QuestData(String name0, int diff, QuestMode mode0, GameFormat userFormat, - boolean allowSetUnlocks, final String startingWorld) { + boolean allowSetUnlocks, final String startingWorld, DeckConstructionRules dcr) { this.name = name0; if (userFormat != null) { @@ -99,6 +106,7 @@ public final class QuestData { this.achievements = new QuestAchievements(diff); this.assets = new QuestAssets(format); this.worldId = startingWorld; + this.deckConstructionRules = dcr; } /** diff --git a/forge-gui/src/main/java/forge/quest/io/QuestDataIO.java b/forge-gui/src/main/java/forge/quest/io/QuestDataIO.java index de680502e37..cd232367df9 100644 --- a/forge-gui/src/main/java/forge/quest/io/QuestDataIO.java +++ b/forge-gui/src/main/java/forge/quest/io/QuestDataIO.java @@ -223,10 +223,16 @@ public class QuestDataIO { // Current Deck moved from preferences to quest data - it should not be global for all quests!!! QuestDataIO.setFinalField(QuestData.class, "currentDeck", newData, FModel.getQuestPreferences().getPref(QPref.CURRENT_DECK)); } - if (saveVersion < 13) { + if(saveVersion < 13){ + //Update for quest DeckConstructionRules + //Add a DeckConstructionRules set to Default. + QuestDataIO.setFinalField(QuestData.class, "deckConstructionRules", newData, DeckConstructionRules.Default); + } + if (saveVersion < 14) { // Migrate DraftTournaments to use new Tournament class } + final QuestAssets qS = newData.getAssets(); final QuestAchievements qA = newData.getAchievements(); From f4ba5258ead0ef30b979a008f2d4b20399d42a91 Mon Sep 17 00:00:00 2001 From: imakunee Date: Sat, 3 Nov 2018 00:36:26 +0000 Subject: [PATCH 2/2] Revert "-CardPool" This reverts commit 37d9dc753f915853ecf1e17e1fea1b5feb7480bd --- .../src/main/java/forge/deck/CardPool.java | 14 -- .../src/main/java/forge/game/GameRules.java | 3 +- .../deckeditor/controllers/CEditorQuest.java | 145 +------------ .../screens/home/quest/CSubmenuQuestData.java | 10 +- .../screens/home/quest/VSubmenuQuestData.java | 28 +-- forge-gui/res/quest/world/worlds.txt | 1 - .../java/forge/quest/QuestController.java | 14 +- .../src/main/java/forge/quest/QuestEvent.java | 4 - .../forge/quest/QuestEventCommanderDuel.java | 19 -- .../quest/QuestEventCommanderDuelManager.java | 205 ------------------ .../main/java/forge/quest/QuestSpellShop.java | 17 +- .../src/main/java/forge/quest/QuestUtil.java | 60 +---- .../main/java/forge/quest/QuestUtilCards.java | 12 +- .../forge/quest/QuestWinLoseController.java | 5 - .../src/main/java/forge/quest/QuestWorld.java | 8 +- .../quest/data/DeckConstructionRules.java | 17 -- .../java/forge/quest/data/QuestAssets.java | 9 +- .../main/java/forge/quest/data/QuestData.java | 12 +- .../main/java/forge/quest/io/QuestDataIO.java | 8 +- 19 files changed, 33 insertions(+), 558 deletions(-) delete mode 100644 forge-gui/src/main/java/forge/quest/QuestEventCommanderDuel.java delete mode 100644 forge-gui/src/main/java/forge/quest/QuestEventCommanderDuelManager.java delete mode 100644 forge-gui/src/main/java/forge/quest/data/DeckConstructionRules.java diff --git a/forge-core/src/main/java/forge/deck/CardPool.java b/forge-core/src/main/java/forge/deck/CardPool.java index b484f31ab98..72ab8efb851 100644 --- a/forge-core/src/main/java/forge/deck/CardPool.java +++ b/forge-core/src/main/java/forge/deck/CardPool.java @@ -17,7 +17,6 @@ */ package forge.deck; -import com.google.common.base.Predicate; import com.google.common.collect.Lists; import forge.StaticData; import forge.card.CardDb; @@ -217,17 +216,4 @@ public class CardPool extends ItemPool { } return sb.toString(); } - - /** - * Applies a predicate to this CardPool's cards. - * @param predicate the Predicate to apply to this CardPool - * @return a new CardPool made from this CardPool with only the cards that agree with the provided Predicate - */ - public CardPool getFilteredPool(Predicate predicate){ - CardPool filteredPool = new CardPool(); - for(PaperCard pc : this.items.keySet()){ - if(predicate.apply(pc)) filteredPool.add(pc); - } - return filteredPool; - } } diff --git a/forge-game/src/main/java/forge/game/GameRules.java b/forge-game/src/main/java/forge/game/GameRules.java index 5e6876e9801..dc7ed4cddf1 100644 --- a/forge-game/src/main/java/forge/game/GameRules.java +++ b/forge-game/src/main/java/forge/game/GameRules.java @@ -78,8 +78,7 @@ public class GameRules { } public boolean hasCommander() { - return appliedVariants.contains(GameType.Commander) - || appliedVariants.contains(GameType.TinyLeaders) + return appliedVariants.contains(GameType.Commander) || appliedVariants.contains(GameType.TinyLeaders) || appliedVariants.contains(GameType.Brawl); } diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuest.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuest.java index b7842272e42..c33da12cda7 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuest.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorQuest.java @@ -18,20 +18,11 @@ 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; @@ -44,7 +35,6 @@ 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; @@ -58,7 +48,6 @@ 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; @@ -114,14 +103,6 @@ public final class CEditorQuest extends CDeckEditor { 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); @@ -177,10 +158,6 @@ public final class CEditorQuest extends CDeckEditor { @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 @@ -268,98 +245,16 @@ public final class CEditorQuest extends CDeckEditor { public void resetTables() { this.sectionMode = DeckSection.Main; - // show cards, makes this user friendly - this.getCatalogManager().setPool(getRemainingCardPool()); - this.getDeckManager().setPool(getDeck().getMain()); - } + final Deck deck = this.controller.getModel(); - /*** - * 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()); - + cardpool.removeAll(deck.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 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 { - 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 commanders = getDeck().getOrCreate(DeckSection.Commander).toFlatList(); - List 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 getCommanderCardPool(){ - Predicate commanderPredicate = Predicates.compose(CardRulesPredicates.Presets.CAN_BE_COMMANDER, PaperCard.FN_GET_RULES); - return getRemainingCardPool().getFilteredPool(commanderPredicate); + cardpool.removeAll(deck.getOrCreate(DeckSection.Sideboard)); + // show cards, makes this user friendly + this.getCatalogManager().setPool(cardpool); + this.getDeckManager().setPool(deck.getMain()); } @Override @@ -385,30 +280,14 @@ public final class CEditorQuest extends CDeckEditor { } /** - * Switch between the main deck and the sideboard/Command Zone editor. + * Switch between the main deck and the sideboard editor. */ public void setEditorMode(DeckSection sectionMode) { - //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; + if (sectionMode == DeckSection.Sideboard) { + this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Sideboard)); + } + else { + this.getDeckManager().setPool(this.controller.getModel().getMain()); } this.sectionMode = sectionMode; diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuQuestData.java b/forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuQuestData.java index 8aa76293b55..131805b738b 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuQuestData.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuQuestData.java @@ -10,7 +10,6 @@ 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; @@ -341,16 +340,9 @@ 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, dcr); + qc.newGame(questName, difficulty, mode, fmtPrizes, view.isUnlockSetsAllowed(), dckStartPool, fmtStartPool, view.getStartingWorldName(), userPrefs); FModel.getQuest().save(); // Save in preferences. diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/quest/VSubmenuQuestData.java b/forge-gui-desktop/src/main/java/forge/screens/home/quest/VSubmenuQuestData.java index 70e284faf3d..9eee9e58ca3 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/quest/VSubmenuQuestData.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/quest/VSubmenuQuestData.java @@ -62,7 +62,6 @@ public enum VSubmenuQuestData implements IVSubmenu { 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 cbxStartingWorld = new FComboBoxWrapper<>(); @@ -275,25 +274,9 @@ public enum VSubmenuQuestData implements IVSubmenu { } }); - // Fantasy box selected by Default + // Fantasy box enabled 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); @@ -303,7 +286,6 @@ public enum VSubmenuQuestData implements IVSubmenu { 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); @@ -505,14 +487,6 @@ public enum VSubmenuQuestData implements IVSubmenu { 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(); } diff --git a/forge-gui/res/quest/world/worlds.txt b/forge-gui/res/quest/world/worlds.txt index 5d99427d535..b08931cde59 100644 --- a/forge-gui/res/quest/world/worlds.txt +++ b/forge-gui/res/quest/world/worlds.txt @@ -1,6 +1,5 @@ Name:Main world Name:Random Standard -Name:Random Commander Name:Amonkhet|Dir:Amonkhet|Sets:AKH, HOU Name:Jamuraa|Dir:jamuraa|Sets:5ED, ARN, MIR, VIS, WTH|Banned:Chaos Orb; Falling Star Name:Kamigawa|Dir:2004 Kamigawa|Sets:CHK, BOK, SOK diff --git a/forge-gui/src/main/java/forge/quest/QuestController.java b/forge-gui/src/main/java/forge/quest/QuestController.java index 5366f7200bc..0d306f4431a 100644 --- a/forge-gui/src/main/java/forge/quest/QuestController.java +++ b/forge-gui/src/main/java/forge/quest/QuestController.java @@ -276,10 +276,9 @@ public class QuestController { public void newGame(final String name, final int difficulty, final QuestMode mode, final GameFormat formatPrizes, final boolean allowSetUnlocks, final Deck startingCards, final GameFormat formatStartingPool, - final String startingWorld, final StartingPoolPreferences userPrefs, - DeckConstructionRules dcr) { + final String startingWorld, final StartingPoolPreferences userPrefs) { - this.load(new QuestData(name, difficulty, mode, formatPrizes, allowSetUnlocks, startingWorld, dcr)); // pass awards and unlocks here + this.load(new QuestData(name, difficulty, mode, formatPrizes, allowSetUnlocks, startingWorld)); // pass awards and unlocks here if (startingCards != null) { this.myCards.addDeck(startingCards); @@ -436,12 +435,6 @@ public class QuestController { QuestWorld world = getWorld(); String path = ForgeConstants.DEFAULT_CHALLENGES_DIR; - //Use a variant specialized duel manager if this is a variant quest - switch(FModel.getQuest().getDeckConstructionRules()){ - case Default: break; - case Commander: this.duelManager = new QuestEventCommanderDuelManager(); return; - } - if (world != null) { if (world.getName().equals(QuestWorld.STANDARDWORLDNAME)) { @@ -456,6 +449,7 @@ public class QuestController { } this.duelManager = new QuestEventDuelManager(new File(path)); + } public HashSet GetRating() { @@ -613,6 +607,4 @@ public class QuestController { public void setCurrentDeck(String s) { model.currentDeck = s; } - - public DeckConstructionRules getDeckConstructionRules(){return model.deckConstructionRules;} } diff --git a/forge-gui/src/main/java/forge/quest/QuestEvent.java b/forge-gui/src/main/java/forge/quest/QuestEvent.java index 2a9bf6c1b63..00a140c80e2 100644 --- a/forge-gui/src/main/java/forge/quest/QuestEvent.java +++ b/forge-gui/src/main/java/forge/quest/QuestEvent.java @@ -48,7 +48,6 @@ public abstract class QuestEvent implements IQuestEvent { private String profile = "Default"; // Opponent name if different from the challenge name private String opponentName = null; - private boolean isRandomMatch = false; public static final Function FN_GET_NAME = new Function() { @@ -175,7 +174,4 @@ public abstract class QuestEvent implements IQuestEvent { this.showDifficulty = showDifficulty; } - public boolean getIsRandomMatch(){return isRandomMatch;} - - public void setIsRandomMatch(boolean b){isRandomMatch = b;} } diff --git a/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuel.java b/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuel.java deleted file mode 100644 index 1d7924a04d7..00000000000 --- a/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuel.java +++ /dev/null @@ -1,19 +0,0 @@ -package forge.quest; - -import forge.deck.DeckProxy; - -/** - * A QuestEventDuel with a CommanderDeckGenerator used exclusively within QuestEventCommanderDuelManager for the - * creation of randomly generated Commander decks in a Commander variant quest. - * Auth. Imakuni & Forge - */ -public class QuestEventCommanderDuel extends QuestEventDuel{ - /** - * The CommanderDeckGenerator for this duel. - */ - private DeckProxy deckProxy; - - public DeckProxy getDeckProxy() {return deckProxy;} - - public void setDeckProxy(DeckProxy dp) {deckProxy = dp;} -} diff --git a/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuelManager.java b/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuelManager.java deleted file mode 100644 index 384d9d11d77..00000000000 --- a/forge-gui/src/main/java/forge/quest/QuestEventCommanderDuelManager.java +++ /dev/null @@ -1,205 +0,0 @@ -package forge.quest; - -import forge.deck.*; -import forge.item.PaperCard; -import forge.model.FModel; -import forge.quest.data.QuestPreferences; -import forge.util.CollectionSuppliers; -import forge.util.MyRandom; -import forge.util.maps.EnumMapOfLists; -import forge.util.maps.MapOfLists; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Manages the creation of random Commander duels for a Commander variant quest. Random generation is handled via - * the CommanderDeckGenerator class. - * Auth. Forge & Imakuni#8015 - */ -public class QuestEventCommanderDuelManager implements QuestEventDuelManagerInterface { - /** - * The list of all possible Commander variant duels. - */ - private ArrayList commanderDuels = new ArrayList<>(); - - /** - * Contains the expert deck lists for the commanders. - */ - private List expertCommanderDecks; - - /** - * Immediately calls assembleDuels() to setup the commanderDuels variable. - */ - public QuestEventCommanderDuelManager(){ - assembleDuels(); - } - - /** - * Assembles the list of all possible Commander duels via CommanderDeckGenerator. Should be done within constructor. - */ - private void assembleDuels(){ - //isCardGen = true seemed to make slightly more difficult decks based purely on experience with a very small sample size. - //Gotta work on this more, its making pretty average decks after further testing. - expertCommanderDecks = CommanderDeckGenerator.getCommanderDecks(DeckFormat.Commander, true, true); - - List generatedDuels = CommanderDeckGenerator.getCommanderDecks(DeckFormat.Commander, true, false); - - for(DeckProxy dp : generatedDuels){ - QuestEventCommanderDuel duel = new QuestEventCommanderDuel(); - - duel.setDescription("Randomly generated " + dp.getName() + " commander deck."); - duel.setName(dp.getName()); - duel.setTitle(dp.getName()); - duel.setOpponentName(dp.getName()); - duel.setDifficulty(QuestEventDifficulty.EASY); - duel.setDeckProxy(dp); - - //Setting a blank deck avoids a null pointer exception. The deck is generated in generateDuels() to avoid long load times. - duel.setEventDeck(new Deck()); - - commanderDuels.add(duel); - } - } - - /** - * Retrieve list of all possible Commander duels. - * @return ArrayList containing all possible Commander duels. - */ - public Iterable getAllDuels() { - return commanderDuels; - } - - /** - * Retrieve list of all possible Commander duels. - * @param difficulty Currently unused - * @return ArrayList containing all possible Commander duels. - */ - public Iterable getDuels(QuestEventDifficulty difficulty){ - return commanderDuels; - } - - /** - * Composes an ArrayList containing 4 QuestEventDuels composed with Commander variant decks. One duel will have its - * title replaced as Random. - * @return ArrayList of QuestEventDuels containing 4 duels. - */ - public List generateDuels(){ - final List duelOpponents = new ArrayList<>(); - - //While there are less than 4 duels chosen - while(duelOpponents.size() < 4){ - //Get a random duel from the possible duels list - QuestEventCommanderDuel duel = (QuestEventCommanderDuel)commanderDuels.get(((int) (commanderDuels.size() * MyRandom.getRandom().nextDouble()))); - - //If the chosen duels list already contains this duel, get a different duel to prevent duplicate duels - if(duelOpponents.contains(duel)) continue; - - //Add the randomly chosen duel to the duel list - duelOpponents.add(duel); - - //Here the actual deck for this commander is generated by calling .getDeck() on the saved DeckProxy - duel.setEventDeck(duel.getDeckProxy().getDeck()); - - //Modify deck for difficulty - modifyDuelForDifficulty(duel); - - - } - - //Modify the stats of the final duel to hide the opponent, creating a "random" duel. - //We make a copy of the final duel and overwrite it in the duelOpponents to avoid changing the variables in - //the original duel, which gets reused. - QuestEventCommanderDuel duel = (QuestEventCommanderDuel)duelOpponents.get(duelOpponents.size() - 1); - QuestEventCommanderDuel randomDuel = new QuestEventCommanderDuel(); - - randomDuel.setName(duel.getName()); - randomDuel.setOpponentName(duel.getName()); - randomDuel.setDeckProxy(duel.getDeckProxy()); - randomDuel.setTitle("Random Opponent"); - randomDuel.setShowDifficulty(false); - randomDuel.setDescription("Fight a random generated commander opponent."); - randomDuel.setIsRandomMatch(true); - randomDuel.setEventDeck(duel.getEventDeck()); - - //Replace the final duel with this newly modified "random" duel - duelOpponents.set(duelOpponents.size()-1, randomDuel); - - return duelOpponents; - } - - /** - * Retrieves the expert level deck generation of a deck with the same commander as the provided DeckProxy. - * @param dp The easy generation commander deck - * @return The same commander's expert generation DeckProxy - */ - private Deck getExpertGenDeck(DeckProxy dp){ - for(QuestEventDuel qed : commanderDuels){ - QuestEventCommanderDuel cmdQED = (QuestEventCommanderDuel)qed; - if(cmdQED.getDeckProxy().getName().equals(dp.getName())){ - return cmdQED.getDeckProxy().getDeck(); - } - } - return null; - } - - /** - * Modifies a given duel by replacing a percentage of the deck with random cards from the more difficult generated version - * of the same commander's deck. Medium replaces 30%, Hard replaces 60%, Expert replaces 100%. - * @param duel The QuestEventCommanderDuel to modify - */ - private void modifyDuelForDifficulty(QuestEventCommanderDuel duel){ - final QuestPreferences questPreferences = FModel.getQuestPreferences(); - final int index = FModel.getQuest().getAchievements().getDifficulty(); - final int numberOfWins = FModel.getQuest().getAchievements().getWin(); - Deck expertDeck = getExpertGenDeck(duel.getDeckProxy()); - - int difficultyReplacementPercent = 0; - - //Note: The code is ordered to make the least number of comparisons I could think of at the time for speed reasons. - //In reality, it shouldn't really make much difference, but why not? - if (numberOfWins >= questPreferences.getPrefInt(QuestPreferences.DifficultyPrefs.WINS_EXPERTAI, index)) { - //At expert, the deck is replaced with the entire expert deck, and we can return immediately - duel.setEventDeck(expertDeck); - duel.setDifficulty(QuestEventDifficulty.EXPERT); - return; - } - - if (numberOfWins >= questPreferences.getPrefInt(QuestPreferences.DifficultyPrefs.WINS_MEDIUMAI, index)) { - difficultyReplacementPercent += 30; - duel.setDifficulty(QuestEventDifficulty.MEDIUM); - } else return; //return early here since it would be an easy opponent with no changes - - if (numberOfWins >= questPreferences.getPrefInt(QuestPreferences.DifficultyPrefs.WINS_HARDAI, index)) { - difficultyReplacementPercent += 30; - duel.setDifficulty(QuestEventDifficulty.HARD); - } - - CardPool easyMain = duel.getEventDeck().getMain(); - CardPool expertMain = expertDeck.getMain(); - - List easyList = easyMain.toFlatList(); - List expertList = expertMain.toFlatList(); - - //Replace cards in the easy deck with cards from the expert deck up to the difficulty replacement percent - for(int i = 0; i < difficultyReplacementPercent; i++){ - if(!easyMain.contains(expertList.get(i))) { //ensure that the card being copied over isn't already in the deck - easyMain.remove(easyList.get(i)); - easyMain.add(expertList.get(i)); - } - else{ - expertList.remove(expertList.get(i)); - i--; - if(expertList.size() == 0) break; //break if there are no more cards to copy over - } - } - } - - /** - * Randomizes the list of Commander Duels. - */ - public void randomizeOpponents(){ - Collections.shuffle(commanderDuels); - } -} diff --git a/forge-gui/src/main/java/forge/quest/QuestSpellShop.java b/forge-gui/src/main/java/forge/quest/QuestSpellShop.java index 96eae768963..e9196e417df 100644 --- a/forge-gui/src/main/java/forge/quest/QuestSpellShop.java +++ b/forge-gui/src/main/java/forge/quest/QuestSpellShop.java @@ -344,24 +344,11 @@ public class QuestSpellShop { List> cardsToRemove = new LinkedList<>(); for (Entry item : inventoryManager.getPool()) { PaperCard card = (PaperCard)item.getKey(); - //Number of a particular card to keep - int numToKeep = 4; - - if(card.getRules().getType().isBasic()){ - numToKeep = FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_BASIC_LAND_SIZE); - } else{ - //Choose card limit restrictions based on deck construction rules, e.g.: Commander allows only singletons - switch(FModel.getQuest().getDeckConstructionRules()){ - case Default: numToKeep = FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_SIZE); break; - case Commander: numToKeep = 1; - } - } - - //If this card has an exception to the card limit, e.g.: Relentless Rats, get the quest preference + int numToKeep = card.getRules().getType().isBasic() ? + FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_BASIC_LAND_SIZE) : FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_SIZE); if (DeckFormat.getLimitExceptions().contains(card.getName())) { numToKeep = FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_ANY_NUMBER_SIZE); } - if (numToKeep < item.getValue()) { cardsToRemove.add(Pair.of(item.getKey(), item.getValue() - numToKeep)); } diff --git a/forge-gui/src/main/java/forge/quest/QuestUtil.java b/forge-gui/src/main/java/forge/quest/QuestUtil.java index 7ad6b6cd335..c2fe796ef7b 100644 --- a/forge-gui/src/main/java/forge/quest/QuestUtil.java +++ b/forge-gui/src/main/java/forge/quest/QuestUtil.java @@ -41,7 +41,6 @@ import forge.properties.ForgePreferences.FPref; import forge.quest.bazaar.IQuestBazaarItem; import forge.quest.bazaar.QuestItemType; import forge.quest.bazaar.QuestPetController; -import forge.quest.data.DeckConstructionRules; import forge.quest.data.QuestAchievements; import forge.quest.data.QuestAssets; import forge.util.gui.SGuiChoose; @@ -52,7 +51,6 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; -import java.util.TreeSet; /** *

@@ -533,17 +531,7 @@ public class QuestUtil { Integer lifeHuman = null; boolean useBazaar = true; Boolean forceAnte = null; - - //Generate a life modifier based on this quest's variant as held in the Quest Controller's DeckConstructionRules - int variantLifeModifier = 0; - - switch(FModel.getQuest().getDeckConstructionRules()){ - case Default: break; - case Commander: variantLifeModifier = 20; break; - } - - int lifeAI = 20 + variantLifeModifier; - + int lifeAI = 20; if (event instanceof QuestEventChallenge) { final QuestEventChallenge qc = ((QuestEventChallenge) event); lifeAI = qc.getAILife(); @@ -557,9 +545,8 @@ public class QuestUtil { forceAnte = qc.isForceAnte(); } - final RegisteredPlayer humanStart = getRegisteredPlayerByVariant(getDeckForNewGame()); - - final RegisteredPlayer aiStart = getRegisteredPlayerByVariant(event.getEventDeck()); + final RegisteredPlayer humanStart = new RegisteredPlayer(getDeckForNewGame()); + final RegisteredPlayer aiStart = new RegisteredPlayer(event.getEventDeck()); if (lifeHuman != null) { humanStart.setStartingLife(lifeHuman); @@ -594,39 +581,17 @@ public class QuestUtil { rules.setGamesPerMatch(qData.getMatchLength()); rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN)); rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE)); - - TreeSet variant = new TreeSet(); - if(FModel.getQuest().getDeckConstructionRules() == DeckConstructionRules.Commander){ - variant.add(GameType.Commander); - } - final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch(); final IGuiGame gui = GuiBase.getInterface().getNewGuiGame(); gui.setPlayerAvatar(aiPlayer, event); FThreads.invokeInEdtNowOrLater(new Runnable(){ @Override public void run() { - hostedMatch.startMatch(rules, variant, starter, ImmutableMap.of(humanStart, gui)); + hostedMatch.startMatch(rules, null, starter, ImmutableMap.of(humanStart, gui)); } }); } - /** - * Uses the appropriate RegisteredPlayer command for generating a RegisteredPlayer based on this quest's variant as - * held by the QuestController's DeckConstructionRules. - * @param deck The deck to generate the RegisteredPlayer with - * @return A newly made RegisteredPlayer specific to the quest's variant - */ - private static RegisteredPlayer getRegisteredPlayerByVariant(Deck deck){ - switch (FModel.getQuest().getDeckConstructionRules()) { - case Default: - return new RegisteredPlayer(deck); - case Commander: - return RegisteredPlayer.forCommander(deck); - } - return null; - } - private static Deck getDeckForNewGame() { Deck deck = null; if (event instanceof QuestEventChallenge) { @@ -658,7 +623,7 @@ public class QuestUtil { } if (FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) { - final String errorMessage = getDeckConformanceProblems(deck); + final String errorMessage = GameType.Quest.getDeckFormat().getDeckConformanceProblem(deck); if (null != errorMessage) { SOptionPane.showErrorDialog("Your deck " + errorMessage + " Please edit or choose a different deck.", "Invalid Deck"); return false; @@ -668,21 +633,6 @@ public class QuestUtil { return true; } - public static String getDeckConformanceProblems(Deck deck){ - String errorMessage = GameType.Quest.getDeckFormat().getDeckConformanceProblem(deck);; - - if(errorMessage != null) return errorMessage; //return immediately if the deck does not conform to quest requirements - - //Check for all applicable deck construction rules per this quests's saved DeckConstructionRules enum - switch(FModel.getQuest().getDeckConstructionRules()){ - case Commander: - errorMessage = GameType.Commander.getDeckFormat().getDeckConformanceProblem(deck); - break; - } - - return errorMessage; - } - /** Duplicate in DeckEditorQuestMenu and * probably elsewhere...can streamline at some point * (probably shouldn't be here). diff --git a/forge-gui/src/main/java/forge/quest/QuestUtilCards.java b/forge-gui/src/main/java/forge/quest/QuestUtilCards.java index 54a4887ba8a..c8d77d863ad 100644 --- a/forge-gui/src/main/java/forge/quest/QuestUtilCards.java +++ b/forge-gui/src/main/java/forge/quest/QuestUtilCards.java @@ -308,16 +308,10 @@ public final class QuestUtilCards { * user preferences */ public void setupNewGameCardPool(final GameFormat formatStartingPool, final int idxDifficulty, final StartingPoolPreferences userPrefs) { - //Add additional cards to the starter card pool based on variant if applicable - double variantModifier = 0; - switch(FModel.getQuest().getDeckConstructionRules()){ - case Default: break; - case Commander: variantModifier = 2; break; - } - final int nC = (int)(questPreferences.getPrefInt(DifficultyPrefs.STARTING_COMMONS, idxDifficulty) * variantModifier); - final int nU = (int)(questPreferences.getPrefInt(DifficultyPrefs.STARTING_UNCOMMONS, idxDifficulty) * variantModifier); - final int nR = (int)(questPreferences.getPrefInt(DifficultyPrefs.STARTING_RARES, idxDifficulty) * variantModifier); + final int nC = questPreferences.getPrefInt(DifficultyPrefs.STARTING_COMMONS, idxDifficulty); + final int nU = questPreferences.getPrefInt(DifficultyPrefs.STARTING_UNCOMMONS, idxDifficulty); + final int nR = questPreferences.getPrefInt(DifficultyPrefs.STARTING_RARES, idxDifficulty); addAllCards(BoosterUtils.getQuestStarterDeck(formatStartingPool, nC, nU, nR, userPrefs)); diff --git a/forge-gui/src/main/java/forge/quest/QuestWinLoseController.java b/forge-gui/src/main/java/forge/quest/QuestWinLoseController.java index 265ea61bb58..2e93e7d4fec 100644 --- a/forge-gui/src/main/java/forge/quest/QuestWinLoseController.java +++ b/forge-gui/src/main/java/forge/quest/QuestWinLoseController.java @@ -226,11 +226,6 @@ public class QuestWinLoseController { sb.append(StringUtils.capitalize(qEvent.getDifficulty().getTitle())); sb.append(" opponent: ").append(credBase).append(" credits.\n"); - if(qEvent.getIsRandomMatch()){ - sb.append("Random Opponent Bonus: " + credBase + " credit" + (credBase > 1 ? "s." : ".") + "\n"); - credBase += credBase; - } - final int winMultiplier = Math.min(qData.getAchievements().getWin(), FModel.getQuestPreferences().getPrefInt(QPref.REWARDS_WINS_MULTIPLIER_MAX)); final int creditsForPreviousWins = (int) ((Double.parseDouble(FModel.getQuestPreferences() .getPref(QPref.REWARDS_WINS_MULTIPLIER)) * winMultiplier)); diff --git a/forge-gui/src/main/java/forge/quest/QuestWorld.java b/forge-gui/src/main/java/forge/quest/QuestWorld.java index 4c77b0ec515..e20dcf98ce8 100644 --- a/forge-gui/src/main/java/forge/quest/QuestWorld.java +++ b/forge-gui/src/main/java/forge/quest/QuestWorld.java @@ -40,7 +40,6 @@ public class QuestWorld implements Comparable{ private final String dir; private final GameFormatQuest format; public static final String STANDARDWORLDNAME = "Random Standard"; - public static final String RANDOMCOMMANDERWORLDNAME = "Random Commander"; private boolean isCustom; @@ -130,6 +129,7 @@ public class QuestWorld implements Comparable{ /** * TODO: Write javadoc for Constructor. * @param file0 + * @param keySelector0 */ public Reader(String file0) { super(file0, QuestWorld.FN_GET_NAME); @@ -194,12 +194,6 @@ public class QuestWorld implements Comparable{ FModel.getFormats().getStandard().getBannedCardNames(),false); } - if (useName.equalsIgnoreCase(QuestWorld.RANDOMCOMMANDERWORLDNAME)){ - useFormat = new GameFormatQuest(QuestWorld.RANDOMCOMMANDERWORLDNAME, - FModel.getFormats().getFormat("Commander").getAllowedSetCodes(), - FModel.getFormats().getFormat("Commander").getBannedCardNames(),false); - } - // System.out.println("Creating quest world " + useName + " (index " + useIdx + ", dir: " + useDir); // if (useFormat != null) { System.out.println("SETS: " + sets + "\nBANNED: " + bannedCards); } diff --git a/forge-gui/src/main/java/forge/quest/data/DeckConstructionRules.java b/forge-gui/src/main/java/forge/quest/data/DeckConstructionRules.java deleted file mode 100644 index 3744beea09d..00000000000 --- a/forge-gui/src/main/java/forge/quest/data/DeckConstructionRules.java +++ /dev/null @@ -1,17 +0,0 @@ -package forge.quest.data; - -/** - * Used to clarify which subformat a quest is using e.g. Commander. - * Auth. Imakuni - */ -public enum DeckConstructionRules { - /** - * Typically has no effect on Quest gameplay. - */ - Default, - - /** - * Commander ruleset. 99 card deck, no copies other than basic lands, commander(s) in Command zone - */ - Commander -} diff --git a/forge-gui/src/main/java/forge/quest/data/QuestAssets.java b/forge-gui/src/main/java/forge/quest/data/QuestAssets.java index 8f7b644641b..616c49d937b 100644 --- a/forge-gui/src/main/java/forge/quest/data/QuestAssets.java +++ b/forge-gui/src/main/java/forge/quest/data/QuestAssets.java @@ -200,14 +200,7 @@ public class QuestAssets { * @return the life */ public int getLife(final QuestMode mode) { - int base = mode.equals(QuestMode.Fantasy) ? 15 : 20; - - //Modify life for the quest's sub-format, e.g.: Commander adds 20 - switch(FModel.getQuest().getDeckConstructionRules()){ - case Default: break; - case Commander: base += 20; - } - + final int base = mode.equals(QuestMode.Fantasy) ? 15 : 20; return (base + this.getItemLevel(QuestItemType.ELIXIR_OF_LIFE)) - this.getItemLevel(QuestItemType.POUND_FLESH); } diff --git a/forge-gui/src/main/java/forge/quest/data/QuestData.java b/forge-gui/src/main/java/forge/quest/data/QuestData.java index 41574634113..e6a75adef7d 100644 --- a/forge-gui/src/main/java/forge/quest/data/QuestData.java +++ b/forge-gui/src/main/java/forge/quest/data/QuestData.java @@ -42,7 +42,7 @@ import java.util.Map; */ public final class QuestData { /** Holds the latest version of the Quest Data. */ - public static final int CURRENT_VERSION_NUMBER = 13; + public static final int CURRENT_VERSION_NUMBER = 12; // This field places the version number into QD instance, // but only when the object is created through the constructor @@ -70,11 +70,6 @@ public final class QuestData { public String currentDeck = "DEFAULT"; - /** - * Holds the subformat for this quest. Defaults to DeckConstructionRules.Default. - */ - public DeckConstructionRules deckConstructionRules = DeckConstructionRules.Default; - public QuestData() { //needed for XML serialization } @@ -92,11 +87,9 @@ public final class QuestData { * allow set unlocking during quest * @param startingWorld * starting world - * @param dcr - * deck construction rules e.g. Commander */ public QuestData(String name0, int diff, QuestMode mode0, GameFormat userFormat, - boolean allowSetUnlocks, final String startingWorld, DeckConstructionRules dcr) { + boolean allowSetUnlocks, final String startingWorld) { this.name = name0; if (userFormat != null) { @@ -106,7 +99,6 @@ public final class QuestData { this.achievements = new QuestAchievements(diff); this.assets = new QuestAssets(format); this.worldId = startingWorld; - this.deckConstructionRules = dcr; } /** diff --git a/forge-gui/src/main/java/forge/quest/io/QuestDataIO.java b/forge-gui/src/main/java/forge/quest/io/QuestDataIO.java index cd232367df9..de680502e37 100644 --- a/forge-gui/src/main/java/forge/quest/io/QuestDataIO.java +++ b/forge-gui/src/main/java/forge/quest/io/QuestDataIO.java @@ -223,16 +223,10 @@ public class QuestDataIO { // Current Deck moved from preferences to quest data - it should not be global for all quests!!! QuestDataIO.setFinalField(QuestData.class, "currentDeck", newData, FModel.getQuestPreferences().getPref(QPref.CURRENT_DECK)); } - if(saveVersion < 13){ - //Update for quest DeckConstructionRules - //Add a DeckConstructionRules set to Default. - QuestDataIO.setFinalField(QuestData.class, "deckConstructionRules", newData, DeckConstructionRules.Default); - } - if (saveVersion < 14) { + if (saveVersion < 13) { // Migrate DraftTournaments to use new Tournament class } - final QuestAssets qS = newData.getAssets(); final QuestAchievements qA = newData.getAchievements();