Merge branch 'token_scripts' into 'master'

Allow scripts to be written for Tokens

See merge request core-developers/forge!236
This commit is contained in:
austinio7116
2018-02-27 22:14:13 +00:00
16 changed files with 633 additions and 263 deletions

View File

@@ -43,7 +43,7 @@ import java.util.zip.ZipFile;
import forge.util.BuildInfo;
import org.apache.commons.lang3.time.StopWatch;
import com.google.common.io.Files;
import forge.card.CardRules;
import forge.util.FileUtil;
import forge.util.Localizer;
@@ -71,6 +71,7 @@ public class CardStorageReader {
}
private static final String CARD_FILE_DOT_EXTENSION = ".txt";
private static final String UPCOMING = "upcoming";
/** Default charset when loading from files. */
public static final String DEFAULT_CHARSET_NAME = "UTF-8";
@@ -80,6 +81,7 @@ public class CardStorageReader {
private final ProgressObserver progressObserver;
private final boolean loadingTokens;
private transient File cardsfolder;
private transient ZipFile zip;
@@ -91,6 +93,9 @@ public class CardStorageReader {
public CardStorageReader(final String cardDataDir, final CardStorageReader.ProgressObserver progressObserver, boolean loadCardsLazily) {
this.progressObserver = progressObserver != null ? progressObserver : CardStorageReader.ProgressObserver.emptyObserver;
this.cardsfolder = new File(cardDataDir);
this.loadingTokens = cardDataDir.contains("token");
this.loadCardsLazily = loadCardsLazily;
// These read data for lightweight classes.
@@ -240,6 +245,9 @@ public class CardStorageReader {
final Set<CardRules> result = new TreeSet<>(new Comparator<CardRules>() {
@Override
public int compare(final CardRules o1, final CardRules o2) {
if (loadingTokens) {
return String.CASE_INSENSITIVE_ORDER.compare(o1.getNormalizedName(), o2.getNormalizedName());
}
return String.CASE_INSENSITIVE_ORDER.compare(o1.getName(), o2.getName());
}
});
@@ -252,7 +260,7 @@ public class CardStorageReader {
if (!allFiles.isEmpty()) {
int fileParts = zip == null ? NUMBER_OF_PARTS : 1 + NUMBER_OF_PARTS / 3;
if (allFiles.size() < fileParts * 100) {
fileParts = allFiles.size() / 100; // to avoid creation of many threads for a dozen of files
fileParts = Math.max(1, allFiles.size() / 100); // to avoid creation of many threads for a dozen of files
}
final CountDownLatch cdlFiles = new CountDownLatch(fileParts);
final List<Callable<List<CardRules>>> taskFiles = makeTaskListForFiles(allFiles, cdlFiles);
@@ -377,7 +385,7 @@ public class CardStorageReader {
continue;
}
if (filename.equalsIgnoreCase("upcoming") && !BuildInfo.isDevelopmentVersion()) {
if (filename.equalsIgnoreCase(CardStorageReader.UPCOMING) && !BuildInfo.isDevelopmentVersion()) {
// If upcoming folder exits, only load these cards on development builds
continue;
}
@@ -402,7 +410,7 @@ public class CardStorageReader {
fileInputStream = new FileInputStream(file);
reader.reset();
final List<String> lines = readScript(fileInputStream);
return reader.readCard(lines);
return reader.readCard(lines, Files.getNameWithoutExtension(file.getName()));
} catch (final FileNotFoundException ex) {
throw new RuntimeException("CardReader : run error -- file not found: " + file.getPath(), ex);
} finally {
@@ -430,7 +438,7 @@ public class CardStorageReader {
zipInputStream = this.zip.getInputStream(entry);
rulesReader.reset();
return rulesReader.readCard(readScript(zipInputStream));
return rulesReader.readCard(readScript(zipInputStream), Files.getNameWithoutExtension(entry.getName()));
} catch (final IOException exn) {
throw new RuntimeException(exn);
// PM

View File

@@ -9,6 +9,7 @@ import forge.item.BoosterBox;
import forge.item.FatPack;
import forge.item.PaperCard;
import forge.item.SealedProduct;
import forge.token.TokenDb;
import forge.util.storage.IStorage;
import forge.util.storage.StorageBase;
@@ -22,10 +23,13 @@ import java.util.*;
* @author Max
*/
public class StaticData {
private final CardStorageReader reader;
private final CardStorageReader cardReader;
private final CardStorageReader tokenReader;
private final String blockDataFolder;
private final CardDb commonCards;
private final CardDb variantCards;
private final TokenDb allTokens;
private final CardEdition.Collection editions;
// Loaded lazily:
@@ -38,32 +42,50 @@ public class StaticData {
private static StaticData lastInstance = null;
public StaticData(CardStorageReader reader, String editionFolder, String blockDataFolder) {
this.reader = reader;
public StaticData(CardStorageReader cardReader, String editionFolder, String blockDataFolder) {
this(cardReader, null, editionFolder, blockDataFolder);
}
public StaticData(CardStorageReader cardReader, CardStorageReader tokenReader, String editionFolder, String blockDataFolder) {
this.cardReader = cardReader;
this.tokenReader = tokenReader;
this.editions = new CardEdition.Collection(new CardEdition.Reader(new File(editionFolder)));
this.blockDataFolder = blockDataFolder;
lastInstance = this;
final Map<String, CardRules> regularCards = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
final Map<String, CardRules> variantsCards = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
{
final Map<String, CardRules> regularCards = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
final Map<String, CardRules> variantsCards = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (CardRules card : reader.loadCards()) {
if (null == card) continue;
for (CardRules card : cardReader.loadCards()) {
if (null == card) continue;
final String cardName = card.getName();
if (card.isVariant()) {
variantsCards.put(cardName, card);
} else {
regularCards.put(cardName, card);
final String cardName = card.getName();
if (card.isVariant()) {
variantsCards.put(cardName, card);
} else {
regularCards.put(cardName, card);
}
}
commonCards = new CardDb(regularCards, editions);
variantCards = new CardDb(variantsCards, editions);
//muse initialize after establish field values for the sake of card image logic
commonCards.initialize(false, false);
variantCards.initialize(false, false);
}
commonCards = new CardDb(regularCards, editions);
variantCards = new CardDb(variantsCards, editions);
{
final Map<String, CardRules> tokens = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
//muse initialize after establish field values for the sake of card image logic
commonCards.initialize(false, false);
variantCards.initialize(false, false);
for (CardRules card : tokenReader.loadCards()) {
if (null == card) continue;
tokens.put(card.getNormalizedName(), card);
}
allTokens = new TokenDb(tokens, editions);
}
}
public static StaticData instance() {
@@ -91,7 +113,7 @@ public class StaticData {
PaperCard card = commonCards.getCard(cardName, setCode, artIndex);
if (card == null) {
attemptToLoadCard(cardName, setCode);
commonCards.getCard(cardName, setCode, artIndex);
card = commonCards.getCard(cardName, setCode, artIndex);
}
if (card == null) {
card = commonCards.getCard(cardName, setCode, -1);
@@ -105,7 +127,7 @@ public class StaticData {
public void attemptToLoadCard(String encodedCardName, String setCode) {
CardDb.CardRequest r = CardRequest.fromString(encodedCardName);
String cardName = r.cardName;
CardRules rules = reader.attemptToLoadCard(cardName, setCode);
CardRules rules = cardReader.attemptToLoadCard(cardName, setCode);
if (rules != null) {
if (rules.isVariant()) {
variantCards.loadCard(cardName, rules);
@@ -162,6 +184,8 @@ public class StaticData {
return variantCards;
}
public TokenDb getAllTokens() { return allTokens; }
public PaperCard getCardByEditionDate(PaperCard card, Date editionDate) {
PaperCard c = this.getCommonCards().getCardFromEdition(card.getName(), editionDate, CardDb.SetPreference.LatestCoreExp, card.getArtIndex());

View File

@@ -74,6 +74,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
// NO GETTERS/SETTERS HERE!
public static class CardRequest {
// TODO Move Request to its own class
public String cardName;
public String edition;
public int artIndex;

View File

@@ -26,6 +26,7 @@ public enum CardRarity {
Rare("R", "Rare"),
MythicRare("M", "Mythic Rare"),
Special("S", "Special"), // Timeshifted
None("N", "None"), // Tokens
Unknown("?", "Unknown"); // In development
public static final CardRarity[] FILTER_OPTIONS = new CardRarity[] {

View File

@@ -34,6 +34,7 @@ import java.util.StringTokenizer;
* @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $
*/
public final class CardRules implements ICardCharacteristics {
private String normalizedName;
private CardSplitType splitType;
private ICardFace mainPart;
private ICardFace otherPart;
@@ -124,6 +125,9 @@ public final class CardRules implements ICardCharacteristics {
}
}
public String getNormalizedName() { return normalizedName; }
public void setNormalizedName(String filename) { normalizedName = filename; }
public CardAiHints getAiHints() {
return aiHints;
}
@@ -207,12 +211,6 @@ public final class CardRules implements ICardCharacteristics {
return meldWith;
}
// public Set<String> getSets() { return this.setsPrinted.keySet(); }
// public CardInSet getEditionInfo(final String setCode) {
// final CardInSet result = this.setsPrinted.get(setCode);
// return result; // if returns null, String.format("Card '%s' was never printed in set '%s'", this.getName(), setCode);
// }
// vanguard card fields, they don't use sides.
private int deltaHand;
private int deltaLife;
@@ -260,6 +258,7 @@ public final class CardRules implements ICardCharacteristics {
private CardSplitType altMode = CardSplitType.None;
private String meldWith = "";
private String handLife = null;
private String normalizedName = "";
// fields to build CardAiHints
private boolean removedFromAIDecks = false;
@@ -287,6 +286,7 @@ public final class CardRules implements ICardCharacteristics {
this.hints = null;
this.has = null;
this.meldWith = "";
this.normalizedName = "";
}
/**
@@ -299,6 +299,8 @@ public final class CardRules implements ICardCharacteristics {
faces[0].assignMissingFields();
if (null != faces[1]) faces[1].assignMissingFields();
final CardRules result = new CardRules(faces, altMode, cah);
result.setNormalizedName(this.normalizedName);
result.meldWith = this.meldWith;
result.setDlUrls(pictureUrl);
if (StringUtils.isNotBlank(handLife))
@@ -306,7 +308,7 @@ public final class CardRules implements ICardCharacteristics {
return result;
}
public final CardRules readCard(final Iterable<String> script) {
public final CardRules readCard(final Iterable<String> script, String filename) {
this.reset();
for (String line : script) {
if (line.isEmpty() || line.charAt(0) == '#') {
@@ -314,9 +316,14 @@ public final class CardRules implements ICardCharacteristics {
}
this.parseLine(line);
}
this.normalizedName = filename;
return this.getCard();
}
public final CardRules readCard(final Iterable<String> script) {
return readCard(script, null);
}
/**
* Parses the line.
*

View File

@@ -4,10 +4,12 @@ import forge.ImageKeys;
import forge.card.CardEdition;
import forge.card.CardRarity;
import forge.card.CardRules;
import forge.card.ColorSet;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Locale;
public class PaperToken implements InventoryItemFromSet, IPaperCard {
private String name;
private CardEdition edition;
@@ -29,21 +31,91 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
return out.toString().toLowerCase(Locale.ENGLISH);
}
public static String makeTokenFileName(String colors, int power, int toughness, String name) {
return makeTokenFileName(colors, String.valueOf(power), String.valueOf(toughness), name);
public static String makeTokenFileName(String colors, String power, String toughness, String types) {
return makeTokenFileName(null, colors, power, toughness, types);
}
public static String makeTokenFileName(String colors, String power, String toughness, String name) {
StringBuilder fileName = new StringBuilder();
fileName.append(colors).append('_').append(power).append('_').append(toughness).append('_').append(name);
return makeTokenFileName(fileName.toString());
public static String makeTokenFileName(String name, String colors, String power, String toughness, String types) {
ArrayList<String> build = new ArrayList<>();
if (name != null) {
build.add(name);
}
build.add(colors);
if (power != null && toughness != null) {
build.add(power);
build.add(toughness);
}
build.add(types);
String fileName = StringUtils.join(build, "_");
return makeTokenFileName(fileName);
}
public PaperToken(final CardRules c, CardEdition edition0, final String imageFileName) {
public static String makeTokenFileName(final CardRules rules, CardEdition edition) {
ArrayList<String> build = new ArrayList<>();
String subtypes = StringUtils.join(rules.getType().getSubtypes(), " ");
if (!rules.getName().equals(subtypes)) {
return makeTokenFileName(rules.getName());
}
ColorSet colors = rules.getColor();
if (colors.isColorless()) {
build.add("C");
} else {
String color = "";
if (colors.hasWhite()) color += "W";
if (colors.hasBlue()) color += "U";
if (colors.hasBlack()) color += "B";
if (colors.hasRed()) color += "R";
if (colors.hasGreen()) color += "G";
build.add(color);
}
if (rules.getPower() != null && rules.getToughness() != null) {
build.add(rules.getPower());
build.add(rules.getToughness());
}
String cardTypes = "";
if (rules.getType().isArtifact()) cardTypes += "A";
if (rules.getType().isEnchantment()) cardTypes += "E";
if (!cardTypes.isEmpty()) {
build.add(cardTypes);
}
build.add(subtypes);
// Are these keywords sorted?
for(String keyword : rules.getMainPart().getKeywords()) {
build.add(keyword);
}
build.add(edition.getCode());
// Should future image file names be all lower case? Instead of Up case sets?
return StringUtils.join(build, "_").toLowerCase();
}
public PaperToken(final CardRules c) { this(c, null, null); }
public PaperToken(final CardRules c, final String fileName) { this(c, null, fileName); }
public PaperToken(final CardRules c, CardEdition edition) { this(c, edition, null); }
public PaperToken(final CardRules c, CardEdition edition0, String imageFileName) {
this.card = c;
this.name = c.getName();
this.edition = edition0;
this.imageFileName = String.format("%s%s", null == edition || CardEdition.UNKNOWN == edition ? "" : edition.getCode(), imageFileName);
if (imageFileName == null) {
this.imageFileName = makeTokenFileName(c, edition0);
} else {
String formatEdition = null == edition || CardEdition.UNKNOWN == edition ? "" : edition.getCode();
this.imageFileName = String.format("%s%s", formatEdition, imageFileName);
}
}
@Override public String getName() { return name; }
@@ -54,7 +126,7 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
@Override public boolean isFoil() { return false; }
@Override public CardRules getRules() { return card; }
@Override public CardRarity getRarity() { return CardRarity.Common; } // They don't have rarity though!
@Override public CardRarity getRarity() { return CardRarity.None; }
// Unfortunately this is a property of token, cannot move it outside of class
public String getImageFilename() { return imageFileName; }

View File

@@ -0,0 +1,32 @@
package forge.token;
import com.google.common.base.Predicate;
import forge.card.CardDb;
import forge.item.PaperToken;
import java.util.Collection;
import java.util.Date;
import java.util.List;
public interface ITokenDatabase extends Iterable<PaperToken> {
PaperToken getToken(String tokenName);
PaperToken getToken(String tokenName, String edition);
PaperToken getToken(String tokenName, String edition, int artIndex);
PaperToken getTokenFromEdition(String tokenName, CardDb.SetPreference fromSet);
PaperToken getTokenFromEdition(String tokenName, Date printedBefore, CardDb.SetPreference fromSet);
PaperToken getTokenFromEdition(String tokenName, Date printedBefore, CardDb.SetPreference fromSet, int artIndex);
PaperToken getFoiled(PaperToken cpi);
int getPrintCount(String tokenName, String edition);
int getMaxPrintCount(String tokenName);
int getArtCount(String tokenName, String edition);
Collection<PaperToken> getUniqueTokens();
List<PaperToken> getAllTokens();
List<PaperToken> getAllTokens(String tokenName);
List<PaperToken> getAllTokens(Predicate<PaperToken> predicate);
Predicate<? super PaperToken> wasPrintedInSets(List<String> allowedSetCodes);
}

View File

@@ -0,0 +1,121 @@
package forge.token;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import forge.card.*;
import forge.item.PaperCard;
import forge.item.PaperToken;
import java.util.*;
public class TokenDb implements ITokenDatabase {
// Expected naming convention of scripts
// token_name
// minor_demon
// marit_lage
// gold
// colors_power_toughness_cardtypes_sub_types_keywords
// Some examples:
// c_3_3_a_wurm_lifelink
// w_2_2_knight_first_strike
// The image names should be the same as the script name + _set
// If that isn't found, consider falling back to the original token
private final Map<String, PaperToken> tokensByName = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
private final CardEdition.Collection editions;
private final Map<String, CardRules> rulesByName;
public TokenDb(Map<String, CardRules> rules, CardEdition.Collection editions) {
this.rulesByName = rules;
this.editions = editions;
}
@Override
public PaperToken getToken(String tokenName) {
return getToken(tokenName, CardEdition.UNKNOWN.getName());
}
@Override
public PaperToken getToken(String tokenName, String edition) {
try {
PaperToken pt = new PaperToken(rulesByName.get(tokenName), editions.get(edition));
// TODO Cache the token after it's referenced
return pt;
} catch(Exception e) {
return null;
}
}
@Override
public PaperToken getToken(String tokenName, String edition, int artIndex) {
return null;
}
@Override
public PaperToken getTokenFromEdition(String tokenName, CardDb.SetPreference fromSet) {
return null;
}
@Override
public PaperToken getTokenFromEdition(String tokenName, Date printedBefore, CardDb.SetPreference fromSet) {
return null;
}
@Override
public PaperToken getTokenFromEdition(String tokenName, Date printedBefore, CardDb.SetPreference fromSet, int artIndex) {
return null;
}
@Override
public PaperToken getFoiled(PaperToken cpi) {
return null;
}
@Override
public int getPrintCount(String cardName, String edition) {
return 0;
}
@Override
public int getMaxPrintCount(String cardName) {
return 0;
}
@Override
public int getArtCount(String cardName, String edition) {
return 0;
}
@Override
public Collection<PaperToken> getUniqueTokens() {
return null;
}
@Override
public List<PaperToken> getAllTokens() {
return null;
}
@Override
public List<PaperToken> getAllTokens(String tokenName) {
return null;
}
@Override
public List<PaperToken> getAllTokens(Predicate<PaperToken> predicate) {
return null;
}
@Override
public Predicate<? super PaperToken> wasPrintedInSets(List<String> allowedSetCodes) {
return null;
}
@Override
public Iterator<PaperToken> iterator() {
return null;
}
}