mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 20:58:03 +00:00
Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user