mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +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:
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package forge.deck;
|
package forge.deck;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
import forge.card.CardDb;
|
import forge.card.CardDb;
|
||||||
@@ -216,4 +217,17 @@ public class CardPool extends ItemPool<PaperCard> {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
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<PaperCard> predicate){
|
||||||
|
CardPool filteredPool = new CardPool();
|
||||||
|
for(PaperCard pc : this.items.keySet()){
|
||||||
|
if(predicate.apply(pc)) filteredPool.add(pc);
|
||||||
|
}
|
||||||
|
return filteredPool;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ public class GameRules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasCommander() {
|
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);
|
|| appliedVariants.contains(GameType.Brawl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,20 @@
|
|||||||
package forge.screens.deckeditor.controllers;
|
package forge.screens.deckeditor.controllers;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
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.base.Supplier;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import forge.UiCommand;
|
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.CardPool;
|
||||||
import forge.deck.Deck;
|
import forge.deck.Deck;
|
||||||
import forge.deck.DeckSection;
|
import forge.deck.DeckSection;
|
||||||
|
import forge.deck.generation.DeckGeneratorBase;
|
||||||
import forge.gui.GuiUtils;
|
import forge.gui.GuiUtils;
|
||||||
import forge.gui.framework.DragCell;
|
import forge.gui.framework.DragCell;
|
||||||
import forge.gui.framework.FScreen;
|
import forge.gui.framework.FScreen;
|
||||||
@@ -35,6 +44,7 @@ import forge.itemmanager.views.ItemTableColumn;
|
|||||||
import forge.model.FModel;
|
import forge.model.FModel;
|
||||||
import forge.properties.ForgePreferences.FPref;
|
import forge.properties.ForgePreferences.FPref;
|
||||||
import forge.quest.QuestController;
|
import forge.quest.QuestController;
|
||||||
|
import forge.quest.data.DeckConstructionRules;
|
||||||
import forge.screens.deckeditor.AddBasicLandsDialog;
|
import forge.screens.deckeditor.AddBasicLandsDialog;
|
||||||
import forge.screens.deckeditor.SEditorIO;
|
import forge.screens.deckeditor.SEditorIO;
|
||||||
import forge.screens.deckeditor.views.VAllDecks;
|
import forge.screens.deckeditor.views.VAllDecks;
|
||||||
@@ -48,6 +58,7 @@ import forge.util.ItemPool;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.print.Paper;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -103,6 +114,14 @@ public final class CEditorQuest extends CDeckEditor<Deck> {
|
|||||||
allSections.add(DeckSection.Main);
|
allSections.add(DeckSection.Main);
|
||||||
allSections.add(DeckSection.Sideboard);
|
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;
|
this.questData = questData0;
|
||||||
|
|
||||||
final CardManager catalogManager = new CardManager(cDetailPicture, false, true);
|
final CardManager catalogManager = new CardManager(cDetailPicture, false, true);
|
||||||
@@ -158,6 +177,10 @@ public final class CEditorQuest extends CDeckEditor<Deck> {
|
|||||||
@Override
|
@Override
|
||||||
protected CardLimit getCardLimit() {
|
protected CardLimit getCardLimit() {
|
||||||
if (FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
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.Default;
|
||||||
}
|
}
|
||||||
return CardLimit.None; //if not enforcing deck legality, don't enforce default limit
|
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() {
|
public void resetTables() {
|
||||||
this.sectionMode = DeckSection.Main;
|
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
|
// show cards, makes this user friendly
|
||||||
this.getCatalogManager().setPool(cardpool);
|
this.getCatalogManager().setPool(getRemainingCardPool());
|
||||||
this.getDeckManager().setPool(deck.getMain());
|
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
|
@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) {
|
public void setEditorMode(DeckSection sectionMode) {
|
||||||
if (sectionMode == DeckSection.Sideboard) {
|
//Fixes null pointer error on switching tabs while quest deck editor is open. TODO: Find source of bug possibly?
|
||||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Sideboard));
|
if(sectionMode == null) sectionMode = DeckSection.Main;
|
||||||
}
|
|
||||||
else {
|
//Based on which section the editor is in, display the remaining card pool (or applicable card pool if in
|
||||||
this.getDeckManager().setPool(this.controller.getModel().getMain());
|
//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;
|
this.sectionMode = sectionMode;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import forge.model.FModel;
|
|||||||
import forge.properties.ForgeConstants;
|
import forge.properties.ForgeConstants;
|
||||||
import forge.quest.*;
|
import forge.quest.*;
|
||||||
import forge.quest.StartingPoolPreferences.PoolType;
|
import forge.quest.StartingPoolPreferences.PoolType;
|
||||||
|
import forge.quest.data.DeckConstructionRules;
|
||||||
import forge.quest.data.GameFormatQuest;
|
import forge.quest.data.GameFormatQuest;
|
||||||
import forge.quest.data.QuestData;
|
import forge.quest.data.QuestData;
|
||||||
import forge.quest.data.QuestPreferences.QPref;
|
import forge.quest.data.QuestPreferences.QPref;
|
||||||
@@ -340,9 +341,16 @@ public enum CSubmenuQuestData implements ICDoc {
|
|||||||
break;
|
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();
|
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();
|
FModel.getQuest().save();
|
||||||
|
|
||||||
// Save in preferences.
|
// Save in preferences.
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ public enum VSubmenuQuestData implements IVSubmenu<CSubmenuQuestData> {
|
|||||||
private final FRadioButton radHard = new FRadioButton("Hard");
|
private final FRadioButton radHard = new FRadioButton("Hard");
|
||||||
private final FRadioButton radExpert = new FRadioButton("Expert");
|
private final FRadioButton radExpert = new FRadioButton("Expert");
|
||||||
private final FCheckBox boxFantasy = new FCheckBox("Fantasy Mode");
|
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 FLabel lblStartingWorld = new FLabel.Builder().text("Starting world:").build();
|
||||||
private final FComboBoxWrapper<QuestWorld> cbxStartingWorld = new FComboBoxWrapper<>();
|
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.setSelected(true);
|
||||||
boxFantasy.setEnabled(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);
|
boxCompleteSet.setEnabled(true);
|
||||||
boxAllowDuplicates.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"));
|
final JPanel pnlDifficultyMode = new JPanel(new MigLayout("insets 0, gap 1%, flowy"));
|
||||||
pnlDifficultyMode.add(difficultyPanel, "gapright 4%");
|
pnlDifficultyMode.add(difficultyPanel, "gapright 4%");
|
||||||
pnlDifficultyMode.add(boxFantasy, "h 25px!, gapbottom 15, 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");
|
pnlDifficultyMode.add(lblStartingWorld, "h 25px!, hidemode 3");
|
||||||
cbxStartingWorld.addTo(pnlDifficultyMode, "h 27px!, w 40%, pushx, gapbottom 7");
|
cbxStartingWorld.addTo(pnlDifficultyMode, "h 27px!, w 40%, pushx, gapbottom 7");
|
||||||
pnlDifficultyMode.setOpaque(false);
|
pnlDifficultyMode.setOpaque(false);
|
||||||
@@ -487,6 +505,14 @@ public enum VSubmenuQuestData implements IVSubmenu<CSubmenuQuestData> {
|
|||||||
return boxFantasy.isSelected();
|
return boxFantasy.isSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auth. Imakuni
|
||||||
|
* @return True if the "Commander Subformat" check box is selected.
|
||||||
|
*/
|
||||||
|
public boolean isCommander() {
|
||||||
|
return boxCommander.isSelected();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean startWithCompleteSet() {
|
public boolean startWithCompleteSet() {
|
||||||
return boxCompleteSet.isSelected();
|
return boxCompleteSet.isSelected();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
Name:Main world
|
Name:Main world
|
||||||
Name:Random Standard
|
Name:Random Standard
|
||||||
|
Name:Random Commander
|
||||||
Name:Amonkhet|Dir:Amonkhet|Sets:AKH, HOU
|
Name:Amonkhet|Dir:Amonkhet|Sets:AKH, HOU
|
||||||
Name:Jamuraa|Dir:jamuraa|Sets:5ED, ARN, MIR, VIS, WTH|Banned:Chaos Orb; Falling Star
|
Name:Jamuraa|Dir:jamuraa|Sets:5ED, ARN, MIR, VIS, WTH|Banned:Chaos Orb; Falling Star
|
||||||
Name:Kamigawa|Dir:2004 Kamigawa|Sets:CHK, BOK, SOK
|
Name:Kamigawa|Dir:2004 Kamigawa|Sets:CHK, BOK, SOK
|
||||||
|
|||||||
@@ -276,9 +276,10 @@ public class QuestController {
|
|||||||
public void newGame(final String name, final int difficulty, final QuestMode mode,
|
public void newGame(final String name, final int difficulty, final QuestMode mode,
|
||||||
final GameFormat formatPrizes, final boolean allowSetUnlocks,
|
final GameFormat formatPrizes, final boolean allowSetUnlocks,
|
||||||
final Deck startingCards, final GameFormat formatStartingPool,
|
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) {
|
if (startingCards != null) {
|
||||||
this.myCards.addDeck(startingCards);
|
this.myCards.addDeck(startingCards);
|
||||||
@@ -435,6 +436,12 @@ public class QuestController {
|
|||||||
QuestWorld world = getWorld();
|
QuestWorld world = getWorld();
|
||||||
String path = ForgeConstants.DEFAULT_CHALLENGES_DIR;
|
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 != null) {
|
||||||
|
|
||||||
if (world.getName().equals(QuestWorld.STANDARDWORLDNAME)) {
|
if (world.getName().equals(QuestWorld.STANDARDWORLDNAME)) {
|
||||||
@@ -449,7 +456,6 @@ public class QuestController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.duelManager = new QuestEventDuelManager(new File(path));
|
this.duelManager = new QuestEventDuelManager(new File(path));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<StarRating> GetRating() {
|
public HashSet<StarRating> GetRating() {
|
||||||
@@ -607,4 +613,6 @@ public class QuestController {
|
|||||||
public void setCurrentDeck(String s) {
|
public void setCurrentDeck(String s) {
|
||||||
model.currentDeck = s;
|
model.currentDeck = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DeckConstructionRules getDeckConstructionRules(){return model.deckConstructionRules;}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ public abstract class QuestEvent implements IQuestEvent {
|
|||||||
private String profile = "Default";
|
private String profile = "Default";
|
||||||
// Opponent name if different from the challenge name
|
// Opponent name if different from the challenge name
|
||||||
private String opponentName = null;
|
private String opponentName = null;
|
||||||
|
private boolean isRandomMatch = false;
|
||||||
|
|
||||||
|
|
||||||
public static final Function<QuestEvent, String> FN_GET_NAME = new Function<QuestEvent, String>() {
|
public static final Function<QuestEvent, String> FN_GET_NAME = new Function<QuestEvent, String>() {
|
||||||
@@ -174,4 +175,7 @@ public abstract class QuestEvent implements IQuestEvent {
|
|||||||
this.showDifficulty = showDifficulty;
|
this.showDifficulty = showDifficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getIsRandomMatch(){return isRandomMatch;}
|
||||||
|
|
||||||
|
public void setIsRandomMatch(boolean b){isRandomMatch = b;}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;}
|
||||||
|
}
|
||||||
@@ -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<QuestEventDuel> commanderDuels = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the expert deck lists for the commanders.
|
||||||
|
*/
|
||||||
|
private List<DeckProxy> 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<DeckProxy> 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<QuestEventDuel> getAllDuels() {
|
||||||
|
return commanderDuels;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve list of all possible Commander duels.
|
||||||
|
* @param difficulty Currently unused
|
||||||
|
* @return ArrayList containing all possible Commander duels.
|
||||||
|
*/
|
||||||
|
public Iterable<QuestEventDuel> 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<QuestEventDuel> generateDuels(){
|
||||||
|
final List<QuestEventDuel> 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<PaperCard> easyList = easyMain.toFlatList();
|
||||||
|
List<PaperCard> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -344,11 +344,24 @@ public class QuestSpellShop {
|
|||||||
List<Entry<InventoryItem, Integer>> cardsToRemove = new LinkedList<>();
|
List<Entry<InventoryItem, Integer>> cardsToRemove = new LinkedList<>();
|
||||||
for (Entry<InventoryItem, Integer> item : inventoryManager.getPool()) {
|
for (Entry<InventoryItem, Integer> item : inventoryManager.getPool()) {
|
||||||
PaperCard card = (PaperCard)item.getKey();
|
PaperCard card = (PaperCard)item.getKey();
|
||||||
int numToKeep = card.getRules().getType().isBasic() ?
|
//Number of a particular card to keep
|
||||||
FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_BASIC_LAND_SIZE) : FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_SIZE);
|
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())) {
|
if (DeckFormat.getLimitExceptions().contains(card.getName())) {
|
||||||
numToKeep = FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_ANY_NUMBER_SIZE);
|
numToKeep = FModel.getQuestPreferences().getPrefInt(QPref.PLAYSET_ANY_NUMBER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numToKeep < item.getValue()) {
|
if (numToKeep < item.getValue()) {
|
||||||
cardsToRemove.add(Pair.of(item.getKey(), item.getValue() - numToKeep));
|
cardsToRemove.add(Pair.of(item.getKey(), item.getValue() - numToKeep));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import forge.properties.ForgePreferences.FPref;
|
|||||||
import forge.quest.bazaar.IQuestBazaarItem;
|
import forge.quest.bazaar.IQuestBazaarItem;
|
||||||
import forge.quest.bazaar.QuestItemType;
|
import forge.quest.bazaar.QuestItemType;
|
||||||
import forge.quest.bazaar.QuestPetController;
|
import forge.quest.bazaar.QuestPetController;
|
||||||
|
import forge.quest.data.DeckConstructionRules;
|
||||||
import forge.quest.data.QuestAchievements;
|
import forge.quest.data.QuestAchievements;
|
||||||
import forge.quest.data.QuestAssets;
|
import forge.quest.data.QuestAssets;
|
||||||
import forge.util.gui.SGuiChoose;
|
import forge.util.gui.SGuiChoose;
|
||||||
@@ -51,6 +52,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
|
|||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -531,7 +533,17 @@ public class QuestUtil {
|
|||||||
Integer lifeHuman = null;
|
Integer lifeHuman = null;
|
||||||
boolean useBazaar = true;
|
boolean useBazaar = true;
|
||||||
Boolean forceAnte = null;
|
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) {
|
if (event instanceof QuestEventChallenge) {
|
||||||
final QuestEventChallenge qc = ((QuestEventChallenge) event);
|
final QuestEventChallenge qc = ((QuestEventChallenge) event);
|
||||||
lifeAI = qc.getAILife();
|
lifeAI = qc.getAILife();
|
||||||
@@ -545,8 +557,9 @@ public class QuestUtil {
|
|||||||
forceAnte = qc.isForceAnte();
|
forceAnte = qc.isForceAnte();
|
||||||
}
|
}
|
||||||
|
|
||||||
final RegisteredPlayer humanStart = new RegisteredPlayer(getDeckForNewGame());
|
final RegisteredPlayer humanStart = getRegisteredPlayerByVariant(getDeckForNewGame());
|
||||||
final RegisteredPlayer aiStart = new RegisteredPlayer(event.getEventDeck());
|
|
||||||
|
final RegisteredPlayer aiStart = getRegisteredPlayerByVariant(event.getEventDeck());
|
||||||
|
|
||||||
if (lifeHuman != null) {
|
if (lifeHuman != null) {
|
||||||
humanStart.setStartingLife(lifeHuman);
|
humanStart.setStartingLife(lifeHuman);
|
||||||
@@ -581,17 +594,39 @@ public class QuestUtil {
|
|||||||
rules.setGamesPerMatch(qData.getMatchLength());
|
rules.setGamesPerMatch(qData.getMatchLength());
|
||||||
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
|
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
|
||||||
rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE));
|
rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE));
|
||||||
|
|
||||||
|
TreeSet<GameType> variant = new TreeSet();
|
||||||
|
if(FModel.getQuest().getDeckConstructionRules() == DeckConstructionRules.Commander){
|
||||||
|
variant.add(GameType.Commander);
|
||||||
|
}
|
||||||
|
|
||||||
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
|
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
|
||||||
final IGuiGame gui = GuiBase.getInterface().getNewGuiGame();
|
final IGuiGame gui = GuiBase.getInterface().getNewGuiGame();
|
||||||
gui.setPlayerAvatar(aiPlayer, event);
|
gui.setPlayerAvatar(aiPlayer, event);
|
||||||
FThreads.invokeInEdtNowOrLater(new Runnable(){
|
FThreads.invokeInEdtNowOrLater(new Runnable(){
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
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() {
|
private static Deck getDeckForNewGame() {
|
||||||
Deck deck = null;
|
Deck deck = null;
|
||||||
if (event instanceof QuestEventChallenge) {
|
if (event instanceof QuestEventChallenge) {
|
||||||
@@ -623,7 +658,7 @@ public class QuestUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
if (FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
||||||
final String errorMessage = GameType.Quest.getDeckFormat().getDeckConformanceProblem(deck);
|
final String errorMessage = getDeckConformanceProblems(deck);
|
||||||
if (null != errorMessage) {
|
if (null != errorMessage) {
|
||||||
SOptionPane.showErrorDialog("Your deck " + errorMessage + " Please edit or choose a different deck.", "Invalid Deck");
|
SOptionPane.showErrorDialog("Your deck " + errorMessage + " Please edit or choose a different deck.", "Invalid Deck");
|
||||||
return false;
|
return false;
|
||||||
@@ -633,6 +668,21 @@ public class QuestUtil {
|
|||||||
return true;
|
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
|
/** Duplicate in DeckEditorQuestMenu and
|
||||||
* probably elsewhere...can streamline at some point
|
* probably elsewhere...can streamline at some point
|
||||||
* (probably shouldn't be here).
|
* (probably shouldn't be here).
|
||||||
|
|||||||
@@ -308,10 +308,16 @@ public final class QuestUtilCards {
|
|||||||
* user preferences
|
* user preferences
|
||||||
*/
|
*/
|
||||||
public void setupNewGameCardPool(final GameFormat formatStartingPool, final int idxDifficulty, final StartingPoolPreferences userPrefs) {
|
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 nC = (int)(questPreferences.getPrefInt(DifficultyPrefs.STARTING_COMMONS, idxDifficulty) * variantModifier);
|
||||||
final int nU = questPreferences.getPrefInt(DifficultyPrefs.STARTING_UNCOMMONS, idxDifficulty);
|
final int nU = (int)(questPreferences.getPrefInt(DifficultyPrefs.STARTING_UNCOMMONS, idxDifficulty) * variantModifier);
|
||||||
final int nR = questPreferences.getPrefInt(DifficultyPrefs.STARTING_RARES, idxDifficulty);
|
final int nR = (int)(questPreferences.getPrefInt(DifficultyPrefs.STARTING_RARES, idxDifficulty) * variantModifier);
|
||||||
|
|
||||||
addAllCards(BoosterUtils.getQuestStarterDeck(formatStartingPool, nC, nU, nR, userPrefs));
|
addAllCards(BoosterUtils.getQuestStarterDeck(formatStartingPool, nC, nU, nR, userPrefs));
|
||||||
|
|
||||||
|
|||||||
@@ -226,6 +226,11 @@ public class QuestWinLoseController {
|
|||||||
sb.append(StringUtils.capitalize(qEvent.getDifficulty().getTitle()));
|
sb.append(StringUtils.capitalize(qEvent.getDifficulty().getTitle()));
|
||||||
sb.append(" opponent: ").append(credBase).append(" credits.\n");
|
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 winMultiplier = Math.min(qData.getAchievements().getWin(), FModel.getQuestPreferences().getPrefInt(QPref.REWARDS_WINS_MULTIPLIER_MAX));
|
||||||
final int creditsForPreviousWins = (int) ((Double.parseDouble(FModel.getQuestPreferences()
|
final int creditsForPreviousWins = (int) ((Double.parseDouble(FModel.getQuestPreferences()
|
||||||
.getPref(QPref.REWARDS_WINS_MULTIPLIER)) * winMultiplier));
|
.getPref(QPref.REWARDS_WINS_MULTIPLIER)) * winMultiplier));
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public class QuestWorld implements Comparable<QuestWorld>{
|
|||||||
private final String dir;
|
private final String dir;
|
||||||
private final GameFormatQuest format;
|
private final GameFormatQuest format;
|
||||||
public static final String STANDARDWORLDNAME = "Random Standard";
|
public static final String STANDARDWORLDNAME = "Random Standard";
|
||||||
|
public static final String RANDOMCOMMANDERWORLDNAME = "Random Commander";
|
||||||
|
|
||||||
private boolean isCustom;
|
private boolean isCustom;
|
||||||
|
|
||||||
@@ -129,7 +130,6 @@ public class QuestWorld implements Comparable<QuestWorld>{
|
|||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for Constructor.
|
* TODO: Write javadoc for Constructor.
|
||||||
* @param file0
|
* @param file0
|
||||||
* @param keySelector0
|
|
||||||
*/
|
*/
|
||||||
public Reader(String file0) {
|
public Reader(String file0) {
|
||||||
super(file0, QuestWorld.FN_GET_NAME);
|
super(file0, QuestWorld.FN_GET_NAME);
|
||||||
@@ -194,6 +194,12 @@ public class QuestWorld implements Comparable<QuestWorld>{
|
|||||||
FModel.getFormats().getStandard().getBannedCardNames(),false);
|
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);
|
// System.out.println("Creating quest world " + useName + " (index " + useIdx + ", dir: " + useDir);
|
||||||
// if (useFormat != null) { System.out.println("SETS: " + sets + "\nBANNED: " + bannedCards); }
|
// if (useFormat != null) { System.out.println("SETS: " + sets + "\nBANNED: " + bannedCards); }
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -200,7 +200,14 @@ public class QuestAssets {
|
|||||||
* @return the life
|
* @return the life
|
||||||
*/
|
*/
|
||||||
public int getLife(final QuestMode mode) {
|
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);
|
return (base + this.getItemLevel(QuestItemType.ELIXIR_OF_LIFE)) - this.getItemLevel(QuestItemType.POUND_FLESH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public final class QuestData {
|
public final class QuestData {
|
||||||
/** Holds the latest version of the Quest Data. */
|
/** 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,
|
// This field places the version number into QD instance,
|
||||||
// but only when the object is created through the constructor
|
// but only when the object is created through the constructor
|
||||||
@@ -70,6 +70,11 @@ public final class QuestData {
|
|||||||
|
|
||||||
public String currentDeck = "DEFAULT";
|
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
|
public QuestData() { //needed for XML serialization
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,9 +92,11 @@ public final class QuestData {
|
|||||||
* allow set unlocking during quest
|
* allow set unlocking during quest
|
||||||
* @param startingWorld
|
* @param startingWorld
|
||||||
* starting world
|
* starting world
|
||||||
|
* @param dcr
|
||||||
|
* deck construction rules e.g. Commander
|
||||||
*/
|
*/
|
||||||
public QuestData(String name0, int diff, QuestMode mode0, GameFormat userFormat,
|
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;
|
this.name = name0;
|
||||||
|
|
||||||
if (userFormat != null) {
|
if (userFormat != null) {
|
||||||
@@ -99,6 +106,7 @@ public final class QuestData {
|
|||||||
this.achievements = new QuestAchievements(diff);
|
this.achievements = new QuestAchievements(diff);
|
||||||
this.assets = new QuestAssets(format);
|
this.assets = new QuestAssets(format);
|
||||||
this.worldId = startingWorld;
|
this.worldId = startingWorld;
|
||||||
|
this.deckConstructionRules = dcr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -223,10 +223,16 @@ public class QuestDataIO {
|
|||||||
// Current Deck moved from preferences to quest data - it should not be global for all quests!!!
|
// 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));
|
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
|
// Migrate DraftTournaments to use new Tournament class
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final QuestAssets qS = newData.getAssets();
|
final QuestAssets qS = newData.getAssets();
|
||||||
final QuestAchievements qA = newData.getAchievements();
|
final QuestAchievements qA = newData.getAchievements();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user