Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master

This commit is contained in:
Michael Kamensky
2021-06-05 17:34:06 +03:00
1889 changed files with 19680 additions and 14391 deletions

View File

@@ -151,6 +151,11 @@ public class StaticData {
return this.editions;
}
public final CardEdition.Collection getCustomEditions(){
return this.customEditions;
}
private List<CardEdition> sortedEditions;
public final List<CardEdition> getSortedEditions() {
if (sortedEditions == null) {
@@ -158,12 +163,24 @@ public class StaticData {
for (CardEdition set : editions) {
sortedEditions.add(set);
}
if (customEditions.size() > 0){
for (CardEdition set : customEditions) {
sortedEditions.add(set);
}
}
Collections.sort(sortedEditions);
Collections.reverse(sortedEditions); //put newer sets at the top
}
return sortedEditions;
}
public CardEdition getCardEdition(String setCode){
CardEdition edition = this.editions.get(setCode);
if (edition == null) // try custom editions
edition = this.customEditions.get(setCode);
return edition;
}
public PaperCard getOrLoadCommonCard(String cardName, String setCode, int artIndex, boolean foil) {
PaperCard card = commonCards.getCard(cardName, setCode, artIndex);
boolean isCustom = false;

View File

@@ -6,7 +6,6 @@ package forge.card;
*/
public class CardAiHints {
private final boolean isRemovedFromAIDecks;
private final boolean isRemovedFromRandomDecks;
private final boolean isRemovedFromNonCommanderDecks;
@@ -15,7 +14,6 @@ public class CardAiHints {
private final DeckHints deckNeeds;
private final DeckHints deckHas;
public CardAiHints(boolean remAi, boolean remRandom, boolean remUnlessCommander, DeckHints dh, DeckHints dn, DeckHints has) {
isRemovedFromAIDecks = remAi;
isRemovedFromRandomDecks = remRandom;
@@ -90,5 +88,4 @@ public class CardAiHints {
}
}
}

View File

@@ -533,6 +533,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
@Override
public int getPrintCount(String cardName, String edition) {
int cnt = 0;
if (edition == null || cardName == null)
return cnt;
for (PaperCard pc : getAllCards(cardName)) {
if (pc.getEdition().equals(edition)) {
cnt++;
@@ -544,6 +546,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
@Override
public int getMaxPrintCount(String cardName) {
int max = -1;
if (cardName == null)
return max;
for (PaperCard pc : getAllCards(cardName)) {
if (max < pc.getArtIndex()) {
max = pc.getArtIndex();
@@ -555,6 +559,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
@Override
public int getArtCount(String cardName, String setName) {
int cnt = 0;
if (cardName == null || setName == null)
return cnt;
Collection<PaperCard> cards = getAllCards(cardName);
if (null == cards) {
@@ -628,6 +634,23 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
}));
}
public Collection<PaperCard> getAllNonPromosNonReprintsNoAlt() {
return Lists.newArrayList(Iterables.filter(getAllCardsNoAlt(), new Predicate<PaperCard>() {
@Override
public boolean apply(final PaperCard paperCard) {
CardEdition edition = null;
try {
edition = editions.getEditionByCodeOrThrow(paperCard.getEdition());
if (edition.getType() == Type.PROMOS||edition.getType() == Type.REPRINT)
return false;
} catch (Exception ex) {
return false;
}
return true;
}
}));
}
public String getName(final String cardName) {
if (alternateName.containsKey(cardName)) {
return alternateName.get(cardName);
@@ -749,7 +772,6 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
}
public PaperCard createUnsupportedCard(String cardName) {
CardRequest request = CardRequest.fromString(cardName);
CardEdition cardEdition = CardEdition.UNKNOWN;
CardRarity cardRarity = CardRarity.Unknown;
@@ -790,7 +812,6 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
}
return new PaperCard(CardRules.getUnsupportedCardNamed(request.cardName), cardEdition.getCode(), cardRarity, 1);
}
private final Editor editor = new Editor();

View File

@@ -19,7 +19,6 @@ package forge.card;
import java.io.File;
import java.io.FilenameFilter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -121,13 +120,16 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
// commonly used printsheets with collector number
public enum EditionSectionWithCollectorNumbers {
CARDS("cards"),
SPECIAL_SLOT("special slot"), //to help with convoluted boosters
PRECON_PRODUCT("precon product"),
BORDERLESS("borderless"),
SHOWCASE("showcase"),
EXTENDED_ART("extended art"),
ALTERNATE_ART("alternate art"),
ALTERNATE_FRAME("alternate frame"),
BUY_A_BOX("buy a box"),
PROMO("promo"),
BUNDLE("bundle"),
BOX_TOPPER("box topper");
private final String name;
@@ -148,7 +150,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
}
}
public static class CardInSet {
public static class CardInSet implements Comparable<CardInSet> {
public final CardRarity rarity;
public final String collectorNumber;
public final String name;
@@ -172,6 +174,56 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
sb.append(name);
return sb.toString();
}
/**
* This method implements the main strategy to allow for natural ordering of collectorNumber
* (i.e. "1" < "10"), overloading the default lexicographic order (i.e. "10" < "1").
* Any non-numerical parts in the input collectorNumber will be also accounted for, and attached to the
* resulting sorting key, accordingly.
*
* @param collectorNumber: Input collectorNumber tro transform in a Sorting Key
* @return A 5-digits zero-padded collector number + any non-numerical parts attached.
*/
public static String getSortableCollectorNumber(final String collectorNumber){
String sortableCollNr = collectorNumber;
if (sortableCollNr == null || sortableCollNr.length() == 0)
sortableCollNr = "50000"; // very big number of 5 digits to have them in last positions
// Now, for proper sorting, let's zero-pad the collector number (if integer)
int collNr;
try {
collNr = Integer.parseInt(sortableCollNr);
sortableCollNr = String.format("%05d", collNr);
} catch (NumberFormatException ex) {
String nonNumeric = sortableCollNr.replaceAll("[0-9]", "");
String onlyNumeric = sortableCollNr.replaceAll("[^0-9]", "");
try {
collNr = Integer.parseInt(onlyNumeric);
} catch (NumberFormatException exon) {
collNr = 0; // this is the case of ONLY-letters collector numbers
}
if ((collNr > 0) && (sortableCollNr.startsWith(onlyNumeric))) // e.g. 12a, 37+, 2018f,
sortableCollNr = String.format("%05d", collNr) + nonNumeric;
else // e.g. WS6, S1
sortableCollNr = nonNumeric + String.format("%05d", collNr);
}
return sortableCollNr;
}
@Override
public int compareTo(CardInSet o) {
final int nameCmp = name.compareToIgnoreCase(o.name);
if (0 != nameCmp) {
return nameCmp;
}
String thisCollNr = getSortableCollectorNumber(collectorNumber);
String othrCollNr = getSortableCollectorNumber(o.collectorNumber);
final int collNrCmp = thisCollNr.compareTo(othrCollNr);
if (0 != collNrCmp) {
return collNrCmp;
}
return rarity.compareTo(o.rarity);
}
}
private final static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
@@ -206,6 +258,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
private String[] chaosDraftThemes = new String[0];
private final ListMultimap<String, CardInSet> cardMap;
private final List<CardInSet> cardsInSet;
private final Map<String, Integer> tokenNormalized;
// custom print sheets that will be loaded lazily
private final Map<String, List<String>> customPrintSheetsToParse;
@@ -215,13 +268,18 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
private CardEdition(ListMultimap<String, CardInSet> cardMap, Map<String, Integer> tokens, Map<String, List<String>> customPrintSheetsToParse) {
this.cardMap = cardMap;
this.cardsInSet = new ArrayList<>(cardMap.values());
Collections.sort(cardsInSet);
this.tokenNormalized = tokens;
this.customPrintSheetsToParse = customPrintSheetsToParse;
}
private CardEdition(CardInSet[] cards, Map<String, Integer> tokens) {
List<CardInSet> cardsList = Arrays.asList(cards);
this.cardMap = ArrayListMultimap.create();
this.cardMap.replaceValues("cards", Arrays.asList(cards));
this.cardMap.replaceValues("cards", cardsList);
this.cardsInSet = new ArrayList<>(cardsList);
Collections.sort(cardsInSet);
this.tokenNormalized = tokens;
this.customPrintSheetsToParse = new HashMap<>();
}
@@ -256,7 +314,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
date = date + "-01";
try {
return formatter.parse(date);
} catch (ParseException e) {
} catch (Exception e) {
return new Date();
}
}
@@ -287,7 +345,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
public List<CardInSet> getCards() { return cardMap.get("cards"); }
public List<CardInSet> getAllCardsInSet() {
return Lists.newArrayList(cardMap.values());
return cardsInSet;
}
public boolean isModern() { return getDate().after(parseDate("2003-07-27")); } //8ED and above are modern except some promo cards and others
@@ -306,7 +364,10 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
if (o == null) {
return 1;
}
return date.compareTo(o.date);
int dateComp = date.compareTo(o.date);
if (0 != dateComp)
return dateComp;
return name.compareTo(o.name);
}
@Override
@@ -340,7 +401,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
}
public boolean isLargeSet() {
return getAllCardsInSet().size() > 200 && !smallSetOverride;
return this.cardsInSet.size() > 200 && !smallSetOverride;
}
public int getCntBoosterPictures() {
@@ -414,7 +475,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
* rarity - grouping #4
* name - grouping #5
*/
"(^([0-9]+.?) )?(([SCURML]) )?(.*)$"
"(^([0-9A-Z]+.?) )?(([SCURML]) )?(.*)$"
);
ListMultimap<String, CardInSet> cardMap = ArrayListMultimap.create();

View File

@@ -84,6 +84,12 @@ public final class CardRules implements ICardCharacteristics {
boolean isReminder = false;
boolean isSymbol = false;
String oracleText = face.getOracleText();
// CR 903.4 colors defined by its characteristic-defining abilities
for (String staticAbility : face.getStaticAbilities()) {
if (staticAbility.contains("CharacteristicDefining$ True") && staticAbility.contains("SetColor$ All")) {
res |= MagicColor.ALL_COLORS;
}
}
int len = oracleText.length();
for(int i = 0; i < len; i++) {
char c = oracleText.charAt(i); // This is to avoid needless allocations performed by toCharArray()
@@ -388,7 +394,6 @@ public final class CardRules implements ICardCharacteristics {
this.removedFromNonCommanderDecks = "NonCommander".equalsIgnoreCase(value);
}
} else if ("AlternateMode".equals(key)) {
//System.out.println(faces[curFace].getName());
this.altMode = CardSplitType.smartValueOf(value);
} else if ("ALTERNATE".equals(key)) {
this.curFace = 1;
@@ -539,7 +544,6 @@ public final class CardRules implements ICardCharacteristics {
@Override
public final ManaCostShard next() {
final String unparsed = st.nextToken();
// System.out.println(unparsed);
if (StringUtils.isNumeric(unparsed)) {
this.genericCost += Integer.parseInt(unparsed);
return null;
@@ -555,7 +559,7 @@ public final class CardRules implements ICardCharacteristics {
*/
@Override
public void remove() {
} // unsuported
} // unsupported
}
}

View File

@@ -30,7 +30,6 @@ public class ManaCostParser implements IParserManaCost {
*/
public ManaCostParser(final String cost) {
this.cost = cost.split(" ");
// System.out.println(cost);
this.nextToken = 0;
this.genericCost = 0;
}
@@ -66,7 +65,6 @@ public class ManaCostParser implements IParserManaCost {
@Override
public final ManaCostShard next() {
final String unparsed = this.cost[this.nextToken++];
// System.out.println(unparsed);
if (StringUtils.isNumeric(unparsed)) {
this.genericCost += Integer.parseInt(unparsed);
return null;

View File

@@ -17,10 +17,11 @@ import forge.card.MagicColor;
import forge.util.PredicateCard;
import forge.util.PredicateString;
//import forge.Card;
public interface IPaperCard extends InventoryItem, Serializable {
String NO_COLLECTOR_NUMBER = "N.A."; // Placeholder for No-Collection number available
int DEFAULT_ART_INDEX = 1;
/**
* Number of filters based on CardPrinted values.
*/
@@ -228,6 +229,7 @@ public interface IPaperCard extends InventoryItem, Serializable {
String getName();
String getEdition();
String getCollectorNumber();
int getArtIndex();
boolean isFoil();
boolean isToken();

View File

@@ -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/>.
*/
@@ -26,6 +26,7 @@ import com.google.common.base.Function;
import forge.ImageKeys;
import forge.StaticData;
import forge.card.CardDb;
import forge.card.CardEdition;
import forge.card.CardRarity;
import forge.card.CardRules;
import forge.util.CardTranslation;
@@ -36,7 +37,7 @@ import forge.util.TextUtil;
* A lightweight version of a card that matches real-world cards, to use outside of games (eg. inventory, decks, trade).
* <br><br>
* The full set of rules is in the CardRules class.
*
*
* @author Forge
*/
public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard, Serializable {
@@ -48,12 +49,19 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
// These fields are kinda PK for PrintedCard
private final String name;
private final String edition;
/* [NEW] Attribute to store reference to CollectorNumber of each PaperCard.
By default the attribute is marked as "unset" so that it could be retrieved and set.
(see getCollectorNumber())
*/
private String collectorNumber = null;
private final int artIndex;
private final boolean foil;
private Boolean hasImage;
// Calculated fields are below:
private transient CardRarity rarity; // rarity is given in ctor when set is assigned
// Reference to a new instance of Self, but foiled!
private transient PaperCard foiledVersion = null;
@Override
public String getName() {
@@ -65,6 +73,23 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
return edition;
}
@Override
public String getCollectorNumber() {
/* The collectorNumber attribute is managed in a property-like fashion.
By default it is marked as "unset" (-1), which integrates with all constructors
invocations not including this as an extra parameter. In this way, the new
attribute could be added to the API with minimum disruption to code
throughout the other packages.
If "unset", the corresponding collectorNumber will be retrieved
from the corresponding CardEdition (see retrieveCollectorNumber)
* */
if (collectorNumber == null) {
collectorNumber = this.retrieveCollectorNumber();
}
return collectorNumber;
}
@Override
public int getArtIndex() {
return artIndex;
@@ -90,6 +115,20 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
return rarity;
}
/* FIXME: At the moment, every card can get Foiled, with no restriction on the
corresponding Edition - so we could Foil even Alpha cards.
*/
public PaperCard getFoiled() {
if (this.foil)
return this;
if (this.foiledVersion == null) {
this.foiledVersion = new PaperCard(this.rules, this.edition, this.rarity,
this.artIndex, true, String.valueOf(collectorNumber));
}
return this.foiledVersion;
}
// @Override
// public String getImageKey() {
// return getImageLocator(getImageName(), getArtIndex(), true, false);
@@ -124,11 +163,16 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
}
};
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0) {
this(rules0, edition0, rarity0, IPaperCard.DEFAULT_ART_INDEX, false);
}
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, final int artIndex0) {
this(rules0, edition0, rarity0, artIndex0, false);
}
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, final int artIndex0, final boolean foil0) {
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, final int artIndex0,
final boolean foil0) {
if (rules0 == null || edition0 == null || rarity0 == null) {
throw new IllegalArgumentException("Cannot create card without rules, edition or rarity");
}
@@ -140,6 +184,17 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
rarity = rarity0;
}
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, final int artIndex0,
final String collectorNumber0) {
this(rules0, edition0, rarity0, artIndex0, false, collectorNumber0);
}
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0,
final int artIndex0, final boolean foil0, final String collectorNumber0) {
this(rules0, edition0, rarity0, artIndex0, foil0);
collectorNumber = collectorNumber0;
}
// Want this class to be a key for HashTable
@Override
public boolean equals(final Object obj) {
@@ -160,32 +215,27 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
if (!edition.equals(other.edition)) {
return false;
}
if ((other.foil != foil) || (other.artIndex != artIndex)) {
if (!getCollectorNumber().equals(other.getCollectorNumber()))
return false;
}
return true;
return (other.foil == foil) && (other.artIndex == artIndex);
}
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int code = (name.hashCode() * 11) + (edition.hashCode() * 59) + (artIndex * 2);
final int code = (name.hashCode() * 11) + (edition.hashCode() * 59) +
(artIndex * 2) + (getCollectorNumber().hashCode() * 383);
if (foil) {
return code + 1;
}
return code;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
// FIXME: Check
@Override
public String toString() {
return CardTranslation.getTranslatedName(name);
@@ -194,21 +244,49 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
}
/*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
* This (utility) method transform a collectorNumber String into a key string for sorting.
* This method proxies the same strategy implemented in CardEdition.CardInSet class from which the
* collectorNumber of PaperCard instances are originally retrieved.
* This is also to centralise the criterion, whilst avoiding code duplication.
*
* Note: The method has been made private as this is for internal API use **only**, to allow
* for generalised comparison with IPaperCard instances (see compareTo)
*
* The public API of PaperCard includes a method (i.e. getCollectorNumberSortingKey) which applies
* this method on instance's own collector number.
*
* @return a zero-padded 5-digits String + any non-numerical content in the input String, properly attached.
*/
private static String makeCollectorNumberSortingKey(final String collectorNumber0){
String collectorNumber = collectorNumber0;
if (collectorNumber.equals(NO_COLLECTOR_NUMBER))
collectorNumber = null;
return CardEdition.CardInSet.getSortableCollectorNumber(collectorNumber);
}
public String getCollectorNumberSortingKey(){
// Hardly the case, but just invoke getter rather than direct
// attribute to be sure that collectorNumber has been retrieved already!
return makeCollectorNumberSortingKey(getCollectorNumber());
}
@Override
public int compareTo(final IPaperCard o) {
final int nameCmp = getName().compareToIgnoreCase(o.getName());
final int nameCmp = name.compareToIgnoreCase(o.getName());
if (0 != nameCmp) {
return nameCmp;
}
// TODO compare sets properly
//FIXME: compare sets properly
int setDiff = edition.compareTo(o.getEdition());
if ( 0 != setDiff )
if (0 != setDiff)
return setDiff;
String thisCollNrKey = getCollectorNumberSortingKey();
String othrCollNrKey = makeCollectorNumberSortingKey(o.getCollectorNumber());
final int collNrCmp = thisCollNrKey.compareTo(othrCollNrKey);
if (0 != collNrCmp) {
return collNrCmp;
}
return Integer.compare(artIndex, o.getArtIndex());
}
@@ -216,8 +294,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
// default deserialization
ois.defaultReadObject();
IPaperCard pc = null;
pc = StaticData.instance().getCommonCards().getCard(name, edition, artIndex);
IPaperCard pc = StaticData.instance().getCommonCards().getCard(name, edition, artIndex);
if (pc == null) {
pc = StaticData.instance().getVariantCards().getCard(name, edition, artIndex);
if (pc == null) {
@@ -228,9 +305,35 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
rarity = pc.getRarity();
}
private String retrieveCollectorNumber() {
StaticData data = StaticData.instance();
CardEdition edition = data.getEditions().get(this.edition);
if (edition == null) {
edition = data.getCustomEditions().get(this.edition);
if (edition == null) // don't bother continuing - non-existing card!
return NO_COLLECTOR_NUMBER;
}
int artIndexCount = 0;
String collectorNumberInEdition = "";
for (CardEdition.CardInSet card : edition.getAllCardsInSet()) {
if (card.name.equalsIgnoreCase(this.name)) {
artIndexCount += 1;
if (artIndexCount == this.artIndex) {
collectorNumberInEdition = card.collectorNumber;
break;
}
}
}
// CardEdition stores collectorNumber as a String, which is null if there isn't any.
// In this case, the NO_COLLECTOR_NUMBER value (i.e. 0) is returned.
return ((collectorNumberInEdition != null) && (collectorNumberInEdition.length() > 0)) ?
collectorNumberInEdition : NO_COLLECTOR_NUMBER;
}
@Override
public String getImageKey(boolean altState) {
String imageKey = ImageKeys.CARD_PREFIX + name + CardDb.NameSetSeparator + edition + CardDb.NameSetSeparator + artIndex;
String imageKey = ImageKeys.CARD_PREFIX + name + CardDb.NameSetSeparator
+ edition + CardDb.NameSetSeparator + artIndex;
if (altState) {
imageKey += ImageKeys.BACKFACE_POSTFIX;
}

View File

@@ -132,6 +132,12 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
@Override public String toString() { return name; }
@Override public String getEdition() { return edition != null ? edition.getCode() : "???"; }
@Override
public String getCollectorNumber() {
return IPaperCard.NO_COLLECTOR_NUMBER;
}
@Override public int getArtIndex() { return artIndex; }
@Override public boolean isFoil() { return false; }
@Override public CardRules getRules() { return card; }

View File

@@ -70,12 +70,14 @@ public class BoosterGenerator {
}
private static PaperCard generateFoilCard(PrintSheet sheet) {
return StaticData.instance().getCommonCards().getFoiled(sheet.random(1, true).get(0));
PaperCard randomCard = sheet.random(1, true).get(0);
return randomCard.getFoiled();
}
private static PaperCard generateFoilCard(List<PaperCard> cardList) {
Collections.shuffle(cardList, MyRandom.getRandom());
return StaticData.instance().getCommonCards().getFoiled(cardList.get(0));
PaperCard randomCard = cardList.get(0);
return randomCard.getFoiled();
}
public static List<PaperCard> getBoosterPack(SealedProduct.Template template) {
@@ -461,7 +463,7 @@ public class BoosterGenerator {
if (toReplace != null) {
// Keep the foil state
if (toReplace.isFoil()) {
toAdd = StaticData.instance().getCommonCards().getFoiled(toAdd);
toAdd = toAdd.getFoiled();
}
booster.remove(toReplace);
booster.add(toAdd);

View File

@@ -21,7 +21,6 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -208,7 +207,7 @@ public final class FileUtil {
if ((file == null) || !file.exists()) {
return new ArrayList<>();
}
return FileUtil.readAllLines(new FileReader(file), false);
return FileUtil.readAllLines(file, false);
} catch (final Exception ex) {
throw new RuntimeException("FileUtil : readFile() error, " + ex);
}
@@ -248,6 +247,31 @@ public final class FileUtil {
}
return list;
}
/**
* Reads all lines from given file to a list of strings.
*
* @param file is the File to read.
* @param mayTrim defines whether to trim lines.
* @return list of strings
*/
public static List<String> readAllLines(final File file, final boolean mayTrim) {
final List<String> list = new ArrayList<>();
try {
final BufferedReader in = new BufferedReader(
new InputStreamReader(new FileInputStream(file), "UTF-8"));
String line;
while ((line = in.readLine()) != null) {
if (mayTrim) {
line = line.trim();
}
list.add(line);
}
in.close();
} catch (final IOException ex) {
throw new RuntimeException("FileUtil : readAllLines() error, " + ex);
}
return list;
}
// returns a list of <name, url> pairs. if the name is not in the file, it is synthesized from the url
public static List<Pair<String, String>> readNameUrlFile(String nameUrlFile) {

View File

@@ -99,7 +99,7 @@ public class ImageUtil {
final CardDb db = StaticData.instance().getCommonCards();
return db.getRules(card.getMeldWith()).getOtherPart().getName();
} else {
return null;
return null;
}
else
return null;
@@ -118,6 +118,27 @@ public class ImageUtil {
return getImageRelativePath(cp, backFace, true, true);
}
public static String getScryfallDownloadUrl(PaperCard cp, boolean backFace, String setCode){
return getScryfallDownloadUrl(cp, backFace, setCode, "en");
}
public static String getScryfallDownloadUrl(PaperCard cp, boolean backFace, String setCode, String langCode){
String editionCode;
if ((setCode != null) && (setCode.length() > 0))
editionCode = setCode;
else
editionCode = cp.getEdition().toLowerCase();
String cardCollectorNumber = cp.getCollectorNumber();
// Hack to account for variations in Arabian Nights
cardCollectorNumber = cardCollectorNumber.replace("+", "");
String faceParam = "";
if (cp.getRules().getOtherPart() != null) {
faceParam = (backFace ? "&face=back" : "&face=front");
}
return String.format("%s/%s/%s?format=image&version=normal%s", editionCode, cardCollectorNumber,
langCode, faceParam);
}
public static String toMWSFilename(String in) {
final StringBuilder out = new StringBuilder();
char c;
@@ -130,4 +151,4 @@ public class ImageUtil {
}
return out.toString();
}
}
}

View File

@@ -15,147 +15,155 @@ import java.util.MissingResourceException;
import java.util.ResourceBundle;
public class Localizer {
private static Localizer instance;
private List<LocalizationChangeObserver> observers = new ArrayList<>();
private Locale locale;
private ResourceBundle resourceBundle;
private static Localizer instance;
public static Localizer getInstance() {
if (instance == null) {
synchronized (Localizer.class) {
instance = new Localizer();
}
}
return instance;
}
private Localizer() {
}
public void initialize(String localeID, String languagesDirectory) {
setLanguage(localeID, languagesDirectory);
}
private List<LocalizationChangeObserver> observers = new ArrayList<>();
public String convert(String value, String fromEncoding, String toEncoding) throws UnsupportedEncodingException {
return new String(value.getBytes(fromEncoding), toEncoding);
}
private Locale locale;
private ResourceBundle resourceBundle;
public String charset(String value, String charsets[]) {
String probe = StandardCharsets.UTF_8.name();
for(String c : charsets) {
Charset charset = Charset.forName(c);
if(charset != null) {
try {
if (value.equals(convert(convert(value, charset.name(), probe), probe, charset.name()))) {
return c;
}
} catch(UnsupportedEncodingException ignored) {}
}
}
return StandardCharsets.UTF_8.name();
}
public static Localizer getInstance() {
if (instance == null) {
synchronized (Localizer.class) {
instance = new Localizer();
}
}
return instance;
}
public String getMessage(final String key, final Object... messageArguments) {
MessageFormat formatter = null;
private Localizer() {
}
try {
//formatter = new MessageFormat(resourceBundle.getString(key.toLowerCase()), locale);
formatter = new MessageFormat(resourceBundle.getString(key), locale);
} catch (final IllegalArgumentException | MissingResourceException e) {
e.printStackTrace();
}
if (formatter == null) {
System.err.println("INVALID PROPERTY: '" + key + "' -- Translation Needed?");
return "INVALID PROPERTY: '" + key + "' -- Translation Needed?";
}
formatter.setLocale(locale);
String formattedMessage = "CHAR ENCODING ERROR";
final String[] charsets = { "ISO-8859-1", "UTF-8" };
//Support non-English-standard characters
String detectedCharset = charset(resourceBundle.getString(key), charsets);
public void initialize(String localeID, String languagesDirectory) {
setLanguage(localeID, languagesDirectory);
}
final int argLength = messageArguments.length;
Object[] syncEncodingMessageArguments = new Object[argLength];
//when messageArguments encoding not equal resourceBundle.getString(key),convert to equal
//avoid convert to a have two encoding content formattedMessage string.
for (int i = 0; i < argLength; i++) {
String objCharset = charset(messageArguments[i].toString(), charsets);
try {
syncEncodingMessageArguments[i] = convert(messageArguments[i].toString(), objCharset, detectedCharset);
} catch (UnsupportedEncodingException ignored) {
System.err.println("Cannot Convert '" + messageArguments[i].toString() + "' from '" + objCharset + "' To '" + detectedCharset + "'");
return "encoding '" + key + "' translate string failure";
}
}
public String convert(String value, String fromEncoding, String toEncoding) throws UnsupportedEncodingException {
return new String(value.getBytes(fromEncoding), toEncoding);
}
try {
formattedMessage = new String(formatter.format(syncEncodingMessageArguments).getBytes(detectedCharset), StandardCharsets.UTF_8);
} catch(UnsupportedEncodingException ignored) {}
public String charset(String value, String charsets[]) {
String probe = StandardCharsets.UTF_8.name();
for(String c : charsets) {
Charset charset = Charset.forName(c);
if(charset != null) {
try {
if (value.equals(convert(convert(value, charset.name(), probe), probe, charset.name()))) {
return c;
}
} catch(UnsupportedEncodingException ignored) {}
}
}
return StandardCharsets.UTF_8.name();
}
return formattedMessage;
}
public String getMessageorUseDefault(final String key, final String defaultValue, final Object... messageArguments) {
try {
return getMessage(key, messageArguments);
} catch (Exception e) {
return defaultValue;
}
}
//FIXME: localizer should return default value from english locale or it will crash some GUI element like the NewGameMenu->NewGameScreen Popup when returned null...
public String getMessage(final String key, final Object... messageArguments) {
MessageFormat formatter = null;
public void setLanguage(final String languageRegionID, final String languagesDirectory) {
String[] splitLocale = languageRegionID.split("-");
Locale oldLocale = locale;
locale = new Locale(splitLocale[0], splitLocale[1]);
//Don't reload the language if nothing changed
if (oldLocale == null || !oldLocale.equals(locale)) {
try {
//formatter = new MessageFormat(resourceBundle.getString(key.toLowerCase()), locale);
formatter = new MessageFormat(resourceBundle.getString(key), locale);
} catch (final IllegalArgumentException | MissingResourceException e) {
e.printStackTrace();
}
File file = new File(languagesDirectory);
URL[] urls = null;
try {
urls = new URL[] { file.toURI().toURL() };
} catch (MalformedURLException e) {
e.printStackTrace();
}
if (formatter == null) {
System.err.println("INVALID PROPERTY: '" + key + "' -- Translation Needed?");
return "INVALID PROPERTY: '" + key + "' -- Translation Needed?";
}
ClassLoader loader = new URLClassLoader(urls);
formatter.setLocale(locale);
try {
resourceBundle = ResourceBundle.getBundle(languageRegionID, new Locale(splitLocale[0], splitLocale[1]), loader);
} catch (NullPointerException | MissingResourceException e) {
//If the language can't be loaded, default to US English
resourceBundle = ResourceBundle.getBundle("en-US", new Locale("en", "US"), loader);
e.printStackTrace();
}
String formattedMessage = "CHAR ENCODING ERROR";
final String[] charsets = { "ISO-8859-1", "UTF-8" };
//Support non-English-standard characters
String detectedCharset = charset(resourceBundle.getString(key), charsets);
System.out.println("Language '" + resourceBundle.toString() + "' loaded successfully.");
notifyObservers();
}
}
public List<Language> getLanguages() {
//TODO List all languages by getting their files
return null;
}
public void registerObserver(LocalizationChangeObserver observer) {
observers.add(observer);
}
private void notifyObservers() {
for (LocalizationChangeObserver observer : observers) {
observer.localizationChanged();
}
}
final int argLength = messageArguments.length;
Object[] syncEncodingMessageArguments = new Object[argLength];
//when messageArguments encoding not equal resourceBundle.getString(key),convert to equal
//avoid convert to a have two encoding content formattedMessage string.
for (int i = 0; i < argLength; i++) {
String objCharset = charset(messageArguments[i].toString(), charsets);
try {
syncEncodingMessageArguments[i] = convert(messageArguments[i].toString(), objCharset, detectedCharset);
} catch (UnsupportedEncodingException ignored) {
System.err.println("Cannot Convert '" + messageArguments[i].toString() + "' from '" + objCharset + "' To '" + detectedCharset + "'");
return "encoding '" + key + "' translate string failure";
}
}
public static class Language {
public String languageName;
public String languageID;
}
try {
formattedMessage = new String(formatter.format(syncEncodingMessageArguments).getBytes(detectedCharset), StandardCharsets.UTF_8);
} catch(UnsupportedEncodingException ignored) {}
return formattedMessage;
}
public void setLanguage(final String languageRegionID, final String languagesDirectory) {
String[] splitLocale = languageRegionID.split("-");
Locale oldLocale = locale;
locale = new Locale(splitLocale[0], splitLocale[1]);
//Don't reload the language if nothing changed
if (oldLocale == null || !oldLocale.equals(locale)) {
File file = new File(languagesDirectory);
URL[] urls = null;
try {
urls = new URL[] { file.toURI().toURL() };
} catch (MalformedURLException e) {
e.printStackTrace();
}
ClassLoader loader = new URLClassLoader(urls);
try {
resourceBundle = ResourceBundle.getBundle(languageRegionID, new Locale(splitLocale[0], splitLocale[1]), loader);
} catch (NullPointerException | MissingResourceException e) {
//If the language can't be loaded, default to US English
resourceBundle = ResourceBundle.getBundle("en-US", new Locale("en", "US"), loader);
e.printStackTrace();
}
System.out.println("Language '" + resourceBundle.toString() + "' loaded successfully.");
notifyObservers();
}
}
public List<Language> getLanguages() {
//TODO List all languages by getting their files
return null;
}
public void registerObserver(LocalizationChangeObserver observer) {
observers.add(observer);
}
private void notifyObservers() {
for (LocalizationChangeObserver observer : observers) {
observer.localizationChanged();
}
}
public static class Language {
public String languageName;
public String languageID;
}
}