mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
Added new color-specific booster packs to quests.
This commit is contained in:
@@ -1,17 +1,16 @@
|
||||
package forge;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import forge.card.CardDb;
|
||||
import forge.item.*;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.ImageUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class ImageKeys {
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class ImageKeys {
|
||||
public static final String CARD_PREFIX = "c:";
|
||||
public static final String TOKEN_PREFIX = "t:";
|
||||
public static final String ICON_PREFIX = "i:";
|
||||
@@ -31,7 +30,7 @@ public class ImageKeys {
|
||||
CACHE_FATPACK_PICS_DIR, CACHE_BOOSTERBOX_PICS_DIR, CACHE_PRECON_PICS_DIR, CACHE_TOURNAMENTPACK_PICS_DIR;
|
||||
private static Map<String, String> CACHE_CARD_PICS_SUBDIR;
|
||||
|
||||
private static Map<String, Boolean> editionImageLookup = new HashMap<String, Boolean>();
|
||||
private static Map<String, Boolean> editionImageLookup = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation.
|
||||
@@ -60,7 +59,7 @@ public class ImageKeys {
|
||||
return ImageKeys.CARD_PREFIX + pc.getName() + CardDb.NameSetSeparator + pc.getEdition() + CardDb.NameSetSeparator + pc.getArtIndex() + (altState ? BACKFACE_POSTFIX : "");
|
||||
}
|
||||
|
||||
// Inventory items don't have to know how a certain client should draw them.
|
||||
// Inventory items don't have to know how a certain client should draw them.
|
||||
// That's why this method is not encapsulated and overloaded in the InventoryItem descendants
|
||||
public static String getImageKey(InventoryItem ii, boolean altState) {
|
||||
if (ii instanceof PaperCard) {
|
||||
@@ -71,6 +70,9 @@ public class ImageKeys {
|
||||
}
|
||||
if (ii instanceof BoosterPack) {
|
||||
BoosterPack bp = (BoosterPack)ii;
|
||||
if (SealedProduct.specialSets.contains(bp.getEdition()) || bp.getEdition().equals("?")) {
|
||||
return "b:" + bp.getName().substring(0, bp.getName().indexOf(bp.getItemType()) - 1);
|
||||
}
|
||||
int cntPics = StaticData.instance().getEditions().get(bp.getEdition()).getCntBoosterPictures();
|
||||
String suffix = (1 >= cntPics) ? "" : ("_" + bp.getArtIndex());
|
||||
return ImageKeys.BOOSTER_PREFIX + bp.getEdition() + suffix;
|
||||
@@ -145,7 +147,7 @@ public class ImageKeys {
|
||||
}
|
||||
|
||||
// try without set name
|
||||
if (dir == CACHE_TOKEN_PICS_DIR) {
|
||||
if (dir.equals(CACHE_TOKEN_PICS_DIR)) {
|
||||
int index = filename.lastIndexOf('_');
|
||||
if (index != -1) {
|
||||
String setlessFilename = filename.substring(0, index);
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.google.common.collect.Lists;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardEdition.FoilType;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.IPaperCard.Predicates.Presets;
|
||||
import forge.item.PaperCard;
|
||||
import forge.item.SealedProduct;
|
||||
import forge.util.Aggregates;
|
||||
@@ -76,7 +77,7 @@ public class BoosterGenerator {
|
||||
|
||||
String[] sType = TextUtil.splitWithParenthesis(slotType, ' ');
|
||||
String setCode = sType.length == 1 && template.getEdition() != null ? template.getEdition() : null;
|
||||
String sheetKey = StaticData.instance().getEditions().contains(setCode) ? slotType.trim() + " " + setCode: slotType.trim();
|
||||
String sheetKey = StaticData.instance().getEditions().contains(setCode) ? slotType.trim() + " " + setCode : slotType.trim();
|
||||
|
||||
boolean foilInThisSlot = hasFoil && slotType.startsWith(foilSlot);
|
||||
if (foilInThisSlot)
|
||||
@@ -220,6 +221,32 @@ public class BoosterGenerator {
|
||||
operator = StringUtils.strip(operator.substring(4), "() ");
|
||||
String[] cardNames = TextUtil.splitWithParenthesis(operator, ',', '"', '"');
|
||||
toAdd = IPaperCard.Predicates.names(Lists.newArrayList(cardNames));
|
||||
} else if (operator.startsWith("color(")) {
|
||||
operator = StringUtils.strip(operator.substring("color(".length() + 1), "()\" ");
|
||||
switch (operator.toLowerCase()) {
|
||||
case "black":
|
||||
toAdd = Presets.IS_BLACK;
|
||||
break;
|
||||
case "blue":
|
||||
toAdd = Presets.IS_BLUE;
|
||||
break;
|
||||
case "green":
|
||||
toAdd = Presets.IS_GREEN;
|
||||
break;
|
||||
case "red":
|
||||
toAdd = Presets.IS_RED;
|
||||
break;
|
||||
case "white":
|
||||
toAdd = Presets.IS_WHITE;
|
||||
break;
|
||||
case "colorless":
|
||||
toAdd = Presets.IS_COLORLESS;
|
||||
break;
|
||||
}
|
||||
} else if (operator.startsWith("fromSets(")) {
|
||||
operator = StringUtils.strip(operator.substring("fromSets(".length() + 1), "()\" ");
|
||||
String[] sets = operator.split(",");
|
||||
toAdd = IPaperCard.Predicates.printedInSets(sets);
|
||||
} else if (operator.startsWith("fromSheet(") && invert) {
|
||||
String sheetName = StringUtils.strip(operator.substring(9), "()\" ");
|
||||
Iterable<PaperCard> src = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -50,25 +50,25 @@ import java.util.Map.Entry;
|
||||
* <p>
|
||||
* CardSet class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: CardSet.java 9708 2011-08-09 19:34:12Z jendave $
|
||||
*/
|
||||
public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
public enum Type {
|
||||
UNKNOWN,
|
||||
|
||||
|
||||
CORE,
|
||||
EXPANSION,
|
||||
|
||||
|
||||
REPRINT,
|
||||
ONLINE,
|
||||
STARTER,
|
||||
|
||||
|
||||
DUEL_DECKS,
|
||||
PREMIUM_DECK_SERIES,
|
||||
FROM_THE_VAULT,
|
||||
|
||||
|
||||
OTHER,
|
||||
THIRDPARTY // custom sets
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
OLD_STYLE, // sets between Urza's Legacy and 8th Edition
|
||||
MODERN // 8th Edition and newer
|
||||
}
|
||||
|
||||
|
||||
public static class CardInSet {
|
||||
public final CardRarity rarity;
|
||||
public final String name;
|
||||
@@ -89,10 +89,10 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** The Constant unknown. */
|
||||
private final static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
|
||||
public static final CardEdition UNKNOWN = new CardEdition("1990-01-01", "??", "???", Type.UNKNOWN, "Undefined", FoilType.NOT_SUPPORTED, new CardInSet[]{});
|
||||
|
||||
private Date date;
|
||||
@@ -106,19 +106,19 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
private double foilChanceInBooster = 0;
|
||||
private boolean foilAlwaysInCommonSlot = false;
|
||||
private final CardInSet[] cards;
|
||||
|
||||
|
||||
|
||||
|
||||
private int boosterArts = 1;
|
||||
private SealedProduct.Template boosterTpl = null;
|
||||
|
||||
private CardEdition(CardInSet[] cards) {
|
||||
this.cards = cards;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a new card set.
|
||||
*
|
||||
* @param index indicates order of set release date
|
||||
*
|
||||
* @param date indicates order of set release date
|
||||
* @param code2 the 2 (usually) letter code used for image filenames/URLs distributed by the HQ pics team that
|
||||
* use Magic Workstation-type edition codes. Older sets only had 2-letter codes, and some of the 3-letter
|
||||
* codes they use now aren't the same as the official list of 3-letter codes. When Forge downloads set-pics,
|
||||
@@ -126,7 +126,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
* @param code the MTG 3-letter set code
|
||||
* @param type the set type
|
||||
* @param name the name of the set
|
||||
* @param an optional secondary code alias for the set
|
||||
* @param cards the cards in the set
|
||||
*/
|
||||
private CardEdition(String date, String code2, String code, Type type, String name, FoilType foil, CardInSet[] cards) {
|
||||
this(cards);
|
||||
@@ -137,9 +137,9 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
this.date = parseDate(date);
|
||||
this.foilType = foil;
|
||||
}
|
||||
|
||||
|
||||
private static Date parseDate(String date) {
|
||||
if( date.length() <= 7 )
|
||||
if( date.length() <= 7 )
|
||||
date = date + "-01";
|
||||
try {
|
||||
return formatter.parse(date);
|
||||
@@ -220,19 +220,17 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
public boolean hasBoosterTemplate() {
|
||||
return boosterTpl != null;
|
||||
}
|
||||
|
||||
|
||||
public static class Reader extends StorageReaderFolder<CardEdition> {
|
||||
public Reader(File path) {
|
||||
super(path, CardEdition.FN_GET_CODE);
|
||||
}
|
||||
|
||||
public final static CardInSet[] arrCards = new CardInSet[] {};
|
||||
|
||||
@Override
|
||||
protected CardEdition read(File file) {
|
||||
final Map<String, List<String>> contents = FileSection.parseSections(FileUtil.readFile(file));
|
||||
|
||||
List<CardEdition.CardInSet> processedCards = new ArrayList<CardEdition.CardInSet>();
|
||||
List<CardEdition.CardInSet> processedCards = new ArrayList<>();
|
||||
for(String line : contents.get("cards")) {
|
||||
if (StringUtils.isBlank(line))
|
||||
continue;
|
||||
@@ -240,26 +238,26 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
// You may omit rarity for early development
|
||||
CardRarity r = CardRarity.smartValueOf(line.substring(0, 1));
|
||||
boolean hadRarity = r != CardRarity.Unknown && line.charAt(1) == ' ';
|
||||
String cardName = hadRarity ? line.substring(2) : line;
|
||||
String cardName = hadRarity ? line.substring(2) : line;
|
||||
CardInSet cis = new CardInSet(cardName, r);
|
||||
processedCards.add(cis);
|
||||
}
|
||||
|
||||
CardEdition res = new CardEdition(processedCards.toArray(arrCards));
|
||||
|
||||
|
||||
CardEdition res = new CardEdition(processedCards.toArray(new CardInSet[processedCards.size()]));
|
||||
|
||||
FileSection section = FileSection.parse(contents.get("metadata"), "=");
|
||||
res.name = section.get("name");
|
||||
res.date = parseDate(section.get("date"));
|
||||
res.code = section.get("code");
|
||||
res.code2 = section.get("code2");
|
||||
if( res.code2 == null )
|
||||
if (res.code2 == null) {
|
||||
res.code2 = res.code;
|
||||
|
||||
}
|
||||
|
||||
res.boosterArts = section.getInt("BoosterCovers", 1);
|
||||
String boosterDesc = section.get("Booster");
|
||||
res.boosterTpl = boosterDesc == null ? null : new SealedProduct.Template(res.code, SealedProduct.Template.Reader.parseSlots(boosterDesc));
|
||||
|
||||
|
||||
res.alias = section.get("alias");
|
||||
res.whiteBorder = "white".equalsIgnoreCase(section.get("border"));
|
||||
String type = section.get("type");
|
||||
@@ -267,7 +265,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
if (null != type && !type.isEmpty()) {
|
||||
try {
|
||||
enumType = Type.valueOf(type.toUpperCase(Locale.ENGLISH));
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
// ignore; type will get UNKNOWN
|
||||
System.err.println(String.format("Ignoring unknown type in set definitions: name: %s; type: %s", res.name, type));
|
||||
}
|
||||
@@ -292,7 +290,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
}
|
||||
res.foilChanceInBooster = section.getDouble("FoilChanceInBooster", 21.43F) / 100.0F;
|
||||
res.foilAlwaysInCommonSlot = section.getBoolean("FoilAlwaysInCommonSlot", true);
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -312,7 +310,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
|
||||
public static class Collection extends StorageBase<CardEdition> {
|
||||
|
||||
private final Map<String, CardEdition> aliasToEdition = new TreeMap<String, CardEdition>(String.CASE_INSENSITIVE_ORDER);
|
||||
private final Map<String, CardEdition> aliasToEdition = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
public Collection(IItemReader<CardEdition> reader) {
|
||||
super("Card editions", reader);
|
||||
@@ -328,7 +326,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
|
||||
/**
|
||||
* Gets a sets by code. It will search first by three letter codes, then by aliases and two-letter codes.
|
||||
*
|
||||
*
|
||||
* @param code
|
||||
* the code
|
||||
* @return the sets the by code
|
||||
@@ -342,9 +340,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
CardEdition baseResult = super.get(code);
|
||||
return baseResult == null ? aliasToEdition.get(code) : baseResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Iterable<CardEdition> getOrderedEditions() {
|
||||
List<CardEdition> res = Lists.newArrayList(this);
|
||||
Collections.sort(res);
|
||||
@@ -354,7 +350,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
|
||||
/**
|
||||
* Gets the sets by code or throw.
|
||||
*
|
||||
*
|
||||
* @param code
|
||||
* the code
|
||||
* @return the sets the by code or throw
|
||||
@@ -370,7 +366,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
// used by image generating code
|
||||
/**
|
||||
* Gets the code2 by code.
|
||||
*
|
||||
*
|
||||
* @param code
|
||||
* the code
|
||||
* @return the code2 by code
|
||||
@@ -384,38 +380,38 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
@Override
|
||||
public CardEdition apply(String code) {
|
||||
return Collection.this.get(code);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @return
|
||||
* @return ItemReader<SealedProduct.Template>
|
||||
*/
|
||||
public IItemReader<SealedProduct.Template> getBoosterGenerator() {
|
||||
// TODO Auto-generated method stub
|
||||
return new StorageReaderBase<SealedProduct.Template>(null) {
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, SealedProduct.Template> readAll() {
|
||||
Map<String, SealedProduct.Template> map = new TreeMap<String, SealedProduct.Template>(String.CASE_INSENSITIVE_ORDER);
|
||||
Map<String, SealedProduct.Template> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
for(CardEdition ce : Collection.this) {
|
||||
map.put(ce.getCode(), ce.getBoosterTemplate());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getItemKey(SealedProduct.Template item) {
|
||||
return item.getEdition();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public CardEdition getEarliestEditionWithAllCards(CardPool cards) {
|
||||
Set<String> minEditions = new HashSet<String>();
|
||||
|
||||
Set<String> minEditions = new HashSet<>();
|
||||
|
||||
SetPreference strictness = SetPreference.EarliestCoreExp;
|
||||
|
||||
|
||||
for(Entry<PaperCard, Integer> k : cards) {
|
||||
PaperCard cp = StaticData.instance().getCommonCards().getCardFromEdition(k.getKey().getName(), strictness);
|
||||
if( cp == null && strictness == SetPreference.EarliestCoreExp) {
|
||||
@@ -424,14 +420,14 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
}
|
||||
if ( cp == null )
|
||||
cp = k.getKey(); // it's unlikely, this code will ever run
|
||||
|
||||
|
||||
minEditions.add(cp.getEdition());
|
||||
}
|
||||
|
||||
for(CardEdition ed : getOrderedEditions()) {
|
||||
if(minEditions.contains(ed.getCode()))
|
||||
return ed;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -448,10 +444,10 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
}
|
||||
|
||||
|
||||
public final static CardEdition getRandomSetWithAllBasicLands(Iterable<CardEdition> allEditions) {
|
||||
public static CardEdition getRandomSetWithAllBasicLands(Iterable<CardEdition> allEditions) {
|
||||
return Aggregates.random(Iterables.filter(allEditions, hasBasicLands));
|
||||
}
|
||||
|
||||
|
||||
public static final Predicate<CardEdition> HAS_TOURNAMENT_PACK = new CanMakeStarter();
|
||||
private static class CanMakeStarter implements Predicate<CardEdition> {
|
||||
@Override
|
||||
@@ -485,7 +481,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import forge.item.PaperCard;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
|
||||
public interface IUnOpenedProduct extends Supplier<List<PaperCard>> {
|
||||
public List<PaperCard> get();
|
||||
List<PaperCard> get();
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -19,9 +19,12 @@
|
||||
package forge.item;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.StaticData;
|
||||
import forge.card.BoosterSlots;
|
||||
import forge.card.CardEdition;
|
||||
import forge.util.MyRandom;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
public class BoosterPack extends SealedProduct {
|
||||
private final int artIndex;
|
||||
@@ -35,11 +38,30 @@ public class BoosterPack extends SealedProduct {
|
||||
}
|
||||
};
|
||||
|
||||
public static final Function<String, BoosterPack> FN_FROM_COLOR = new Function<String, BoosterPack>() {
|
||||
@Override
|
||||
public BoosterPack apply(final String color) {
|
||||
return new BoosterPack(color, new Template("?", ImmutableList.of(
|
||||
Pair.of(BoosterSlots.COMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 11),
|
||||
Pair.of(BoosterSlots.UNCOMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 3),
|
||||
Pair.of(BoosterSlots.RARE_MYTHIC + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 1),
|
||||
Pair.of(BoosterSlots.LAND + ":color(\"" + color + "\")", 1))
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
public BoosterPack(final String name0, final Template boosterData) {
|
||||
super(name0, boosterData);
|
||||
int maxIdx = StaticData.instance().getEditions().get(boosterData.getEdition()).getCntBoosterPictures();
|
||||
artIndex = MyRandom.getRandom().nextInt(maxIdx) + 1;
|
||||
hash = super.hashCode() ^ artIndex;
|
||||
|
||||
if (specialSets.contains(boosterData.getEdition()) || boosterData.getEdition().equals("?")) {
|
||||
artIndex = 1;
|
||||
} else {
|
||||
int maxIdx = StaticData.instance().getEditions().get(boosterData.getEdition()).getCntBoosterPictures();
|
||||
artIndex = MyRandom.getRandom().nextInt(maxIdx) + 1;
|
||||
}
|
||||
|
||||
hash = super.hashCode() ^ artIndex;
|
||||
|
||||
}
|
||||
|
||||
public final int getArtIndex() {
|
||||
@@ -51,24 +73,32 @@ public class BoosterPack extends SealedProduct {
|
||||
return "Booster Pack";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
if (specialSets.contains(getEdition()) || getEdition().equals("?")) {
|
||||
String color = getName().substring(0, getName().indexOf(getItemType()) - 1).toLowerCase();
|
||||
return "11 " + color + " commons, 3 " + color + " uncommons, 1 " + color + " rare, and 1 " + color + " land.";
|
||||
}
|
||||
return super.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object clone() {
|
||||
return new BoosterPack(name, contents);
|
||||
}
|
||||
|
||||
|
||||
public Template getBoosterData() {
|
||||
return contents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
BoosterPack other = (BoosterPack)obj;
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
|
||||
BoosterPack other = (BoosterPack) o;
|
||||
|
||||
return artIndex == other.artIndex;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.card.CardRarity;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardType.CoreType;
|
||||
import forge.card.MagicColor;
|
||||
import forge.util.PredicateString;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -18,88 +20,130 @@ public interface IPaperCard extends InventoryItem {
|
||||
/**
|
||||
* Number of filters based on CardPrinted values.
|
||||
*/
|
||||
public abstract static class Predicates {
|
||||
|
||||
abstract class Predicates {
|
||||
|
||||
public static Predicate<PaperCard> rarity(final boolean isEqual, final CardRarity value) {
|
||||
return new PredicateRarity(value, isEqual);
|
||||
}
|
||||
|
||||
public static Predicate<PaperCard> color(final boolean isEqual, final boolean noColor, final byte value) {
|
||||
return new PredicateColor(value, noColor, isEqual);
|
||||
}
|
||||
|
||||
public static Predicate<PaperCard> printedInSets(final String[] sets) {
|
||||
return printedInSets(Lists.newArrayList(sets), true);
|
||||
}
|
||||
|
||||
|
||||
public static Predicate<PaperCard> printedInSets(final List<String> value, final boolean shouldContain) {
|
||||
if ((value == null) || value.isEmpty()) {
|
||||
return com.google.common.base.Predicates.alwaysTrue();
|
||||
}
|
||||
return new PredicateSets(value, shouldContain);
|
||||
}
|
||||
|
||||
|
||||
public static Predicate<PaperCard> printedInSet(final String value) {
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return com.google.common.base.Predicates.alwaysTrue();
|
||||
}
|
||||
return new PredicateSets(Lists.newArrayList(value), true);
|
||||
}
|
||||
|
||||
|
||||
public static Predicate<PaperCard> name(final String what) {
|
||||
return new PredicateName(PredicateString.StringOp.EQUALS_IC, what);
|
||||
}
|
||||
|
||||
|
||||
public static Predicate<PaperCard> name(final PredicateString.StringOp op, final String what) {
|
||||
return new PredicateName(op, what);
|
||||
}
|
||||
|
||||
|
||||
public static Predicate<PaperCard> names(final List<String> what) {
|
||||
return new PredicateNames(what);
|
||||
}
|
||||
|
||||
private static class PredicateRarity implements Predicate<PaperCard> {
|
||||
|
||||
private static final class PredicateColor implements Predicate<PaperCard> {
|
||||
|
||||
private final byte operand;
|
||||
private final boolean noColor;
|
||||
private final boolean shouldBeEqual;
|
||||
|
||||
private PredicateColor(final byte color, final boolean noColor, final boolean wantEqual) {
|
||||
this.operand = color;
|
||||
this.noColor = noColor;
|
||||
this.shouldBeEqual = wantEqual;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(final PaperCard card) {
|
||||
boolean colorFound = false;
|
||||
if (noColor) {
|
||||
return card.getRules().getColor().isColorless() == shouldBeEqual;
|
||||
}
|
||||
for (final byte color : card.getRules().getColor()) {
|
||||
if (color == operand) {
|
||||
colorFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (card.getRules().getType().hasType(CoreType.Land)) {
|
||||
for (final byte color : card.getRules().getColorIdentity()) {
|
||||
if (color == operand) {
|
||||
colorFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return colorFound == shouldBeEqual;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class PredicateRarity implements Predicate<PaperCard> {
|
||||
private final CardRarity operand;
|
||||
private final boolean shouldBeEqual;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean apply(final PaperCard card) {
|
||||
return (card.getRarity() == this.operand) == this.shouldBeEqual;
|
||||
}
|
||||
|
||||
public PredicateRarity(final CardRarity type, final boolean wantEqual) {
|
||||
|
||||
private PredicateRarity(final CardRarity type, final boolean wantEqual) {
|
||||
this.operand = type;
|
||||
this.shouldBeEqual = wantEqual;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PredicateSets implements Predicate<PaperCard> {
|
||||
|
||||
private static final class PredicateSets implements Predicate<PaperCard> {
|
||||
private final Set<String> sets;
|
||||
private final boolean mustContain;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean apply(final PaperCard card) {
|
||||
return this.sets.contains(card.getEdition()) == this.mustContain;
|
||||
}
|
||||
|
||||
public PredicateSets(final List<String> wantSets, final boolean shouldContain) {
|
||||
this.sets = new HashSet<String>(wantSets);
|
||||
|
||||
private PredicateSets(final List<String> wantSets, final boolean shouldContain) {
|
||||
this.sets = new HashSet<>(wantSets);
|
||||
this.mustContain = shouldContain;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PredicateName extends PredicateString<PaperCard> {
|
||||
|
||||
private static final class PredicateName extends PredicateString<PaperCard> {
|
||||
private final String operand;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean apply(final PaperCard card) {
|
||||
return this.op(card.getName(), this.operand);
|
||||
}
|
||||
|
||||
public PredicateName(final PredicateString.StringOp operator, final String operand) {
|
||||
|
||||
private PredicateName(final PredicateString.StringOp operator, final String operand) {
|
||||
super(operator);
|
||||
this.operand = operand;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PredicateNames extends PredicateString<PaperCard> {
|
||||
|
||||
private static final class PredicateNames extends PredicateString<PaperCard> {
|
||||
private final List<String> operand;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean apply(final PaperCard card) {
|
||||
final String cardName = card.getName();
|
||||
@@ -110,13 +154,13 @@ public interface IPaperCard extends InventoryItem {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public PredicateNames(final List<String> operand) {
|
||||
|
||||
private PredicateNames(final List<String> operand) {
|
||||
super(StringOp.EQUALS);
|
||||
this.operand = operand;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pre-built predicates are stored here to allow their re-usage and
|
||||
* easier access from code.
|
||||
@@ -126,37 +170,45 @@ public interface IPaperCard extends InventoryItem {
|
||||
// card.
|
||||
/** The Constant isCommon. */
|
||||
public static final Predicate<PaperCard> IS_COMMON = Predicates.rarity(true, CardRarity.Common);
|
||||
|
||||
|
||||
/** The Constant isUncommon. */
|
||||
public static final Predicate<PaperCard> IS_UNCOMMON = Predicates.rarity(true, CardRarity.Uncommon);
|
||||
|
||||
|
||||
/** The Constant isRare. */
|
||||
public static final Predicate<PaperCard> IS_RARE = Predicates.rarity(true, CardRarity.Rare);
|
||||
|
||||
|
||||
/** The Constant isMythicRare. */
|
||||
public static final Predicate<PaperCard> IS_MYTHIC_RARE = Predicates.rarity(true, CardRarity.MythicRare);
|
||||
|
||||
|
||||
/** The Constant isRareOrMythic. */
|
||||
public static final Predicate<PaperCard> IS_RARE_OR_MYTHIC = com.google.common.base.Predicates.or(Presets.IS_RARE,
|
||||
Presets.IS_MYTHIC_RARE);
|
||||
|
||||
|
||||
/** The Constant isSpecial. */
|
||||
public static final Predicate<PaperCard> IS_SPECIAL = Predicates.rarity(true, CardRarity.Special);
|
||||
|
||||
|
||||
/** The Constant exceptLands. */
|
||||
public static final Predicate<PaperCard> IS_BASIC_LAND = Predicates.rarity(true, CardRarity.BasicLand);
|
||||
|
||||
public static final Predicate<PaperCard> IS_BLACK = Predicates.color(true, false, MagicColor.BLACK);
|
||||
public static final Predicate<PaperCard> IS_BLUE = Predicates.color(true, false, MagicColor.BLUE);
|
||||
public static final Predicate<PaperCard> IS_GREEN = Predicates.color(true, false, MagicColor.GREEN);
|
||||
public static final Predicate<PaperCard> IS_RED = Predicates.color(true, false, MagicColor.RED);
|
||||
public static final Predicate<PaperCard> IS_WHITE = Predicates.color(true, false, MagicColor.WHITE);
|
||||
public static final Predicate<PaperCard> IS_COLORLESS = Predicates.color(true, true, MagicColor.COLORLESS);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public abstract String getName();
|
||||
public abstract String getEdition();
|
||||
public abstract int getArtIndex();
|
||||
public abstract boolean isFoil();
|
||||
public abstract boolean isToken();
|
||||
public abstract CardRules getRules();
|
||||
public abstract CardRarity getRarity();
|
||||
String getName();
|
||||
String getEdition();
|
||||
int getArtIndex();
|
||||
boolean isFoil();
|
||||
boolean isToken();
|
||||
CardRules getRules();
|
||||
CardRarity getRarity();
|
||||
|
||||
public abstract String getItemType();
|
||||
String getItemType();
|
||||
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -38,11 +38,23 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class SealedProduct implements InventoryItemFromSet {
|
||||
|
||||
public static final List<String> specialSets = new ArrayList<>();
|
||||
|
||||
protected final Template contents;
|
||||
protected final String name;
|
||||
private final int hash;
|
||||
protected List<PaperCard> cards = null;
|
||||
|
||||
static {
|
||||
specialSets.add("Black");
|
||||
specialSets.add("Blue");
|
||||
specialSets.add("Green");
|
||||
specialSets.add("Red");
|
||||
specialSets.add("White");
|
||||
specialSets.add("Colorless");
|
||||
}
|
||||
|
||||
public SealedProduct(String name0, Template boosterData) {
|
||||
if (null == name0) { throw new IllegalArgumentException("name0 must not be null"); }
|
||||
if (null == boosterData) { throw new IllegalArgumentException("boosterData must not be null"); }
|
||||
@@ -59,37 +71,32 @@ public abstract class SealedProduct implements InventoryItemFromSet {
|
||||
public String getDescription() {
|
||||
return contents.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final String getEdition() {
|
||||
return contents.getEdition();
|
||||
}
|
||||
|
||||
|
||||
public List<PaperCard> getCards() {
|
||||
if (null == cards) {
|
||||
cards = generate();
|
||||
}
|
||||
|
||||
|
||||
return cards;
|
||||
}
|
||||
|
||||
|
||||
public int getTotalCards() {
|
||||
return contents.getNumberOfCardsExpected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SealedProduct other = (SealedProduct)obj;
|
||||
return name.equals(other.name) && contents.equals(other.contents);
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
SealedProduct other = (SealedProduct) o;
|
||||
|
||||
return contents.equals(other.contents) && name.equals(other.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,25 +123,23 @@ public abstract class SealedProduct implements InventoryItemFromSet {
|
||||
Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES));
|
||||
return Aggregates.random(Iterables.filter(StaticData.instance().getCommonCards().getAllCards(), cardsRule), count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class Template {
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public final static Template genericBooster = new Template(null, Lists.newArrayList(
|
||||
Pair.of(BoosterSlots.COMMON, 10), Pair.of(BoosterSlots.UNCOMMON, 3),
|
||||
Pair.of(BoosterSlots.COMMON, 10), Pair.of(BoosterSlots.UNCOMMON, 3),
|
||||
Pair.of(BoosterSlots.RARE_MYTHIC, 1), Pair.of(BoosterSlots.BASIC_LAND, 1)
|
||||
));
|
||||
|
||||
|
||||
|
||||
protected final List<Pair<String, Integer>> slots;
|
||||
protected final String name;
|
||||
|
||||
|
||||
|
||||
public final List<Pair<String, Integer>> getSlots() {
|
||||
return slots;
|
||||
}
|
||||
|
||||
|
||||
public final String getEdition() {
|
||||
return name;
|
||||
}
|
||||
@@ -142,75 +147,92 @@ public abstract class SealedProduct implements InventoryItemFromSet {
|
||||
{
|
||||
this(null, itrSlots);
|
||||
}
|
||||
|
||||
|
||||
public Template(String name0, Iterable<Pair<String, Integer>> itrSlots)
|
||||
{
|
||||
slots = Lists.newArrayList(itrSlots);
|
||||
name = name0;
|
||||
}
|
||||
|
||||
|
||||
public Template(String code, String boosterDesc) {
|
||||
this(code, Reader.parseSlots(boosterDesc));
|
||||
}
|
||||
|
||||
|
||||
public int getNumberOfCardsExpected() {
|
||||
int sum = 0;
|
||||
for(Pair<String, Integer> p : slots) {
|
||||
sum += p.getRight().intValue();
|
||||
sum += p.getRight();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
public static final Function<? super Template, String> FN_GET_NAME = new Function<Template, String>() {
|
||||
@Override
|
||||
public String apply(Template arg1) {
|
||||
return arg1.name;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
|
||||
|
||||
|
||||
|
||||
s.append("consisting of ");
|
||||
for(Pair<String, Integer> p : slots) {
|
||||
s.append(p.getRight()).append(" ").append(p.getLeft()).append(", ");
|
||||
}
|
||||
|
||||
|
||||
// trim the last comma and space
|
||||
s.replace(s.length() - 2, s.length(), "");
|
||||
|
||||
|
||||
// put an 'and' before the previous comma
|
||||
int lastCommaIdx = s.lastIndexOf(",");
|
||||
int lastCommaIdx = s.lastIndexOf(",");
|
||||
if (0 < lastCommaIdx) {
|
||||
s.replace(lastCommaIdx+1, lastCommaIdx+1, " and");
|
||||
}
|
||||
|
||||
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Template template = (Template) o;
|
||||
|
||||
return slots.equals(template.slots) && name.equals(template.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = slots.hashCode();
|
||||
result = 31 * result + name.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public final static class Reader extends StorageReaderFile<Template> {
|
||||
public Reader(File file) {
|
||||
super(file, Template.FN_GET_NAME);
|
||||
}
|
||||
|
||||
|
||||
public static List<Pair<String, Integer>> parseSlots(String data) {
|
||||
final String[] dataz = TextUtil.splitWithParenthesis(data, ',');
|
||||
List<Pair<String, Integer>> slots = new ArrayList<Pair<String,Integer>>();
|
||||
List<Pair<String, Integer>> slots = new ArrayList<>();
|
||||
for(String slotDesc : dataz) {
|
||||
String[] kv = TextUtil.splitWithParenthesis(slotDesc, ' ', 2);
|
||||
slots.add(ImmutablePair.of(kv[1], Integer.parseInt(kv[0])));
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Template read(String line, int i) {
|
||||
String[] headAndData = TextUtil.split(line, ':', 2);
|
||||
return new Template(headAndData[0], parseSlots(headAndData[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -340,6 +340,11 @@ public enum VSubmenuQuestPrefs implements IVSubmenu<CSubmenuQuestPrefs> {
|
||||
pnlBooster.add(new FLabel.Builder().text("Rare").fontAlign(SwingConstants.RIGHT).build(), labelConstraints);
|
||||
pnlBooster.add(new PrefInput(QPref.BOOSTER_RARES, QuestPreferencesErrType.BOOSTER), fieldConstraints);
|
||||
|
||||
FLabel specialBoosters = new FLabel.Builder().text("Special Boosters").fontAlign(SwingConstants.RIGHT).build();
|
||||
specialBoosters.setToolTipText("Allows special, color-specific boosters to appear in the shop and as match rewards.");
|
||||
pnlBooster.add(specialBoosters, labelConstraints);
|
||||
pnlBooster.add(new PrefInput(QPref.SPECIAL_BOOSTERS, QuestPreferencesErrType.BOOSTER), fieldConstraints);
|
||||
|
||||
}
|
||||
|
||||
private void populateShop() {
|
||||
|
||||
@@ -73,6 +73,7 @@ public class QuestPrefsScreen extends FScreen {
|
||||
scroller.add(new PrefsOption("Common", QPref.BOOSTER_COMMONS, PrefsGroup.BOOSTER));
|
||||
scroller.add(new PrefsOption("Uncommon", QPref.BOOSTER_UNCOMMONS, PrefsGroup.BOOSTER));
|
||||
scroller.add(new PrefsOption("Rare", QPref.BOOSTER_RARES, PrefsGroup.BOOSTER));
|
||||
scroller.add(new PrefsOption("Special Boosters", QPref.SPECIAL_BOOSTERS, PrefsGroup.BOOSTER));
|
||||
|
||||
//Shop Preferences
|
||||
scroller.add(new PrefsHeader("Shop Preferences", FSkinImage.QUEST_COIN, PrefsGroup.SHOP));
|
||||
|
||||
@@ -8,11 +8,18 @@ Forge Beta: 65-2015 ver 1.5.41
|
||||
Release Notes
|
||||
-------------
|
||||
|
||||
- Special Quest Booster Packs -
|
||||
New booster packs have been added to quest mode that contain cards of a single
|
||||
color. Blue Booster Packs will contain only blue cards, White Booster Packs
|
||||
will contain only white cards, and so on. There's a pack for each color and
|
||||
these new packs can be disabled by setting the "Special Boosters" quest
|
||||
preference to "0".
|
||||
|
||||
- Updated Forge Content Downloads -
|
||||
Card images for the latest sets are now available for download, including
|
||||
Card images for the latest sets are now available for download, including
|
||||
Tempest Remastered and Modern Masters 2015. All images have also been moved to a
|
||||
new server for increased reliability and performance and many previously
|
||||
missing images have been added. There are still a few images missing in Forge
|
||||
new server for increased reliability and performance and many previously
|
||||
missing images have been added. There are still a few images missing in Forge
|
||||
and rectifying that is an ongoing effort.
|
||||
|
||||
|
||||
@@ -40,12 +47,12 @@ The interface has received several small changes.
|
||||
|
||||
|
||||
- Display Color Identity -
|
||||
There's a new option "Display Color Identity" in both desktop and mobile Forge
|
||||
There's a new option "Display Color Identity" in both desktop and mobile Forge
|
||||
that allows you to display the current color identity of cards. It's disabled
|
||||
by default (set to "Never") but can be toggled to "Always" (which displays color
|
||||
identity for all cards, even mono-color), to "Changed" (which displays color
|
||||
identity of all cards whose color identity has been altered compared to its
|
||||
"pristine" state), to "Multicolor" (which only displays color identity of cards
|
||||
identity of all cards whose color identity has been altered compared to its
|
||||
"pristine" state), to "Multicolor" (which only displays color identity of cards
|
||||
that are currently of two or more colors) or to "Multi+Changed" (which displays
|
||||
color identity of all multicolor cards and also of all cards the identity of
|
||||
which has changed). This might help with effects such as Painter's Servant which
|
||||
@@ -58,12 +65,12 @@ term that is not equivalent to the sum of card colors).
|
||||
|
||||
|
||||
- New Quest Preferences -
|
||||
Three new quest preferences (Playset Size: Basic Lands, Playset Size: Any
|
||||
Three new quest preferences (Playset Size: Basic Lands, Playset Size: Any
|
||||
Number, and Playset Size) have been added to control the number of cards to keep
|
||||
before selling extras in quest mode. One is for basic lands, one is for
|
||||
"unlimited" cards (Relentless Rats and Shadowborn Apostle), and the last one is
|
||||
for all other cards. This is mostly useful when playing for antes and you want
|
||||
to keep an extra couple of cards on hand, just in case, or if you want to keep
|
||||
before selling extras in quest mode. One is for basic lands, one is for
|
||||
"unlimited" cards (Relentless Rats and Shadowborn Apostle), and the last one is
|
||||
for all other cards. This is mostly useful when playing for antes and you want
|
||||
to keep an extra couple of cards on hand, just in case, or if you want to keep
|
||||
fewer copies of basic lands around.
|
||||
|
||||
|
||||
@@ -72,7 +79,7 @@ By default the game now displays tokens on the same row as non-token cards (and
|
||||
to the right of them), which makes the game use the battlefield space more
|
||||
effectively. For example, creature tokens are displayed to the right of regular
|
||||
non-token creatures in the same row. If you prefer the classic Forge behavior
|
||||
(when tokens are displayed on their own row), the option that controls this
|
||||
(when tokens are displayed on their own row), the option that controls this
|
||||
behavior can be found in Forge preferences under "Graphic Options". It is called
|
||||
"Display Tokens in a Separate Row".
|
||||
|
||||
@@ -95,28 +102,13 @@ longer considered unless they can be produced by a land in the deck
|
||||
When filtering colors in the Deck Editor, lands that produce all selected colors will now be shown
|
||||
This means if you filter to show only green and white cards, lands that produce green and white will now be shown
|
||||
This should hopefully make finding multicolor lands for your decks easier.
|
||||
An attempt was made to make this option behave in a more consistent and helpful way - now lands won't suddenly
|
||||
disappear when one color is filtered out, basic lands of appropriate colors will be shown, lands that feature at
|
||||
least one of the colors relevant for the deck will be shown. Also, the mode to filter lands in this way is now
|
||||
An attempt was made to make this option behave in a more consistent and helpful way - now lands won't suddenly
|
||||
disappear when one color is filtered out, basic lands of appropriate colors will be shown, lands that feature at
|
||||
least one of the colors relevant for the deck will be shown. Also, the mode to filter lands in this way is now
|
||||
optional and can be toggled on or off in the Preferences ("Filter Lands by Color in Activated Abilities" under
|
||||
"Deck Editor Options").
|
||||
|
||||
|
||||
- Quest Starting Pool Configuration -
|
||||
When starting a new quest, you now have many more options to configure your starting pool. Instead of just picking a
|
||||
single color, you can pick any combination of them, including colorless. There's also the option to include artifacts in
|
||||
the pool alongside your colored cards or not.
|
||||
The "color bias" option in the quest settings is now the percentage of your generated card pool that will contain the
|
||||
settings you've selected. For example, if you picked Green cards and a color bias of 75, your card pool would be 75%
|
||||
green, 25% all other colors.
|
||||
|
||||
Finally, there's three settings to choose your color distribution. "Balanced" maintains an equal balance between your
|
||||
selected colors. "Random" completely randomizes the pool and makes only miniscule attempts to control the cards you get.
|
||||
"Surprise Me" is much like "Balanced", except it will pick all your settings for you and not tell you what they were!
|
||||
The New/Load quest menu has been given a small facelift to accommodate these changes and make it easier to read.
|
||||
You will need to update your color bias quest setting as the values are now much too low.
|
||||
|
||||
|
||||
---------
|
||||
New Cards
|
||||
---------
|
||||
@@ -144,7 +136,7 @@ Known Issues
|
||||
------------
|
||||
|
||||
- Dragon Presence cards from DTK -
|
||||
Work has started on the Dragon Presence cards. These cards should typically work on their own merits, but a few corner cases dealing with copying/cloning may not work as expected. As these cases are fairly limited, it was decided to add these cards for people to be able to use.
|
||||
Work has started on the Dragon Presence cards. These cards should typically work on their own merits, but a few corner cases dealing with copying/cloning may not work as expected. As these cases are fairly limited, it was decided to add these cards for people to be able to use.
|
||||
|
||||
- Dragons of Tarkir -
|
||||
Forge now includes many of the new Dragons of Tarkir cards. It may take a few days/weeks before these new card pictures become available for downloading via the "Download LQ Card Pictures" button. The LQ set pictures tend to take a few more weeks/months to process before they become available for downloading via the "Download LQ Set Pictures" button. Please be patient. The Forge devs are not involved in maintaining the servers that house these pictures.
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
http://downloads.cardforge.link/images/products/boosters/Black.jpg
|
||||
http://downloads.cardforge.link/images/products/boosters/Blue.jpg
|
||||
http://downloads.cardforge.link/images/products/boosters/Colorless.jpg
|
||||
http://downloads.cardforge.link/images/products/boosters/Green.jpg
|
||||
http://downloads.cardforge.link/images/products/boosters/Red.jpg
|
||||
http://downloads.cardforge.link/images/products/boosters/White.jpg
|
||||
http://downloads.cardforge.link/images/products/boosters/10E_1.jpg
|
||||
http://downloads.cardforge.link/images/products/boosters/10E_2.jpg
|
||||
http://downloads.cardforge.link/images/products/boosters/10E_3.jpg
|
||||
|
||||
@@ -250,4 +250,10 @@ Journey into Nyx Fat Pack =2400
|
||||
Magic 2015 Fat Pack =2400
|
||||
Magic 2013 Fat Pack =2400
|
||||
Magic 2014 Fat Pack =2400
|
||||
Khans of Tarkir Fat Pack =3200
|
||||
Khans of Tarkir Fat Pack =3200
|
||||
Black Booster Pack =600
|
||||
Blue Booster Pack =600
|
||||
Colorless Booster Pack =600
|
||||
Green Booster Pack =600
|
||||
Red Booster Pack =600
|
||||
White Booster Pack =600
|
||||
|
||||
@@ -186,6 +186,13 @@ public class QuestSpellShop {
|
||||
SealedProduct booster = null;
|
||||
if (item instanceof BoosterPack) {
|
||||
booster = (BoosterPack) ((BoosterPack) item).clone();
|
||||
//Replace the booster here if it's a special colored booster
|
||||
//This is to ensure that the correct sets are included in the booster
|
||||
//When loading a quest save, the set information is not available to the booster loader
|
||||
if (SealedProduct.specialSets.contains(booster.getEdition()) || booster.getEdition().equals("?")) {
|
||||
String color = booster.getName().substring(0, booster.getName().indexOf(booster.getItemType()) - 1);
|
||||
booster = new BoosterPack(color, QuestUtilCards.getColoredBoosterTemplate(color));
|
||||
}
|
||||
}
|
||||
else if (item instanceof TournamentPack) {
|
||||
booster = (TournamentPack) ((TournamentPack) item).clone();
|
||||
|
||||
@@ -23,12 +23,12 @@ import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.card.*;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.game.GameFormat;
|
||||
import forge.item.*;
|
||||
import forge.item.SealedProduct.Template;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.quest.bazaar.QuestItemType;
|
||||
@@ -40,7 +40,6 @@ import forge.quest.data.QuestPreferences.QPref;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.ItemPool;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -538,6 +537,15 @@ public final class QuestUtilCards {
|
||||
}
|
||||
this.qa.getShopList().add(BoosterPack.FN_FROM_SET.apply(Aggregates.random(rightEditions)));
|
||||
}
|
||||
|
||||
if (qpref.getPrefInt(QPref.SPECIAL_BOOSTERS) == 1) {
|
||||
for (String color : SealedProduct.specialSets) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
this.qa.getShopList().add(new BoosterPack(color, getColoredBoosterTemplate(color)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -640,6 +648,37 @@ public final class QuestUtilCards {
|
||||
));
|
||||
}
|
||||
|
||||
public static SealedProduct.Template getColoredBoosterTemplate(final String color) {
|
||||
if (FModel.getQuest().getFormat() == null) {
|
||||
return new Template("?", ImmutableList.of(
|
||||
Pair.of(BoosterSlots.COMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 11),
|
||||
Pair.of(BoosterSlots.UNCOMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 3),
|
||||
Pair.of(BoosterSlots.RARE_MYTHIC + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 1),
|
||||
Pair.of(BoosterSlots.LAND + ":color(\"" + color + "\")", 1))
|
||||
);
|
||||
} else {
|
||||
String restrictions = "";
|
||||
List<String> allowedSetCodes = FModel.getQuest().getFormat().getAllowedSetCodes();
|
||||
if (allowedSetCodes.isEmpty()) {
|
||||
for (String restrictedCard : FModel.getQuest().getFormat().getRestrictedCards()) {
|
||||
restrictions += ":!name(\"" + restrictedCard + "\")";
|
||||
}
|
||||
} else {
|
||||
restrictions += ":fromSets(\"";
|
||||
for (String set : allowedSetCodes) {
|
||||
restrictions += set + ",";
|
||||
}
|
||||
restrictions += ")";
|
||||
}
|
||||
return new Template("?", ImmutableList.of(
|
||||
Pair.of(BoosterSlots.COMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND + restrictions, 11),
|
||||
Pair.of(BoosterSlots.UNCOMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND + restrictions, 3),
|
||||
Pair.of(BoosterSlots.RARE_MYTHIC + ":color(\"" + color + "\"):!" + BoosterSlots.LAND + restrictions, 1),
|
||||
Pair.of(BoosterSlots.LAND + ":color(\"" + color + "\")" + restrictions, 1))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate cards in shop.
|
||||
*/
|
||||
@@ -755,6 +794,17 @@ public final class QuestUtilCards {
|
||||
}
|
||||
|
||||
public int getCompletionPercent(String edition) {
|
||||
|
||||
for (String color : SealedProduct.specialSets) {
|
||||
if (color.equals(edition)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (edition.equals("?")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get all cards in the specified edition
|
||||
Predicate<PaperCard> filter = IPaperCard.Predicates.printedInSet(edition);
|
||||
Iterable<PaperCard> editionCards = Iterables.filter(FModel.getMagicDb().getCommonCards().getAllCards(), filter);
|
||||
|
||||
@@ -3,10 +3,7 @@ package forge.quest;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.LobbyPlayer;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.card.BoosterSlots;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.IUnOpenedProduct;
|
||||
import forge.card.UnOpenedProduct;
|
||||
import forge.card.*;
|
||||
import forge.game.GameEndReason;
|
||||
import forge.game.GameFormat;
|
||||
import forge.game.GameOutcome;
|
||||
@@ -495,12 +492,13 @@ public class QuestWinLoseController {
|
||||
String title;
|
||||
if (qData.getFormat() == null) {
|
||||
|
||||
final List<GameFormat> formats = new ArrayList<>();
|
||||
//final List<GameFormat> formats = new ArrayList<>();
|
||||
final List<String> formats = new ArrayList<>();
|
||||
final String preferredFormat = FModel.getQuestPreferences().getPref(QPref.BOOSTER_FORMAT);
|
||||
|
||||
GameFormat pref = null;
|
||||
for (final GameFormat f : FModel.getFormats().getOrderedList()) {
|
||||
formats.add(f);
|
||||
formats.add(f.toString());
|
||||
if (f.toString().equals(preferredFormat)) {
|
||||
pref = f;
|
||||
}
|
||||
@@ -508,14 +506,40 @@ public class QuestWinLoseController {
|
||||
|
||||
Collections.sort(formats);
|
||||
|
||||
final GameFormat selected = SGuiChoose.getChoices("Choose bonus booster format", 1, 1, formats, pref, null).get(0);
|
||||
FModel.getQuestPreferences().setPref(QPref.BOOSTER_FORMAT, selected.toString());
|
||||
formats.addAll(SealedProduct.specialSets);
|
||||
|
||||
String preferredSelection = null;
|
||||
if (pref != null) {
|
||||
preferredSelection = pref.toString();
|
||||
}
|
||||
final String selected = SGuiChoose.getChoices("Choose bonus booster format", 1, 1, formats, preferredSelection, null).get(0);
|
||||
|
||||
if (SealedProduct.specialSets.contains(selected)) {
|
||||
|
||||
BoosterPack boosterPack = BoosterPack.FN_FROM_COLOR.apply(selected);
|
||||
assert boosterPack != null;
|
||||
cardsWon = boosterPack.getCards();
|
||||
title = "Bonus " + selected + " Booster Pack!";
|
||||
|
||||
} else {
|
||||
|
||||
GameFormat selectedFormat = null;
|
||||
for (final GameFormat f : FModel.getFormats().getOrderedList()) {
|
||||
if (f.toString().equals(selected)) {
|
||||
selectedFormat = f;
|
||||
}
|
||||
}
|
||||
|
||||
assert selectedFormat != null;
|
||||
FModel.getQuestPreferences().setPref(QPref.BOOSTER_FORMAT, selected);
|
||||
|
||||
cardsWon = qData.getCards().generateQuestBooster(selectedFormat.getFilterPrinted());
|
||||
title = "Bonus booster pack from the \"" + selectedFormat.getName() + "\" format!";
|
||||
|
||||
}
|
||||
|
||||
cardsWon = qData.getCards().generateQuestBooster(selected.getFilterPrinted());
|
||||
qData.getCards().addAllCards(cardsWon);
|
||||
|
||||
title = "Bonus booster pack from the \"" + selected.getName() + "\" format!";
|
||||
|
||||
} else {
|
||||
|
||||
final List<String> sets = new ArrayList<>();
|
||||
@@ -549,29 +573,43 @@ public class QuestWinLoseController {
|
||||
maxChoices += qData.getAssets().getItemLevel(QuestItemType.MEMBERSHIP_TOKEN);
|
||||
}
|
||||
|
||||
final List<CardEdition> options = new ArrayList<>();
|
||||
final List<String> options = new ArrayList<>();
|
||||
|
||||
while (!sets.isEmpty() && maxChoices > 0) {
|
||||
final int ix = MyRandom.getRandom().nextInt(sets.size());
|
||||
final String set = sets.get(ix);
|
||||
sets.remove(ix);
|
||||
options.add(FModel.getMagicDb().getEditions().get(set));
|
||||
if (SealedProduct.specialSets.contains(set)) {
|
||||
options.add(set);
|
||||
} else {
|
||||
options.add(FModel.getMagicDb().getEditions().get(set).toString());
|
||||
}
|
||||
if (FModel.getQuestPreferences().getPrefInt(QPref.SPECIAL_BOOSTERS) == 1) {
|
||||
if (Math.random() > 0.85 - (double) (maxChoices) / 10.0) {
|
||||
options.add(SealedProduct.specialSets.get((int) (Math.random() * SealedProduct.specialSets.size())));
|
||||
}
|
||||
}
|
||||
maxChoices--;
|
||||
}
|
||||
|
||||
final CardEdition chooseEd = SGuiChoose.one("Choose bonus booster set", options);
|
||||
String chooseEd = SGuiChoose.one("Choose bonus booster set", options);
|
||||
|
||||
if (customBooster) {
|
||||
List<PaperCard> cards = FModel.getMagicDb().getCommonCards().getAllCards(Predicates.printedInSet(chooseEd.getCode()));
|
||||
if (SealedProduct.specialSets.contains(chooseEd)) {
|
||||
final IUnOpenedProduct product = new UnOpenedProduct(QuestUtilCards.getColoredBoosterTemplate(chooseEd));
|
||||
cardsWon = product.get();
|
||||
} else if (customBooster) {
|
||||
List<PaperCard> cards = FModel.getMagicDb().getCommonCards().getAllCards(Predicates.printedInSet(FModel.getMagicDb().getEditions().get(chooseEd).getCode()));
|
||||
final IUnOpenedProduct product = new UnOpenedProduct(getBoosterTemplate(), cards);
|
||||
cardsWon = product.get();
|
||||
} else {
|
||||
final IUnOpenedProduct product = new UnOpenedProduct(FModel.getMagicDb().getBoosters().get(chooseEd.getCode()));
|
||||
String chosenEdition = chooseEd.substring(chooseEd.lastIndexOf("(") + 1, chooseEd.lastIndexOf(")"));
|
||||
chooseEd = chooseEd.substring(0, chooseEd.lastIndexOf("(")).trim();
|
||||
final IUnOpenedProduct product = new UnOpenedProduct(FModel.getMagicDb().getBoosters().get(chosenEdition));
|
||||
cardsWon = product.get();
|
||||
}
|
||||
|
||||
qData.getCards().addAllCards(cardsWon);
|
||||
title = "Bonus " + chooseEd.getName() + " booster pack!";
|
||||
title = "Bonus " + chooseEd + " Booster Pack!";
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ public class QuestPreferences extends PreferencesStore<QuestPreferences.QPref> i
|
||||
// The preferred format of the won booster pack
|
||||
BOOSTER_FORMAT("Standard"),
|
||||
|
||||
SPECIAL_BOOSTERS("1"),
|
||||
|
||||
// How many credits are lost for losing a match
|
||||
PENALTY_LOSS("15"),
|
||||
|
||||
@@ -263,6 +265,7 @@ public class QuestPreferences extends PreferencesStore<QuestPreferences.QPref> i
|
||||
}
|
||||
break;
|
||||
|
||||
case SPECIAL_BOOSTERS:
|
||||
case ITEM_LEVEL_RESTRICTION:
|
||||
if (val != 0 && val != 1) {
|
||||
return "Only values 0 or 1 are acceptable; 1 for enabled, 0 for disabled.";
|
||||
|
||||
@@ -743,7 +743,11 @@ public class QuestDataIO {
|
||||
|
||||
protected void write(final BoosterPack booster, final Integer count, final HierarchicalStreamWriter writer) {
|
||||
writer.startNode("booster");
|
||||
writer.addAttribute("s", booster.getEdition());
|
||||
if (booster.getEdition().equals("?")) {
|
||||
writer.addAttribute("s", booster.getName().substring(0, booster.getName().indexOf(booster.getItemType()) - 1));
|
||||
} else {
|
||||
writer.addAttribute("s", booster.getEdition());
|
||||
}
|
||||
writer.addAttribute("n", count.toString());
|
||||
writer.endNode();
|
||||
}
|
||||
@@ -841,8 +845,13 @@ public class QuestDataIO {
|
||||
}
|
||||
|
||||
protected BoosterPack readBooster(final HierarchicalStreamReader reader) {
|
||||
final CardEdition ed = FModel.getMagicDb().getEditions().get(reader.getAttribute("s"));
|
||||
return BoosterPack.FN_FROM_SET.apply(ed);
|
||||
String s = reader.getAttribute("s");
|
||||
if (SealedProduct.specialSets.contains(s) || s.equals("?")) {
|
||||
return BoosterPack.FN_FROM_COLOR.apply(s);
|
||||
} else {
|
||||
final CardEdition ed = FModel.getMagicDb().getEditions().get(s);
|
||||
return BoosterPack.FN_FROM_SET.apply(ed);
|
||||
}
|
||||
}
|
||||
|
||||
protected TournamentPack readTournamentPack(final HierarchicalStreamReader reader) {
|
||||
|
||||
@@ -17,18 +17,17 @@
|
||||
*/
|
||||
package forge.quest.io;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
|
||||
import forge.card.MagicColor;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* ReadPriceList class.
|
||||
@@ -73,12 +72,13 @@ public class ReadPriceList {
|
||||
* @return a {@link java.util.HashMap} object.
|
||||
*/
|
||||
private Map<String, Integer> readFile(final String file) {
|
||||
final Map<String, Integer> map = new HashMap<String, Integer>();
|
||||
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
final Random r = MyRandom.getRandom();
|
||||
|
||||
final List<String> lines = FileUtil.readFile(file);
|
||||
for (final String line : lines) {
|
||||
if (line.trim().length() == 0) {
|
||||
if (line.trim().isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ public class ReadPriceList {
|
||||
try {
|
||||
int val = Integer.parseInt(price.trim());
|
||||
|
||||
if (!(MagicColor.Constant.BASIC_LANDS.contains(name) || MagicColor.Constant.SNOW_LANDS.contains(name))) {
|
||||
float ff = 0;
|
||||
if (!(MagicColor.Constant.BASIC_LANDS.contains(name) || MagicColor.Constant.SNOW_LANDS.contains(name)) && !ForgeConstants.PRICES_BOOSTER_FILE.equals(file)) {
|
||||
float ff;
|
||||
if (r.nextInt(100) < 90) {
|
||||
ff = r.nextInt(10) * (float) .01;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user