diff --git a/.gitattributes b/.gitattributes index 358e34c54ef..b67fb1e5020 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12044,6 +12044,7 @@ src/main/java/forge/card/FormatCollection.java -text src/main/java/forge/card/MetaSet.java -text src/main/java/forge/card/MtgDataParser.java -text src/main/java/forge/card/TriggerReplacementBase.java -text +src/main/java/forge/card/UnOpenedMeta.java -text src/main/java/forge/card/UnOpenedProduct.java -text src/main/java/forge/card/abilityfactory/AbilityFactory.java svneol=native#text/plain src/main/java/forge/card/abilityfactory/AbilityFactoryAlterLife.java svneol=native#text/plain diff --git a/src/main/java/forge/card/CardBlock.java b/src/main/java/forge/card/CardBlock.java index 532d10f2f77..32573800684 100644 --- a/src/main/java/forge/card/CardBlock.java +++ b/src/main/java/forge/card/CardBlock.java @@ -300,8 +300,6 @@ public final class CardBlock implements Comparable { */ public UnOpenedProduct getBooster(final String code) { - System.out.println("Booster called..."); - if (this.getNumberMetaSets() < 1) { throw new RuntimeException("Attempted to get a booster pack for empty metasets."); } diff --git a/src/main/java/forge/card/MetaSet.java b/src/main/java/forge/card/MetaSet.java index 2337d31803c..e3b72492cf8 100644 --- a/src/main/java/forge/card/MetaSet.java +++ b/src/main/java/forge/card/MetaSet.java @@ -1,6 +1,25 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package forge.card; import java.io.File; +import java.util.ArrayList; import java.util.List; import forge.Singletons; @@ -23,7 +42,8 @@ import forge.util.closures.Lambda1; * * where "X" is an integer from 0...8 (just like for sets) * - * "A" is either "cube", "meta", or "full". + * "A" is either "cube", "meta", "full", "choose1", "random1", + * "set", "pack", or "combo". * * "full" uses all available cards for this booster, just like * the full cardpool option. The values of "B" and "C" are not @@ -47,6 +67,28 @@ import forge.util.closures.Lambda1; * "C" is the name that is displayed for this meta-booster * in the set selection menu. * + * The new types added after beta 1.2.14: + * + * "choose1": define several metasets in a semicolon-separated (;) + * list in value B, the player will choose one of them. + * + * "random1": like choose1, except that the player will get a + * random pack. + * + * "combo": define several metasets in a semicolon-separated (;) + * list in value B, the booster will be based on the combined + * cardpool of all these. Note that if one of the metasets is + * a "full", the rest are irrelevant. + * + * "booster": generate a single booster based on value B set. (You + * should use this only for combo, choose1 and random1 metasets, + * otherwise use normal Sets instead of MetaSets in the block + * definition!) + * + * "pack": like set, but attempts to generate a starter pack instead + * of a booster. If starter packs are not available for value B set, + * a booster is generated instead. + * */ public class MetaSet { @@ -64,24 +106,40 @@ public class MetaSet { */ public MetaSet(final String creationString) { - final String[] kv = creationString.split("/", 3); + + String[] kv = new String [3]; + kv[0] = creationString.substring(0, creationString.indexOf('/')); + kv[1] = creationString.substring(creationString.indexOf('/') + 1, creationString.lastIndexOf('/')); + kv[2] = creationString.substring(creationString.lastIndexOf('/') + 1); + // Display the parse results: + // System.out.println("KV = '" + kv[0] + "', '" + kv[1] + "', '" + kv[2] + "'"); type = kv[0]; data = kv[1]; - // boosterGen = null; - if ("cube".equalsIgnoreCase(type)) { code = "*C:" + kv[2]; - //System.out.println("Created a CUBE, code '" + code + "'"); } else if ("full".equalsIgnoreCase(type)) { code = "*FULL"; - //System.out.println("Selecting from FULL cardpool'" + code + "'"); } else if ("meta".equalsIgnoreCase(type)) { code = "*B:" + kv[2]; - //System.out.println("Created a META set of " + kv[1] + ", code '" + code + "'"); + } + else if ("choose1".equalsIgnoreCase(type)) { + code = "*!:" + kv[2]; + } + else if ("random1".equalsIgnoreCase(type)) { + code = "*?:" + kv[2]; + } + else if ("combo".equalsIgnoreCase(type)) { + code = "*+:" + kv[2]; + } + else if ("booster".equalsIgnoreCase(type)) { + code = "*" + kv[2]; + } + else if ("pack".equalsIgnoreCase(type)) { + code = "*" + kv[2] + "(S)"; } else { code = null; @@ -108,7 +166,12 @@ public class MetaSet { */ public UnOpenedProduct getBooster() { - //System.out.println("MetaSet.booster called..."); + ItemPool cardPool = null; + + if ("meta".equalsIgnoreCase(type) || "choose1".equalsIgnoreCase(type) + || "random1".equalsIgnoreCase(type) || "combo".equalsIgnoreCase(type)) { + cardPool = new ItemPool(CardPrinted.class); + } if ("cube".equalsIgnoreCase(type)) { @@ -144,16 +207,14 @@ public class MetaSet { return new UnOpenedProduct(fnPick, bpCustom); } else if ("full".equalsIgnoreCase(type)) { - // System.out.println("Initializing boosters for FULL cardpool"); final BoosterGenerator bpFull = new BoosterGenerator(CardDb.instance().getAllUniqueCards()); return new UnOpenedProduct(BoosterGenerator.IDENTITY_PICK, bpFull); } else if ("meta".equalsIgnoreCase(type)) { - // System.out.println("Initializing boosters for " + data); // NOTE: The following code is far from ideal in a number of ways. If someone can // think of a way to improve it, please do so. --BBU - ItemPool cardPool = new ItemPool(CardPrinted.class); + // ItemPool cardPool = new ItemPool(CardPrinted.class); for (CardPrinted aCard : CardDb.instance().getAllCards()) { if (data.indexOf(aCard.getEdition()) > -1) { cardPool.add(aCard); @@ -163,9 +224,94 @@ public class MetaSet { final BoosterGenerator bpSets = new BoosterGenerator(cardPool); return new UnOpenedProduct(BoosterGenerator.IDENTITY_PICK, bpSets); + } else if ("booster".equalsIgnoreCase(type)) { + return new UnOpenedProduct(Singletons.getModel().getBoosters().get(data)); + } else if ("pack".equalsIgnoreCase(type)) { + return new UnOpenedProduct(Singletons.getModel().getTournamentPacks().get(data)); + } else if ("choose1".equalsIgnoreCase(type)) { + return new UnOpenedMeta(data, true); + } else if ("random1".equalsIgnoreCase(type)) { + return new UnOpenedMeta(data, false); + } else if ("combo".equalsIgnoreCase(type)) { + final BoosterGenerator bpSets = new BoosterGenerator(buildPool(data)); + return new UnOpenedProduct(BoosterGenerator.IDENTITY_PICK, bpSets); } else { throw new RuntimeException("Cannot initialize boosters for: " + type); } } + + /** + * Build a cardpool for the 'combo' special MetaSet type. + * + * @param creationString + * the data that contains a collection of semicolon-separated metaset definitions + * @return ItemPool + * the collection of cards + * + */ + private ItemPool buildPool(final String creationString) { + + ItemPool cardPool = new ItemPool(CardPrinted.class); + + List metaSets = new ArrayList(); + final String[] metas = creationString.split(";"); + + for (int i = 0; i < metas.length; i++) { + + final String [] typeTest = metas[i].split("/"); + if (typeTest[0].equalsIgnoreCase("choose1") || typeTest[0].equalsIgnoreCase("random1") + || typeTest[0].equalsIgnoreCase("combo")) { + System.out.println("WARNING - MetaSet type '" + typeTest[0] + "' ignored in pool creation."); + } + else if (typeTest[0].equalsIgnoreCase("full")) { + for (CardPrinted aCard : CardDb.instance().getAllUniqueCards()) { + cardPool.add(aCard); + } + return cardPool; + } + final MetaSet addMeta = new MetaSet(metas[i]); + metaSets.add(addMeta); + } + + if (metaSets.size() < 1) { + return null; + } + + for (MetaSet mSet : metaSets) { + if (mSet.type.equalsIgnoreCase("meta") || mSet.type.equalsIgnoreCase("booster") + || mSet.type.equalsIgnoreCase("pack")) { + final String mData = new String(mSet.data); + for (CardPrinted aCard : CardDb.instance().getAllCards()) { + if (mData.indexOf(aCard.getEdition()) > -1) { + if (!cardPool.contains(aCard)) { + cardPool.add(aCard); + // System.out.println(mSet.type + " " + mData + ": Added card: " + aCard.getName()); + } + } + } + } else if (mSet.type.equalsIgnoreCase("cube")) { + final File dFolder = new File("res/sealed/"); + + if (!dFolder.exists()) { + throw new RuntimeException("GenerateSealed : folder not found -- folder is " + + dFolder.getAbsolutePath()); + } + + if (!dFolder.isDirectory()) { + throw new RuntimeException("GenerateSealed : not a folder -- " + dFolder.getAbsolutePath()); + } + + List dfData = FileUtil.readFile("res/sealed/" + mSet.data + ".sealed"); + final CustomLimited myCube = CustomLimited.parse(dfData, Singletons.getModel().getDecks().getCubes()); + for (CardPrinted aCard : myCube.getCardPool().toFlatList()) { + if (!cardPool.contains(aCard)) { + cardPool.add(aCard); + // System.out.println(mSet.type + " " + mSet.data + ": Added card: " + aCard.getName()); + } + } + } + } + return cardPool; + } } diff --git a/src/main/java/forge/card/UnOpenedMeta.java b/src/main/java/forge/card/UnOpenedMeta.java new file mode 100644 index 00000000000..c189e44ded0 --- /dev/null +++ b/src/main/java/forge/card/UnOpenedMeta.java @@ -0,0 +1,189 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package forge.card; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import forge.Singletons; +import forge.gui.GuiUtils; +import forge.item.CardPrinted; +import forge.util.MyRandom; + +/** + * This type extends UnOpenedMeta to support booster choice or random boosters + * in sealed deck games. See MetaSet.java for further information. + * + */ +public class UnOpenedMeta extends UnOpenedProduct { + + private final ArrayList metaSets; + private final boolean choice; + private List partiality; + private final int partialityPreference = 80; + private final Random generator = MyRandom.getRandom(); + + /** + * Constructor for UnOpenedMeta. + * @param creationString + * String, is parsed for MetaSet info. + * @param choose + * sets the random/choice status. + */ + public UnOpenedMeta(final String creationString, final boolean choose) { + // NOTE: we need to call the super constructor with something non-null, + // but it doesn't matter with what exactly, since we are overriding it + // in open() anyway. I'm using Portal because that makes it easier to + // spot if the code is misbehaving in certain ways. --BBU + super(Singletons.getModel().getBoosters().get("POR")); + metaSets = new ArrayList(); + choice = choose; + final String[] metas = creationString.split(";"); + partiality = null; + for (int i = 0; i < metas.length; i++) { + final MetaSet addMeta = new MetaSet(metas[i]); + metaSets.add(addMeta); + } + } + + + + /** + * Adds to partiality info. + * @param addString + * String, add partiality for this String. + */ + private void addPartiality(final String addString) { + if (!hasPartiality(addString)) { + partiality.add(addString); + } + } + + /** + * Checks if the AI has a partiality for this set. + * @param partialString + * String, check partiality for this. + */ + private boolean hasPartiality(final String partialString) { + + if (partiality.isEmpty()) { + return false; + } + + for (final String cmp : partiality) { + if (partialString.equals(cmp)) { + return true; + } + } + return false; + } + + /** + * Open the booster pack, return contents. + * @return List, list of cards. + */ + @Override + public List open() { + return this.open(true, null); + } + + /** + * Like open, can define whether is human or not. + * @param isHuman + * boolean, is human player? + * @param partialities + * known partialities for the AI. + * @return List, list of cards. + */ + @Override + public List open(final boolean isHuman, List partialities) { + + if (metaSets.size() < 1) { + throw new RuntimeException("Empty UnOpenedMetaset, cannot generate booster."); + } + + if (choice) { + + if (isHuman) { + final List choices = new ArrayList(); + + for (MetaSet meta : metaSets) { + choices.add(meta.getCode()); + } + final Object o = GuiUtils.chooseOne("Choose booster:", choices); + + for (int i = 0; i < metaSets.size(); i++) { + if (o.toString().equals(metaSets.get(i).getCode())) { + final UnOpenedProduct newBooster = metaSets.get(i).getBooster(); + return newBooster.open(); + } + } + + throw new RuntimeException("Could not find MetaSet " + o.toString()); + } + else { + partiality = partialities; + int selected = -1; + + if (partiality == null || partiality.isEmpty()) { + // System.out.println("No partiality yet"); + selected = generator.nextInt(metaSets.size()); + // System.out.println("AI randomly chose " + metaSets.get(selected).getCode()); + if (partiality != null) { + addPartiality(metaSets.get(selected).getCode()); + } + } + else { + for (int i = 0; i < metaSets.size(); i++) { + if (hasPartiality(metaSets.get(i).getCode()) && MyRandom.percentTrue(partialityPreference)) { + // System.out.println("AI chose " + metaSets.get(i).getCode() + " because of partiality."); + selected = i; + break; + } + } + } + + if (selected == -1) { + selected = generator.nextInt(metaSets.size()); + if (partiality != null) { + addPartiality(metaSets.get(selected).getCode()); + } + // System.out.println("AI chose " + metaSets.get(selected).getCode() + " because partiality not established or failed percentage test."); + } + final UnOpenedProduct newBooster = metaSets.get(selected).getBooster(); + return newBooster.open(); + } + } + else { + int selected = generator.nextInt(metaSets.size()); + // System.out.println("RANDOMLY got " + metaSets.get(selected).getCode()); + + // It may actually seem slightly unfair to allow the computer change its partialities based + // on the random sets it gets since the player can't do the same...but, OTOH, this could also + // work against the computer, if this results in a bad partiality choice. --BBU + if (!isHuman && partiality != null && MyRandom.percentTrue(partialityPreference)) { + addPartiality(metaSets.get(selected).getCode()); + // System.out.println("AI decided to add " + metaSets.get(selected).getCode() + " to partialities."); + } + final UnOpenedProduct newBooster = metaSets.get(selected).getBooster(); + return newBooster.open(); + } + } +} diff --git a/src/main/java/forge/card/UnOpenedProduct.java b/src/main/java/forge/card/UnOpenedProduct.java index 252ce29eab1..c4605ed0074 100644 --- a/src/main/java/forge/card/UnOpenedProduct.java +++ b/src/main/java/forge/card/UnOpenedProduct.java @@ -35,4 +35,16 @@ public class UnOpenedProduct { return openBooster != null ? openBooster.apply(generator) : generator.getBoosterPack(booster); } + /** + * Like open, can define whether is human or not. + * @param isHuman + * boolean, is human player? + * @param partialities + * known partialities for the AI. + * @return List, list of cards. + */ + public List open(final boolean isHuman, List partialities) { + return open(); + } + } diff --git a/src/main/java/forge/game/limited/SealedDeckFormat.java b/src/main/java/forge/game/limited/SealedDeckFormat.java index afe8aa427d7..a299d8ca5df 100644 --- a/src/main/java/forge/game/limited/SealedDeckFormat.java +++ b/src/main/java/forge/game/limited/SealedDeckFormat.java @@ -46,6 +46,7 @@ import forge.util.closures.Lambda1; */ public class SealedDeckFormat { private final ArrayList product = new ArrayList(); + private List partiality; /** The Land set code. */ private String[] landSetCode = { "" }; @@ -60,6 +61,8 @@ public class SealedDeckFormat { */ public SealedDeckFormat(final String sealedType) { + partiality = new ArrayList(); + if (sealedType.equals("Full")) { final BoosterGenerator bpFull = new BoosterGenerator(CardDb.instance().getAllUniqueCards()); @@ -104,7 +107,6 @@ public class SealedDeckFormat { final int nPacks = block.getCntBoostersSealed(); if (block.getNumberMetaSets() > 0) { - System.out.println("Metasets found "); int j = cardSets.length; @@ -211,7 +213,13 @@ public class SealedDeckFormat { } } } else { - final UnOpenedProduct product1 = new UnOpenedProduct(Singletons.getModel().getBoosters().get(sets[0])); + UnOpenedProduct product1; + if (sets[0].charAt(0) == '*') { + product1 = block.getBooster(sets[0]); + } + else { + product1 = new UnOpenedProduct(Singletons.getModel().getBoosters().get(sets[0])); + } for (int i = 0; i < nPacks; i++) { this.product.add(product1); } @@ -411,15 +419,36 @@ public class SealedDeckFormat { * @return a {@link forge.CardList} object. */ public ItemPool getCardpool() { + return getCardpool(true); + } + + + /** + *

+ * getCardpool. + *

+ * + * @param isHuman + * boolean, get pool for human (possible choices) + * @return a {@link forge.CardList} object. + */ + public ItemPool getCardpool(final boolean isHuman) { + + if (!isHuman) { + if (!partiality.isEmpty()) { + partiality.clear(); + } + } final ItemPool pool = new ItemPool(CardPrinted.class); for (int i = 0; i < this.product.size(); i++) { - pool.addAllFlat(this.product.get(i).open()); + pool.addAllFlat(this.product.get(i).open(isHuman, partiality)); } return pool; } + /** * Gets the land set code. * diff --git a/src/main/java/forge/gui/home/sanctioned/CSubmenuSealed.java b/src/main/java/forge/gui/home/sanctioned/CSubmenuSealed.java index 0051e3f87cc..5df44827093 100644 --- a/src/main/java/forge/gui/home/sanctioned/CSubmenuSealed.java +++ b/src/main/java/forge/gui/home/sanctioned/CSubmenuSealed.java @@ -167,7 +167,7 @@ public enum CSubmenuSealed implements ICDoc { + ">> does not equal any of the sealedTypes."); } - if (sd.getCardpool().isEmpty()) { + if (sd.getCardpool(false).isEmpty()) { return; } @@ -203,8 +203,8 @@ public enum CSubmenuSealed implements ICDoc { sealedDecks.delete(sDeckName); } - final ItemPool sDeck = sd.getCardpool(); - ItemPool aiDecks = sd.getCardpool(); + final ItemPool sDeck = sd.getCardpool(true); + ItemPool aiDecks = sd.getCardpool(false); final Deck deck = new Deck(sDeckName); deck.getSideboard().addAll(sDeck); @@ -218,7 +218,7 @@ public enum CSubmenuSealed implements ICDoc { for (int i = 0; i < rounds; i++) { if (i > 0) { // Re-randomize for AI decks beyond the first... - aiDecks = sd.getCardpool(); + aiDecks = sd.getCardpool(false); } sealed.addAiDeck(new SealedDeck(aiDecks.toFlatList()).buildDeck()); }