mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
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:
@@ -43,7 +43,7 @@ import java.util.zip.ZipFile;
|
|||||||
|
|
||||||
import forge.util.BuildInfo;
|
import forge.util.BuildInfo;
|
||||||
import org.apache.commons.lang3.time.StopWatch;
|
import org.apache.commons.lang3.time.StopWatch;
|
||||||
|
import com.google.common.io.Files;
|
||||||
import forge.card.CardRules;
|
import forge.card.CardRules;
|
||||||
import forge.util.FileUtil;
|
import forge.util.FileUtil;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
@@ -71,6 +71,7 @@ public class CardStorageReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final String CARD_FILE_DOT_EXTENSION = ".txt";
|
private static final String CARD_FILE_DOT_EXTENSION = ".txt";
|
||||||
|
private static final String UPCOMING = "upcoming";
|
||||||
|
|
||||||
/** Default charset when loading from files. */
|
/** Default charset when loading from files. */
|
||||||
public static final String DEFAULT_CHARSET_NAME = "UTF-8";
|
public static final String DEFAULT_CHARSET_NAME = "UTF-8";
|
||||||
@@ -80,6 +81,7 @@ public class CardStorageReader {
|
|||||||
|
|
||||||
private final ProgressObserver progressObserver;
|
private final ProgressObserver progressObserver;
|
||||||
|
|
||||||
|
private final boolean loadingTokens;
|
||||||
private transient File cardsfolder;
|
private transient File cardsfolder;
|
||||||
|
|
||||||
private transient ZipFile zip;
|
private transient ZipFile zip;
|
||||||
@@ -91,6 +93,9 @@ public class CardStorageReader {
|
|||||||
public CardStorageReader(final String cardDataDir, final CardStorageReader.ProgressObserver progressObserver, boolean loadCardsLazily) {
|
public CardStorageReader(final String cardDataDir, final CardStorageReader.ProgressObserver progressObserver, boolean loadCardsLazily) {
|
||||||
this.progressObserver = progressObserver != null ? progressObserver : CardStorageReader.ProgressObserver.emptyObserver;
|
this.progressObserver = progressObserver != null ? progressObserver : CardStorageReader.ProgressObserver.emptyObserver;
|
||||||
this.cardsfolder = new File(cardDataDir);
|
this.cardsfolder = new File(cardDataDir);
|
||||||
|
|
||||||
|
this.loadingTokens = cardDataDir.contains("token");
|
||||||
|
|
||||||
this.loadCardsLazily = loadCardsLazily;
|
this.loadCardsLazily = loadCardsLazily;
|
||||||
|
|
||||||
// These read data for lightweight classes.
|
// These read data for lightweight classes.
|
||||||
@@ -240,6 +245,9 @@ public class CardStorageReader {
|
|||||||
final Set<CardRules> result = new TreeSet<>(new Comparator<CardRules>() {
|
final Set<CardRules> result = new TreeSet<>(new Comparator<CardRules>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(final CardRules o1, final CardRules o2) {
|
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());
|
return String.CASE_INSENSITIVE_ORDER.compare(o1.getName(), o2.getName());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -252,7 +260,7 @@ public class CardStorageReader {
|
|||||||
if (!allFiles.isEmpty()) {
|
if (!allFiles.isEmpty()) {
|
||||||
int fileParts = zip == null ? NUMBER_OF_PARTS : 1 + NUMBER_OF_PARTS / 3;
|
int fileParts = zip == null ? NUMBER_OF_PARTS : 1 + NUMBER_OF_PARTS / 3;
|
||||||
if (allFiles.size() < fileParts * 100) {
|
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 CountDownLatch cdlFiles = new CountDownLatch(fileParts);
|
||||||
final List<Callable<List<CardRules>>> taskFiles = makeTaskListForFiles(allFiles, cdlFiles);
|
final List<Callable<List<CardRules>>> taskFiles = makeTaskListForFiles(allFiles, cdlFiles);
|
||||||
@@ -377,7 +385,7 @@ public class CardStorageReader {
|
|||||||
continue;
|
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
|
// If upcoming folder exits, only load these cards on development builds
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -402,7 +410,7 @@ public class CardStorageReader {
|
|||||||
fileInputStream = new FileInputStream(file);
|
fileInputStream = new FileInputStream(file);
|
||||||
reader.reset();
|
reader.reset();
|
||||||
final List<String> lines = readScript(fileInputStream);
|
final List<String> lines = readScript(fileInputStream);
|
||||||
return reader.readCard(lines);
|
return reader.readCard(lines, Files.getNameWithoutExtension(file.getName()));
|
||||||
} catch (final FileNotFoundException ex) {
|
} catch (final FileNotFoundException ex) {
|
||||||
throw new RuntimeException("CardReader : run error -- file not found: " + file.getPath(), ex);
|
throw new RuntimeException("CardReader : run error -- file not found: " + file.getPath(), ex);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -430,7 +438,7 @@ public class CardStorageReader {
|
|||||||
zipInputStream = this.zip.getInputStream(entry);
|
zipInputStream = this.zip.getInputStream(entry);
|
||||||
rulesReader.reset();
|
rulesReader.reset();
|
||||||
|
|
||||||
return rulesReader.readCard(readScript(zipInputStream));
|
return rulesReader.readCard(readScript(zipInputStream), Files.getNameWithoutExtension(entry.getName()));
|
||||||
} catch (final IOException exn) {
|
} catch (final IOException exn) {
|
||||||
throw new RuntimeException(exn);
|
throw new RuntimeException(exn);
|
||||||
// PM
|
// PM
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import forge.item.BoosterBox;
|
|||||||
import forge.item.FatPack;
|
import forge.item.FatPack;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.item.SealedProduct;
|
import forge.item.SealedProduct;
|
||||||
|
import forge.token.TokenDb;
|
||||||
import forge.util.storage.IStorage;
|
import forge.util.storage.IStorage;
|
||||||
import forge.util.storage.StorageBase;
|
import forge.util.storage.StorageBase;
|
||||||
|
|
||||||
@@ -22,10 +23,13 @@ import java.util.*;
|
|||||||
* @author Max
|
* @author Max
|
||||||
*/
|
*/
|
||||||
public class StaticData {
|
public class StaticData {
|
||||||
private final CardStorageReader reader;
|
private final CardStorageReader cardReader;
|
||||||
|
private final CardStorageReader tokenReader;
|
||||||
|
|
||||||
private final String blockDataFolder;
|
private final String blockDataFolder;
|
||||||
private final CardDb commonCards;
|
private final CardDb commonCards;
|
||||||
private final CardDb variantCards;
|
private final CardDb variantCards;
|
||||||
|
private final TokenDb allTokens;
|
||||||
private final CardEdition.Collection editions;
|
private final CardEdition.Collection editions;
|
||||||
|
|
||||||
// Loaded lazily:
|
// Loaded lazily:
|
||||||
@@ -38,16 +42,22 @@ public class StaticData {
|
|||||||
|
|
||||||
private static StaticData lastInstance = null;
|
private static StaticData lastInstance = null;
|
||||||
|
|
||||||
public StaticData(CardStorageReader reader, String editionFolder, String blockDataFolder) {
|
public StaticData(CardStorageReader cardReader, String editionFolder, String blockDataFolder) {
|
||||||
this.reader = reader;
|
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.editions = new CardEdition.Collection(new CardEdition.Reader(new File(editionFolder)));
|
||||||
this.blockDataFolder = blockDataFolder;
|
this.blockDataFolder = blockDataFolder;
|
||||||
lastInstance = this;
|
lastInstance = this;
|
||||||
|
|
||||||
|
{
|
||||||
final Map<String, CardRules> regularCards = 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);
|
final Map<String, CardRules> variantsCards = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
for (CardRules card : reader.loadCards()) {
|
for (CardRules card : cardReader.loadCards()) {
|
||||||
if (null == card) continue;
|
if (null == card) continue;
|
||||||
|
|
||||||
final String cardName = card.getName();
|
final String cardName = card.getName();
|
||||||
@@ -66,6 +76,18 @@ public class StaticData {
|
|||||||
variantCards.initialize(false, false);
|
variantCards.initialize(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
final Map<String, CardRules> tokens = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
|
for (CardRules card : tokenReader.loadCards()) {
|
||||||
|
if (null == card) continue;
|
||||||
|
|
||||||
|
tokens.put(card.getNormalizedName(), card);
|
||||||
|
}
|
||||||
|
allTokens = new TokenDb(tokens, editions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static StaticData instance() {
|
public static StaticData instance() {
|
||||||
return lastInstance;
|
return lastInstance;
|
||||||
}
|
}
|
||||||
@@ -91,7 +113,7 @@ public class StaticData {
|
|||||||
PaperCard card = commonCards.getCard(cardName, setCode, artIndex);
|
PaperCard card = commonCards.getCard(cardName, setCode, artIndex);
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
attemptToLoadCard(cardName, setCode);
|
attemptToLoadCard(cardName, setCode);
|
||||||
commonCards.getCard(cardName, setCode, artIndex);
|
card = commonCards.getCard(cardName, setCode, artIndex);
|
||||||
}
|
}
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
card = commonCards.getCard(cardName, setCode, -1);
|
card = commonCards.getCard(cardName, setCode, -1);
|
||||||
@@ -105,7 +127,7 @@ public class StaticData {
|
|||||||
public void attemptToLoadCard(String encodedCardName, String setCode) {
|
public void attemptToLoadCard(String encodedCardName, String setCode) {
|
||||||
CardDb.CardRequest r = CardRequest.fromString(encodedCardName);
|
CardDb.CardRequest r = CardRequest.fromString(encodedCardName);
|
||||||
String cardName = r.cardName;
|
String cardName = r.cardName;
|
||||||
CardRules rules = reader.attemptToLoadCard(cardName, setCode);
|
CardRules rules = cardReader.attemptToLoadCard(cardName, setCode);
|
||||||
if (rules != null) {
|
if (rules != null) {
|
||||||
if (rules.isVariant()) {
|
if (rules.isVariant()) {
|
||||||
variantCards.loadCard(cardName, rules);
|
variantCards.loadCard(cardName, rules);
|
||||||
@@ -162,6 +184,8 @@ public class StaticData {
|
|||||||
return variantCards;
|
return variantCards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TokenDb getAllTokens() { return allTokens; }
|
||||||
|
|
||||||
public PaperCard getCardByEditionDate(PaperCard card, Date editionDate) {
|
public PaperCard getCardByEditionDate(PaperCard card, Date editionDate) {
|
||||||
|
|
||||||
PaperCard c = this.getCommonCards().getCardFromEdition(card.getName(), editionDate, CardDb.SetPreference.LatestCoreExp, card.getArtIndex());
|
PaperCard c = this.getCommonCards().getCardFromEdition(card.getName(), editionDate, CardDb.SetPreference.LatestCoreExp, card.getArtIndex());
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
|
|
||||||
// NO GETTERS/SETTERS HERE!
|
// NO GETTERS/SETTERS HERE!
|
||||||
public static class CardRequest {
|
public static class CardRequest {
|
||||||
|
// TODO Move Request to its own class
|
||||||
public String cardName;
|
public String cardName;
|
||||||
public String edition;
|
public String edition;
|
||||||
public int artIndex;
|
public int artIndex;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public enum CardRarity {
|
|||||||
Rare("R", "Rare"),
|
Rare("R", "Rare"),
|
||||||
MythicRare("M", "Mythic Rare"),
|
MythicRare("M", "Mythic Rare"),
|
||||||
Special("S", "Special"), // Timeshifted
|
Special("S", "Special"), // Timeshifted
|
||||||
|
None("N", "None"), // Tokens
|
||||||
Unknown("?", "Unknown"); // In development
|
Unknown("?", "Unknown"); // In development
|
||||||
|
|
||||||
public static final CardRarity[] FILTER_OPTIONS = new CardRarity[] {
|
public static final CardRarity[] FILTER_OPTIONS = new CardRarity[] {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import java.util.StringTokenizer;
|
|||||||
* @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $
|
* @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
*/
|
*/
|
||||||
public final class CardRules implements ICardCharacteristics {
|
public final class CardRules implements ICardCharacteristics {
|
||||||
|
private String normalizedName;
|
||||||
private CardSplitType splitType;
|
private CardSplitType splitType;
|
||||||
private ICardFace mainPart;
|
private ICardFace mainPart;
|
||||||
private ICardFace otherPart;
|
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() {
|
public CardAiHints getAiHints() {
|
||||||
return aiHints;
|
return aiHints;
|
||||||
}
|
}
|
||||||
@@ -207,12 +211,6 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
return meldWith;
|
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.
|
// vanguard card fields, they don't use sides.
|
||||||
private int deltaHand;
|
private int deltaHand;
|
||||||
private int deltaLife;
|
private int deltaLife;
|
||||||
@@ -260,6 +258,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
private CardSplitType altMode = CardSplitType.None;
|
private CardSplitType altMode = CardSplitType.None;
|
||||||
private String meldWith = "";
|
private String meldWith = "";
|
||||||
private String handLife = null;
|
private String handLife = null;
|
||||||
|
private String normalizedName = "";
|
||||||
|
|
||||||
// fields to build CardAiHints
|
// fields to build CardAiHints
|
||||||
private boolean removedFromAIDecks = false;
|
private boolean removedFromAIDecks = false;
|
||||||
@@ -287,6 +286,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
this.hints = null;
|
this.hints = null;
|
||||||
this.has = null;
|
this.has = null;
|
||||||
this.meldWith = "";
|
this.meldWith = "";
|
||||||
|
this.normalizedName = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -299,6 +299,8 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
faces[0].assignMissingFields();
|
faces[0].assignMissingFields();
|
||||||
if (null != faces[1]) faces[1].assignMissingFields();
|
if (null != faces[1]) faces[1].assignMissingFields();
|
||||||
final CardRules result = new CardRules(faces, altMode, cah);
|
final CardRules result = new CardRules(faces, altMode, cah);
|
||||||
|
|
||||||
|
result.setNormalizedName(this.normalizedName);
|
||||||
result.meldWith = this.meldWith;
|
result.meldWith = this.meldWith;
|
||||||
result.setDlUrls(pictureUrl);
|
result.setDlUrls(pictureUrl);
|
||||||
if (StringUtils.isNotBlank(handLife))
|
if (StringUtils.isNotBlank(handLife))
|
||||||
@@ -306,7 +308,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final CardRules readCard(final Iterable<String> script) {
|
public final CardRules readCard(final Iterable<String> script, String filename) {
|
||||||
this.reset();
|
this.reset();
|
||||||
for (String line : script) {
|
for (String line : script) {
|
||||||
if (line.isEmpty() || line.charAt(0) == '#') {
|
if (line.isEmpty() || line.charAt(0) == '#') {
|
||||||
@@ -314,9 +316,14 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
}
|
}
|
||||||
this.parseLine(line);
|
this.parseLine(line);
|
||||||
}
|
}
|
||||||
|
this.normalizedName = filename;
|
||||||
return this.getCard();
|
return this.getCard();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final CardRules readCard(final Iterable<String> script) {
|
||||||
|
return readCard(script, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the line.
|
* Parses the line.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import forge.ImageKeys;
|
|||||||
import forge.card.CardEdition;
|
import forge.card.CardEdition;
|
||||||
import forge.card.CardRarity;
|
import forge.card.CardRarity;
|
||||||
import forge.card.CardRules;
|
import forge.card.CardRules;
|
||||||
|
import forge.card.ColorSet;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
|
||||||
public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
||||||
private String name;
|
private String name;
|
||||||
private CardEdition edition;
|
private CardEdition edition;
|
||||||
@@ -29,21 +31,91 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
|||||||
return out.toString().toLowerCase(Locale.ENGLISH);
|
return out.toString().toLowerCase(Locale.ENGLISH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String makeTokenFileName(String colors, int power, int toughness, String name) {
|
public static String makeTokenFileName(String colors, String power, String toughness, String types) {
|
||||||
return makeTokenFileName(colors, String.valueOf(power), String.valueOf(toughness), name);
|
return makeTokenFileName(null, colors, power, toughness, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String makeTokenFileName(String colors, String power, String toughness, String name) {
|
public static String makeTokenFileName(String name, String colors, String power, String toughness, String types) {
|
||||||
StringBuilder fileName = new StringBuilder();
|
ArrayList<String> build = new ArrayList<>();
|
||||||
fileName.append(colors).append('_').append(power).append('_').append(toughness).append('_').append(name);
|
if (name != null) {
|
||||||
return makeTokenFileName(fileName.toString());
|
build.add(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PaperToken(final CardRules c, CardEdition edition0, final String imageFileName) {
|
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 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.card = c;
|
||||||
this.name = c.getName();
|
this.name = c.getName();
|
||||||
this.edition = edition0;
|
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; }
|
@Override public String getName() { return name; }
|
||||||
@@ -54,7 +126,7 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
|||||||
@Override public boolean isFoil() { return false; }
|
@Override public boolean isFoil() { return false; }
|
||||||
@Override public CardRules getRules() { return card; }
|
@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
|
// Unfortunately this is a property of token, cannot move it outside of class
|
||||||
public String getImageFilename() { return imageFileName; }
|
public String getImageFilename() { return imageFileName; }
|
||||||
|
|||||||
32
forge-core/src/main/java/forge/token/ITokenDatabase.java
Normal file
32
forge-core/src/main/java/forge/token/ITokenDatabase.java
Normal 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);
|
||||||
|
}
|
||||||
121
forge-core/src/main/java/forge/token/TokenDb.java
Normal file
121
forge-core/src/main/java/forge/token/TokenDb.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,10 +18,13 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import forge.StaticData;
|
||||||
|
import forge.card.MagicColor;
|
||||||
import forge.game.card.token.TokenInfo;
|
import forge.game.card.token.TokenInfo;
|
||||||
import forge.util.TextUtil;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
@@ -69,7 +72,23 @@ public class TokenEffect extends SpellAbilityEffect {
|
|||||||
private String[] tokenKeywords;
|
private String[] tokenKeywords;
|
||||||
private String[] tokenHiddenKeywords;
|
private String[] tokenHiddenKeywords;
|
||||||
|
|
||||||
private void readParameters(final SpellAbility mapParams) {
|
private void readMetaParams(final SpellAbility mapParams) {
|
||||||
|
this.tokenTapped = mapParams.getParamOrDefault("TokenTapped", "False").equalsIgnoreCase("True");
|
||||||
|
this.tokenAttacking = mapParams.getParamOrDefault("TokenAttacking", "False").equalsIgnoreCase("True");
|
||||||
|
this.tokenBlocking = mapParams.getParam("TokenBlocking");
|
||||||
|
|
||||||
|
this.tokenAmount = mapParams.getParamOrDefault("TokenAmount", "1");
|
||||||
|
this.tokenOwner = mapParams.getParamOrDefault("TokenOwner", "You");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readParameters(final SpellAbility mapParams, Card prototype) {
|
||||||
|
readMetaParams(mapParams);
|
||||||
|
if (prototype == null) {
|
||||||
|
readTokenParams(mapParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readTokenParams(final SpellAbility mapParams) {
|
||||||
String image;
|
String image;
|
||||||
String[] keywords;
|
String[] keywords;
|
||||||
|
|
||||||
@@ -99,9 +118,6 @@ public class TokenEffect extends SpellAbilityEffect {
|
|||||||
this.tokenAltImages = null;
|
this.tokenAltImages = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tokenTapped = mapParams.hasParam("TokenTapped") && mapParams.getParam("TokenTapped").equals("True");
|
|
||||||
this.tokenAttacking = mapParams.hasParam("TokenAttacking") && mapParams.getParam("TokenAttacking").equals("True");
|
|
||||||
|
|
||||||
if (mapParams.hasParam("TokenAbilities")) {
|
if (mapParams.hasParam("TokenAbilities")) {
|
||||||
this.tokenAbilities = mapParams.getParam("TokenAbilities").split(",");
|
this.tokenAbilities = mapParams.getParam("TokenAbilities").split(",");
|
||||||
} else {
|
} else {
|
||||||
@@ -122,8 +138,7 @@ public class TokenEffect extends SpellAbilityEffect {
|
|||||||
} else {
|
} else {
|
||||||
this.tokenStaticAbilities = null;
|
this.tokenStaticAbilities = null;
|
||||||
}
|
}
|
||||||
this.tokenBlocking = mapParams.getParam("TokenBlocking");
|
|
||||||
this.tokenAmount = mapParams.getParamOrDefault("TokenAmount", "1");
|
|
||||||
this.tokenPower = mapParams.getParam("TokenPower");
|
this.tokenPower = mapParams.getParam("TokenPower");
|
||||||
this.tokenToughness = mapParams.getParam("TokenToughness");
|
this.tokenToughness = mapParams.getParam("TokenToughness");
|
||||||
|
|
||||||
@@ -143,19 +158,19 @@ public class TokenEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
this.tokenKeywords = keywords;
|
this.tokenKeywords = keywords;
|
||||||
this.tokenImage = image;
|
this.tokenImage = image;
|
||||||
if (mapParams.hasParam("TokenOwner")) {
|
|
||||||
this.tokenOwner = mapParams.getParam("TokenOwner");
|
|
||||||
} else {
|
|
||||||
this.tokenOwner = "You";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
Card prototype = loadTokenPrototype(sa);
|
||||||
|
|
||||||
readParameters(sa);
|
if (prototype != null) {
|
||||||
|
return sa.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
readParameters(sa, prototype);
|
||||||
|
|
||||||
final int finalPower = AbilityUtils.calculateAmount(host, this.tokenPower, sa);
|
final int finalPower = AbilityUtils.calculateAmount(host, this.tokenPower, sa);
|
||||||
final int finalToughness = AbilityUtils.calculateAmount(host, this.tokenToughness, sa);
|
final int finalToughness = AbilityUtils.calculateAmount(host, this.tokenToughness, sa);
|
||||||
@@ -181,11 +196,25 @@ public class TokenEffect extends SpellAbilityEffect {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Card loadTokenPrototype(SpellAbility sa) {
|
||||||
|
String script = sa.getParamOrDefault("TokenScript", null);
|
||||||
|
|
||||||
|
String edition = sa.getHostCard().getPaperCard().getEdition();
|
||||||
|
|
||||||
|
PaperToken token = StaticData.instance().getAllTokens().getToken(script, edition);
|
||||||
|
if (token != null) {
|
||||||
|
tokenName = token.getName();
|
||||||
|
return Card.fromPaperCard(token, null, sa.getHostCard().getGame());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
final Game game = host.getGame();
|
||||||
final SpellAbility root = sa.getRootAbility();
|
final SpellAbility root = sa.getRootAbility();
|
||||||
readParameters(sa);
|
|
||||||
|
|
||||||
// Cause of the Token Effect, in general it should be this
|
// Cause of the Token Effect, in general it should be this
|
||||||
// but if its a Replacement Effect, it might be something else or null
|
// but if its a Replacement Effect, it might be something else or null
|
||||||
@@ -194,30 +223,26 @@ public class TokenEffect extends SpellAbilityEffect {
|
|||||||
cause = (SpellAbility)root.getReplacingObject("Cause");
|
cause = (SpellAbility)root.getReplacingObject("Cause");
|
||||||
}
|
}
|
||||||
|
|
||||||
String cost = "";
|
final boolean remember = sa.hasParam("RememberTokens");
|
||||||
|
final boolean imprint = sa.hasParam("ImprintTokens");
|
||||||
|
final List<Card> allTokens = Lists.newArrayList();
|
||||||
|
|
||||||
// Construct original colors
|
boolean combatChanged = false;
|
||||||
String originalColorDesc = "";
|
boolean inCombat = game.getPhaseHandler().inCombat();
|
||||||
for (final String col : this.tokenOriginalColors) {
|
|
||||||
if (col.equalsIgnoreCase("White")) {
|
Card prototype = loadTokenPrototype(sa);
|
||||||
originalColorDesc += "W ";
|
|
||||||
} else if (col.equalsIgnoreCase("Blue")) {
|
readParameters(sa, prototype);
|
||||||
originalColorDesc += "U ";
|
final int finalAmount = AbilityUtils.calculateAmount(host, this.tokenAmount, sa);
|
||||||
} else if (col.equalsIgnoreCase("Black")) {
|
|
||||||
originalColorDesc += "B ";
|
TokenInfo tokenInfo;
|
||||||
} else if (col.equalsIgnoreCase("Red")) {
|
|
||||||
originalColorDesc += "R ";
|
if (prototype == null) {
|
||||||
} else if (col.equalsIgnoreCase("Green")) {
|
String originalColorDesc = parseColorForImage();
|
||||||
originalColorDesc += "G ";
|
|
||||||
} else if (col.equalsIgnoreCase("Colorless")) {
|
|
||||||
originalColorDesc = "C";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<String> imageNames = Lists.newArrayListWithCapacity(1);
|
final List<String> imageNames = Lists.newArrayListWithCapacity(1);
|
||||||
if (this.tokenImage.equals("")) {
|
if (this.tokenImage.equals("")) {
|
||||||
imageNames.add(PaperToken.makeTokenFileName(TextUtil.fastReplace(originalColorDesc,
|
imageNames.add(PaperToken.makeTokenFileName(originalColorDesc, tokenPower, tokenToughness, tokenOriginalName));
|
||||||
" ", ""), tokenPower, tokenToughness, tokenOriginalName));
|
|
||||||
} else {
|
} else {
|
||||||
imageNames.add(0, this.tokenImage);
|
imageNames.add(0, this.tokenImage);
|
||||||
}
|
}
|
||||||
@@ -225,38 +250,10 @@ public class TokenEffect extends SpellAbilityEffect {
|
|||||||
imageNames.addAll(Arrays.asList(this.tokenAltImages));
|
imageNames.addAll(Arrays.asList(this.tokenAltImages));
|
||||||
}
|
}
|
||||||
|
|
||||||
final String[] substitutedColors = Arrays.copyOf(this.tokenColors, this.tokenColors.length);
|
String cost = determineTokenColor(host);
|
||||||
for (int i = 0; i < substitutedColors.length; i++) {
|
|
||||||
if (substitutedColors[i].equals("ChosenColor")) {
|
|
||||||
// this currently only supports 1 chosen color
|
|
||||||
substitutedColors[i] = host.getChosenColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String colorDesc = "";
|
|
||||||
for (final String col : substitutedColors) {
|
|
||||||
if (col.equalsIgnoreCase("White")) {
|
|
||||||
colorDesc += "W ";
|
|
||||||
} else if (col.equalsIgnoreCase("Blue")) {
|
|
||||||
colorDesc += "U ";
|
|
||||||
} else if (col.equalsIgnoreCase("Black")) {
|
|
||||||
colorDesc += "B ";
|
|
||||||
} else if (col.equalsIgnoreCase("Red")) {
|
|
||||||
colorDesc += "R ";
|
|
||||||
} else if (col.equalsIgnoreCase("Green")) {
|
|
||||||
colorDesc += "G ";
|
|
||||||
} else if (col.equalsIgnoreCase("Colorless")) {
|
|
||||||
colorDesc = "C";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (final char c : colorDesc.toCharArray()) {
|
|
||||||
cost += c + ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
cost = colorDesc.replace('C', '1').trim();
|
|
||||||
|
|
||||||
final int finalPower = AbilityUtils.calculateAmount(host, this.tokenPower, sa);
|
final int finalPower = AbilityUtils.calculateAmount(host, this.tokenPower, sa);
|
||||||
final int finalToughness = AbilityUtils.calculateAmount(host, this.tokenToughness, sa);
|
final int finalToughness = AbilityUtils.calculateAmount(host, this.tokenToughness, sa);
|
||||||
final int finalAmount = AbilityUtils.calculateAmount(host, this.tokenAmount, sa);
|
|
||||||
|
|
||||||
final String[] substitutedTypes = Arrays.copyOf(this.tokenTypes, this.tokenTypes.length);
|
final String[] substitutedTypes = Arrays.copyOf(this.tokenTypes, this.tokenTypes.length);
|
||||||
for (int i = 0; i < substitutedTypes.length; i++) {
|
for (int i = 0; i < substitutedTypes.length; i++) {
|
||||||
@@ -266,120 +263,44 @@ public class TokenEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
final String substitutedName = this.tokenName.equals("ChosenType") ? host.getChosenType() : this.tokenName;
|
final String substitutedName = this.tokenName.equals("ChosenType") ? host.getChosenType() : this.tokenName;
|
||||||
|
|
||||||
final boolean remember = sa.hasParam("RememberTokens");
|
|
||||||
final boolean imprint = sa.hasParam("ImprintTokens");
|
|
||||||
final List<Card> allTokens = Lists.newArrayList();
|
|
||||||
for (final Player controller : AbilityUtils.getDefinedPlayers(host, this.tokenOwner, sa)) {
|
|
||||||
final Game game = controller.getGame();
|
|
||||||
for (int i = 0; i < finalAmount; i++) {
|
|
||||||
final String imageName = imageNames.get(MyRandom.getRandom().nextInt(imageNames.size()));
|
final String imageName = imageNames.get(MyRandom.getRandom().nextInt(imageNames.size()));
|
||||||
final TokenInfo tokenInfo = new TokenInfo(substitutedName, imageName,
|
tokenInfo = new TokenInfo(substitutedName, imageName,
|
||||||
cost, substitutedTypes, this.tokenKeywords, finalPower, finalToughness);
|
cost, substitutedTypes, this.tokenKeywords, finalPower, finalToughness);
|
||||||
final List<Card> tokens = tokenInfo.makeTokenWithMultiplier(controller, cause != null);
|
} else {
|
||||||
|
tokenInfo = new TokenInfo(prototype);
|
||||||
// Grant rule changes
|
|
||||||
if (this.tokenHiddenKeywords != null) {
|
|
||||||
for (final String s : this.tokenHiddenKeywords) {
|
|
||||||
for (final Card c : tokens) {
|
|
||||||
c.addHiddenExtrinsicKeyword(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grant SVars
|
for (final Player controller : AbilityUtils.getDefinedPlayers(host, this.tokenOwner, sa)) {
|
||||||
if (this.tokenSVars != null) {
|
List<Card> tokens;
|
||||||
for (final String s : this.tokenSVars) {
|
|
||||||
String actualSVar = AbilityUtils.getSVar(root, s);
|
|
||||||
String name = s;
|
|
||||||
if (actualSVar.startsWith("SVar")) {
|
|
||||||
actualSVar = actualSVar.split("SVar:")[1];
|
|
||||||
name = actualSVar.split(":")[0];
|
|
||||||
actualSVar = actualSVar.split(":")[1];
|
|
||||||
}
|
|
||||||
for (final Card c : tokens) {
|
|
||||||
c.setSVar(name, actualSVar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grant abilities
|
if (prototype == null) {
|
||||||
if (this.tokenAbilities != null) {
|
tokens = tokenInfo.makeTokenWithMultiplier(controller, finalAmount, cause != null);
|
||||||
for (final String s : this.tokenAbilities) {
|
grantHiddenKeywords(tokens);
|
||||||
final String actualAbility = AbilityUtils.getSVar(root, s);
|
grantSvars(tokens, root);
|
||||||
for (final Card c : tokens) {
|
grantAbilities(tokens, root);
|
||||||
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c);
|
grantTriggers(tokens, root);
|
||||||
// Set intrinsic, so that effects like Clone will copy these.
|
grantStatics(tokens, root);
|
||||||
grantedAbility.setIntrinsic(true);
|
} else {
|
||||||
c.addSpellAbility(grantedAbility);
|
tokens = TokenInfo.makeTokensFromPrototype(prototype, controller, finalAmount, cause != null);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grant triggers
|
|
||||||
if (this.tokenTriggers != null) {
|
|
||||||
for (final String s : this.tokenTriggers) {
|
|
||||||
final String actualTrigger = AbilityUtils.getSVar(root, s);
|
|
||||||
for (final Card c : tokens) {
|
|
||||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, true);
|
|
||||||
final String ability = AbilityUtils.getSVar(root, parsedTrigger.getMapParams().get("Execute"));
|
|
||||||
parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(ability, c));
|
|
||||||
c.addTrigger(parsedTrigger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grant static abilities
|
|
||||||
if (this.tokenStaticAbilities != null) {
|
|
||||||
for (final String s : this.tokenStaticAbilities) {
|
|
||||||
final String actualAbility = AbilityUtils.getSVar(root, s);
|
|
||||||
for (final Card c : tokens) {
|
|
||||||
c.addStaticAbility(actualAbility);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Card tok : tokens) {
|
for (Card tok : tokens) {
|
||||||
if (this.tokenTapped) {
|
if (this.tokenTapped) {
|
||||||
tok.setTapped(true);
|
tok.setTapped(true);
|
||||||
}
|
}
|
||||||
game.getAction().moveToPlay(tok, sa);
|
// Should this be catching the Card that's returned?
|
||||||
}
|
Card c = game.getAction().moveToPlay(tok, sa);
|
||||||
game.fireEvent(new GameEventTokenCreated());
|
|
||||||
|
|
||||||
boolean combatChanged = false;
|
|
||||||
for (final Card c : tokens) {
|
|
||||||
if (sa.hasParam("AtEOTTrig")) {
|
if (sa.hasParam("AtEOTTrig")) {
|
||||||
addSelfTrigger(sa, sa.getParam("AtEOTTrig"), c);
|
addSelfTrigger(sa, sa.getParam("AtEOTTrig"), c);
|
||||||
}
|
}
|
||||||
// To show the Abilities and Trigger on the Token
|
|
||||||
|
if (inCombat) {
|
||||||
|
combatChanged = addTokenToCombat(game, c, controller, sa, host) || combatChanged;
|
||||||
|
}
|
||||||
|
|
||||||
c.updateStateForView();
|
c.updateStateForView();
|
||||||
if (this.tokenAttacking && game.getPhaseHandler().inCombat()) {
|
|
||||||
final Combat combat = game.getCombat();
|
|
||||||
|
|
||||||
// into battlefield attacking only should work if you are the attacking player
|
|
||||||
if (combat.getAttackingPlayer().equals(controller)) {
|
|
||||||
final FCollectionView<GameEntity> defs = combat.getDefenders();
|
|
||||||
final GameEntity defender = controller.getController().chooseSingleEntityForEffect(defs, sa, "Choose which defender to attack with " + c, false);
|
|
||||||
combat.addAttacker(c, defender);
|
|
||||||
combatChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.tokenBlocking != null && game.getPhaseHandler().inCombat()) {
|
|
||||||
final Combat combat = game.getPhaseHandler().getCombat();
|
|
||||||
final Card attacker = Iterables.getFirst(AbilityUtils.getDefinedCards(host, this.tokenBlocking, sa), null);
|
|
||||||
if (attacker != null) {
|
|
||||||
final boolean wasBlocked = combat.isBlocked(attacker);
|
|
||||||
combat.addBlocker(attacker, c);
|
|
||||||
combat.orderAttackersForDamageAssignment(c);
|
|
||||||
|
|
||||||
// Run triggers for new blocker and add it to damage assignment order
|
|
||||||
if (!wasBlocked) {
|
|
||||||
combat.setBlocked(attacker, true);
|
|
||||||
combat.addBlockerToDamageAssignmentOrder(attacker, c);
|
|
||||||
}
|
|
||||||
combatChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (remember) {
|
if (remember) {
|
||||||
game.getCardState(sa.getHostCard()).addRemembered(c);
|
game.getCardState(sa.getHostCard()).addRemembered(c);
|
||||||
}
|
}
|
||||||
@@ -396,19 +317,154 @@ public class TokenEffect extends SpellAbilityEffect {
|
|||||||
token.addRemembered(o);
|
token.addRemembered(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
allTokens.add(c);
|
||||||
}
|
}
|
||||||
if (tokenName.equals("Clue")) { // investigate trigger
|
if ("Clue".equals(tokenName)) { // investigate trigger
|
||||||
controller.addInvestigatedThisTurn();
|
controller.addInvestigatedThisTurn();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
game.fireEvent(new GameEventTokenCreated());
|
||||||
|
|
||||||
if (combatChanged) {
|
if (combatChanged) {
|
||||||
game.updateCombatForView();
|
game.updateCombatForView();
|
||||||
game.fireEvent(new GameEventCombatChanged());
|
game.fireEvent(new GameEventCombatChanged());
|
||||||
}
|
}
|
||||||
allTokens.addAll(tokens);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sa.hasParam("AtEOT")) {
|
if (sa.hasParam("AtEOT")) {
|
||||||
registerDelayedTrigger(sa, sa.getParam("AtEOT"), allTokens);
|
registerDelayedTrigger(sa, sa.getParam("AtEOT"), allTokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String parseColorForImage() {
|
||||||
|
String originalColorDesc = "";
|
||||||
|
for (final String col : this.tokenOriginalColors) {
|
||||||
|
originalColorDesc += MagicColor.toShortString(col);
|
||||||
|
if (originalColorDesc.equals("C")) {
|
||||||
|
return originalColorDesc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return originalColorDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String determineTokenColor(Card host) {
|
||||||
|
Set<String> colorSet = new HashSet<>();
|
||||||
|
final String[] substitutedColors = Arrays.copyOf(this.tokenColors, this.tokenColors.length);
|
||||||
|
for (int i = 0; i < substitutedColors.length; i++) {
|
||||||
|
if (substitutedColors[i].equals("ChosenColor")) {
|
||||||
|
// this currently only supports 1 chosen color
|
||||||
|
substitutedColors[i] = host.getChosenColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (final String col : substitutedColors) {
|
||||||
|
String str = MagicColor.toShortString(col);
|
||||||
|
if (str.equals("C")) {
|
||||||
|
return "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(str).append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void grantHiddenKeywords(List<Card> tokens) {
|
||||||
|
// Grant rule changes
|
||||||
|
if (this.tokenHiddenKeywords != null) {
|
||||||
|
for (final String s : this.tokenHiddenKeywords) {
|
||||||
|
for (final Card c : tokens) {
|
||||||
|
c.addHiddenExtrinsicKeyword(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void grantAbilities(List<Card> tokens, SpellAbility root) {
|
||||||
|
if (this.tokenAbilities != null) {
|
||||||
|
for (final String s : this.tokenAbilities) {
|
||||||
|
final String actualAbility = AbilityUtils.getSVar(root, s);
|
||||||
|
for (final Card c : tokens) {
|
||||||
|
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c);
|
||||||
|
// Set intrinsic, so that effects like Clone will copy these.
|
||||||
|
grantedAbility.setIntrinsic(true);
|
||||||
|
c.addSpellAbility(grantedAbility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void grantTriggers(List<Card> tokens, SpellAbility root) {
|
||||||
|
if (this.tokenTriggers != null) {
|
||||||
|
for (final String s : this.tokenTriggers) {
|
||||||
|
final String actualTrigger = AbilityUtils.getSVar(root, s);
|
||||||
|
for (final Card c : tokens) {
|
||||||
|
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, true);
|
||||||
|
final String ability = AbilityUtils.getSVar(root, parsedTrigger.getMapParams().get("Execute"));
|
||||||
|
parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(ability, c));
|
||||||
|
c.addTrigger(parsedTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void grantSvars(List<Card> tokens, SpellAbility root) {
|
||||||
|
if (this.tokenSVars != null) {
|
||||||
|
for (final String s : this.tokenSVars) {
|
||||||
|
String actualSVar = AbilityUtils.getSVar(root, s);
|
||||||
|
String name = s;
|
||||||
|
if (actualSVar.startsWith("SVar")) {
|
||||||
|
actualSVar = actualSVar.split("SVar:")[1];
|
||||||
|
name = actualSVar.split(":")[0];
|
||||||
|
actualSVar = actualSVar.split(":")[1];
|
||||||
|
}
|
||||||
|
for (final Card c : tokens) {
|
||||||
|
c.setSVar(name, actualSVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void grantStatics(List<Card> tokens, SpellAbility root) {
|
||||||
|
if (this.tokenStaticAbilities != null) {
|
||||||
|
for (final String s : this.tokenStaticAbilities) {
|
||||||
|
final String actualAbility = AbilityUtils.getSVar(root, s);
|
||||||
|
for (final Card c : tokens) {
|
||||||
|
c.addStaticAbility(actualAbility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean addTokenToCombat(Game game, Card c, Player controller, SpellAbility sa, Card host) {
|
||||||
|
boolean combatChanged = false;
|
||||||
|
if (this.tokenAttacking) {
|
||||||
|
final Combat combat = game.getCombat();
|
||||||
|
|
||||||
|
// into battlefield attacking only should work if you are the attacking player
|
||||||
|
if (combat.getAttackingPlayer().equals(controller)) {
|
||||||
|
final FCollectionView<GameEntity> defs = combat.getDefenders();
|
||||||
|
final GameEntity defender = controller.getController().chooseSingleEntityForEffect(defs, sa, "Choose which defender to attack with " + c, false);
|
||||||
|
combat.addAttacker(c, defender);
|
||||||
|
combatChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.tokenBlocking != null) {
|
||||||
|
final Combat combat = game.getPhaseHandler().getCombat();
|
||||||
|
final Card attacker = Iterables.getFirst(AbilityUtils.getDefinedCards(host, this.tokenBlocking, sa), null);
|
||||||
|
if (attacker != null) {
|
||||||
|
final boolean wasBlocked = combat.isBlocked(attacker);
|
||||||
|
combat.addBlocker(attacker, c);
|
||||||
|
combat.orderAttackersForDamageAssignment(c);
|
||||||
|
|
||||||
|
// Run triggers for new blocker and add it to damage assignment order
|
||||||
|
if (!wasBlocked) {
|
||||||
|
combat.setBlocked(attacker, true);
|
||||||
|
combat.addBlockerToDamageAssignmentOrder(attacker, c);
|
||||||
|
}
|
||||||
|
combatChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return combatChanged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5405,6 +5405,9 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
public static Card fromPaperCard(IPaperCard pc, Player owner) {
|
public static Card fromPaperCard(IPaperCard pc, Player owner) {
|
||||||
return CardFactory.getCard(pc, owner, owner == null ? null : owner.getGame());
|
return CardFactory.getCard(pc, owner, owner == null ? null : owner.getGame());
|
||||||
}
|
}
|
||||||
|
public static Card fromPaperCard(IPaperCard pc, Player owner, Game game) {
|
||||||
|
return CardFactory.getCard(pc, owner, game);
|
||||||
|
}
|
||||||
|
|
||||||
private static final Map<PaperCard, Card> cp2card = Maps.newHashMap();
|
private static final Map<PaperCard, Card> cp2card = Maps.newHashMap();
|
||||||
public static Card getCardForUi(IPaperCard pc) {
|
public static Card getCardForUi(IPaperCard pc) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import forge.ImageKeys;
|
|||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardFactory;
|
||||||
import forge.game.card.CardFactoryUtil;
|
import forge.game.card.CardFactoryUtil;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
@@ -133,10 +134,7 @@ public class TokenInfo {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Card> makeTokenWithMultiplier(final Player controller, final boolean applyMultiplier) {
|
static private int calculateMultiplier(final Game game, final Player controller, final boolean applyMultiplier) {
|
||||||
final List<Card> list = Lists.newArrayList();
|
|
||||||
final Game game = controller.getGame();
|
|
||||||
|
|
||||||
int multiplier = 1;
|
int multiplier = 1;
|
||||||
Player player = controller;
|
Player player = controller;
|
||||||
|
|
||||||
@@ -155,12 +153,38 @@ public class TokenInfo {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Card> makeTokenWithMultiplier(final Player controller, int amount, final boolean applyMultiplier) {
|
||||||
|
final List<Card> list = Lists.newArrayList();
|
||||||
|
final Game game = controller.getGame();
|
||||||
|
|
||||||
|
int multiplier = calculateMultiplier(game, controller, applyMultiplier);
|
||||||
|
|
||||||
|
for (int i = 0; i < multiplier * amount; i++) {
|
||||||
|
list.add(makeOneToken(controller));
|
||||||
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < multiplier; i++) {
|
static public List<Card> makeTokensFromPrototype(Card prototype, final Player controller, int amount, final boolean applyMultiplier) {
|
||||||
list.add(makeOneToken(player));
|
final List<Card> list = Lists.newArrayList();
|
||||||
|
final Game game = controller.getGame();
|
||||||
|
|
||||||
|
int multiplier = calculateMultiplier(game, controller, applyMultiplier);
|
||||||
|
long timestamp = game.getNextTimestamp();
|
||||||
|
prototype.setController(controller, timestamp);
|
||||||
|
for (int i = 0; i < multiplier * amount; i++) {
|
||||||
|
Card copy = CardFactory.copyCard(prototype, true);
|
||||||
|
copy.setTimestamp(timestamp);
|
||||||
|
copy.setOwner(controller);
|
||||||
|
copy.setToken(true);
|
||||||
|
list.add(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
forge-gui/res/tokenscripts/c_3_3_a_wurm_deathtouch.txt
Normal file
6
forge-gui/res/tokenscripts/c_3_3_a_wurm_deathtouch.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Name:Wurm
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Creature Wurm
|
||||||
|
PT:3/3
|
||||||
|
K:Deathtouch
|
||||||
|
Oracle:Deathtouch
|
||||||
6
forge-gui/res/tokenscripts/c_3_3_a_wurm_lifelink.txt
Normal file
6
forge-gui/res/tokenscripts/c_3_3_a_wurm_lifelink.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Name:Wurm
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Creature Wurm
|
||||||
|
PT:3/3
|
||||||
|
K:Lifelink
|
||||||
|
Oracle:Lifelink
|
||||||
6
forge-gui/res/tokenscripts/voja.txt
Normal file
6
forge-gui/res/tokenscripts/voja.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Name:Voja
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Legendary Creature Wolf
|
||||||
|
Colors:white,green
|
||||||
|
PT:2/2
|
||||||
|
Oracle:
|
||||||
@@ -145,7 +145,9 @@ public final class FModel {
|
|||||||
|
|
||||||
final CardStorageReader reader = new CardStorageReader(ForgeConstants.CARD_DATA_DIR, progressBarBridge,
|
final CardStorageReader reader = new CardStorageReader(ForgeConstants.CARD_DATA_DIR, progressBarBridge,
|
||||||
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
|
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
|
||||||
magicDb = new StaticData(reader, ForgeConstants.EDITIONS_DIR, ForgeConstants.BLOCK_DATA_DIR);
|
final CardStorageReader tokenReader = new CardStorageReader(ForgeConstants.TOKEN_DATA_DIR, progressBarBridge,
|
||||||
|
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
|
||||||
|
magicDb = new StaticData(reader, tokenReader, ForgeConstants.EDITIONS_DIR, ForgeConstants.BLOCK_DATA_DIR);
|
||||||
|
|
||||||
//create profile dirs if they don't already exist
|
//create profile dirs if they don't already exist
|
||||||
for (final String dname : ForgeConstants.PROFILE_DIRS) {
|
for (final String dname : ForgeConstants.PROFILE_DIRS) {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public final class ForgeConstants {
|
|||||||
public static final String DRAFT_RANKINGS_FILE = DRAFT_DIR + "rankings.txt";
|
public static final String DRAFT_RANKINGS_FILE = DRAFT_DIR + "rankings.txt";
|
||||||
public static final String SEALED_DIR = RES_DIR + "sealed" + PATH_SEPARATOR;
|
public static final String SEALED_DIR = RES_DIR + "sealed" + PATH_SEPARATOR;
|
||||||
public static final String CARD_DATA_DIR = RES_DIR + "cardsfolder" + PATH_SEPARATOR;
|
public static final String CARD_DATA_DIR = RES_DIR + "cardsfolder" + PATH_SEPARATOR;
|
||||||
|
public static final String TOKEN_DATA_DIR = RES_DIR + "tokenscripts" + PATH_SEPARATOR;
|
||||||
public static final String EDITIONS_DIR = RES_DIR + "editions" + PATH_SEPARATOR;
|
public static final String EDITIONS_DIR = RES_DIR + "editions" + PATH_SEPARATOR;
|
||||||
public static final String BLOCK_DATA_DIR = RES_DIR + "blockdata" + PATH_SEPARATOR;
|
public static final String BLOCK_DATA_DIR = RES_DIR + "blockdata" + PATH_SEPARATOR;
|
||||||
public static final String DECK_CUBE_DIR = RES_DIR + "cube" + PATH_SEPARATOR;
|
public static final String DECK_CUBE_DIR = RES_DIR + "cube" + PATH_SEPARATOR;
|
||||||
|
|||||||
Reference in New Issue
Block a user