mirror of
https://github.com/correl/mage.git
synced 2024-11-14 11:09:31 +00:00
Tokens rework:
- added reminder / helper tokens support (example: Copy, Morph, Day // Night, related to #10139); - added verify checks for reminder tokens; - added images download for reminder tokens;
This commit is contained in:
parent
0e1e6a0f21
commit
f86cf176d7
11 changed files with 242 additions and 40 deletions
|
@ -1,5 +1,7 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
import mage.cards.repository.TokenRepository;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -13,11 +15,13 @@ public class ScryfallImageSupportTokens {
|
|||
private static final Map<String, String> supportedCards = new HashMap<String, String>() {
|
||||
{
|
||||
// xmage token -> direct or api link:
|
||||
//
|
||||
// examples:
|
||||
// direct example: https://img.scryfall.com/cards/large/en/trix/6.jpg
|
||||
// direct example: https://cards.scryfall.io/large/back/d/c/dc26e13b-7a0f-4e7f-8593-4f22234f4517.jpg
|
||||
// api example: https://api.scryfall.com/cards/trix/6/en?format=image
|
||||
// api example: https://api.scryfall.com/cards/trix/6?format=image
|
||||
// api format is primary
|
||||
// api example: https://api.scryfall.com/cards/tvow/21/en?format=image&face=back
|
||||
// api format is primary (direct images links can be changed by scryfall)
|
||||
//
|
||||
// code form for one token:
|
||||
// set/token_name
|
||||
|
@ -25,6 +29,14 @@ public class ScryfallImageSupportTokens {
|
|||
// code form for same name tokens (alternative images):
|
||||
// set/token_name/1
|
||||
// set/token_name/2
|
||||
//
|
||||
// double faced cards:
|
||||
// front face image: format=image&face=front
|
||||
// back face image: format=image&face=back
|
||||
|
||||
// XMAGE
|
||||
// additional tokens for reminder/helper images
|
||||
putAll(TokenRepository.instance.prepareScryfallDownloadList());
|
||||
|
||||
// RIX
|
||||
put("RIX/City's Blessing", "https://api.scryfall.com/cards/trix/6/en?format=image"); // TODO: missing from tokens data
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
package org.mage.test.serverside;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.repository.TokenRepository;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.game.permanent.token.TokenImpl;
|
||||
import mage.game.permanent.token.custom.CreatureToken;
|
||||
import mage.util.CardUtil;
|
||||
import mage.view.CardView;
|
||||
import mage.view.GameView;
|
||||
|
@ -31,6 +39,22 @@ public class TokenImagesTest extends CardTestPlayerBase {
|
|||
|
||||
private static final Pattern checkPattern = Pattern.compile("(\\w+)([<=>])(\\d+)"); // code=12, code>0
|
||||
|
||||
static class TestToken extends TokenImpl {
|
||||
|
||||
TestToken(String name, String description) {
|
||||
super(name, description);
|
||||
}
|
||||
|
||||
TestToken(final TestToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token copy() {
|
||||
return new TestToken(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareCards_MemorialToGlory(String... cardsList) {
|
||||
// {3}{W}, {T}, Sacrifice Memorial to Glory: Create two 1/1 white Soldier creature tokens.
|
||||
prepareCards_Inner(Zone.BATTLEFIELD, "Memorial to Glory", 4, cardsList);
|
||||
|
@ -315,8 +339,52 @@ public class TokenImagesTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore // TODO: implement
|
||||
@Ignore
|
||||
// TODO: implement auto-generate creature token images from public tokens (by name, type, color, PT, abilities)
|
||||
public void test_CreatureToken_MustGetDefaultImage() {
|
||||
Ability ability = new SimpleActivatedAbility(
|
||||
Zone.ALL,
|
||||
new CreateTokenEffect(new CreatureToken(2, 2), 10),
|
||||
new ManaCostsImpl<>("")
|
||||
);
|
||||
addCustomCardWithAbility("test", playerA, ability);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "create ten");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, 1 + 10); // 1 test card + 10 tokens
|
||||
|
||||
assert_Inner("test", 0, 0, 1,
|
||||
"", 10, false, "XXX=10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_UnknownToken_MustGetDefaultImage() {
|
||||
// all unknown tokens must put in XMAGE set
|
||||
String xmageSetCode = TokenRepository.XMAGE_TOKENS_SET_CODE;
|
||||
TestToken token = new TestToken("Unknown Token", "xxx");
|
||||
Ability ability = new SimpleActivatedAbility(
|
||||
Zone.ALL,
|
||||
new CreateTokenEffect(token, 10),
|
||||
new ManaCostsImpl<>("")
|
||||
);
|
||||
addCustomCardWithAbility("test", playerA, ability);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "create ten");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, 1 + 10); // 1 test card + 10 tokens
|
||||
|
||||
assert_Inner("test", 0, 0, 1,
|
||||
"Unknown Token", 10, false, xmageSetCode + "=10");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1203,7 +1203,7 @@ public class VerifyCardDataTest {
|
|||
Collection<String> errorsList = new ArrayList<>();
|
||||
Collection<String> warningsList = new ArrayList<>();
|
||||
|
||||
// all tokens must be stores in card-pictures-tok.txt (if not then viewer and image downloader are missing token images)
|
||||
// all tokens must be stores in tokens-database.txt (if not then viewer and image downloader are missing token images)
|
||||
// https://github.com/ronmamo/reflections
|
||||
Reflections reflections = new Reflections("mage.");
|
||||
Set<Class<? extends TokenImpl>> tokenClassesList = reflections.getSubTypesOf(TokenImpl.class);
|
||||
|
@ -1220,6 +1220,7 @@ public class VerifyCardDataTest {
|
|||
}
|
||||
// xmage sets
|
||||
Set<String> allSetCodes = Sets.getInstance().values().stream().map(ExpansionSet::getCode).collect(Collectors.toSet());
|
||||
allSetCodes.add(TokenRepository.XMAGE_TOKENS_SET_CODE); // reminder tokens
|
||||
|
||||
|
||||
// tok file's data
|
||||
|
@ -1290,14 +1291,14 @@ public class VerifyCardDataTest {
|
|||
} else if (tokDataNamesIndex.getOrDefault(token.getName().replace(" Token", ""), "").isEmpty()) {
|
||||
// how-to fix: public token must be downloadable, so tok-data must contain miss set
|
||||
// (also don't forget to add new set to scryfall download)
|
||||
errorsList.add("Error: can't find data in card-pictures-tok.txt for token: " + tokenClass.getName() + " -> " + token.getName());
|
||||
errorsList.add("Error: can't find data in tokens-database.txt for token: " + tokenClass.getName() + " -> " + token.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK: wrong set codes in tok-data
|
||||
tokDataTokensBySetIndex.forEach((setCode, setTokens) -> {
|
||||
if (!allSetCodes.contains(setCode)) {
|
||||
errorsList.add("error, card-pictures-tok.txt contains unknown set code: "
|
||||
errorsList.add("error, tokens-database.txt contains unknown set code: "
|
||||
+ setCode + " - " + setTokens.stream().map(TokenInfo::getName).collect(Collectors.joining(", ")));
|
||||
}
|
||||
});
|
||||
|
@ -1353,6 +1354,10 @@ public class VerifyCardDataTest {
|
|||
});
|
||||
// tok data have tokens, but cards from set are miss
|
||||
tokDataTokensBySetIndex.forEach((setCode, setTokens) -> {
|
||||
if (setCode.equals(TokenRepository.XMAGE_TOKENS_SET_CODE)) {
|
||||
// ignore reminder tokens
|
||||
return;
|
||||
}
|
||||
if (!setsWithTokens.containsKey(setCode)) {
|
||||
// Possible reasons:
|
||||
// - outdated set code in tokens database (must be fixed by new set code, another verify check it)
|
||||
|
@ -1364,13 +1369,37 @@ public class VerifyCardDataTest {
|
|||
|
||||
// CHECK: token and class names must be same in all sets
|
||||
TokenRepository.instance.getAllByClassName().forEach((className, list) -> {
|
||||
Set<String> names = list.stream().map(TokenInfo::getName).collect(Collectors.toSet());
|
||||
// ignore reminder tokens
|
||||
Set<String> names = list.stream()
|
||||
.filter(token -> !token.getTokenType().equals(TokenType.XMAGE))
|
||||
.map(TokenInfo::getName)
|
||||
.collect(Collectors.toSet());
|
||||
if (names.size() > 1) {
|
||||
errorsList.add("error, card-pictures-tok.txt contains different names for same class: "
|
||||
errorsList.add("error, tokens-database.txt contains different names for same class: "
|
||||
+ className + " - " + String.join(", ", names));
|
||||
}
|
||||
});
|
||||
|
||||
Set<String> usedNames = new HashSet<>();
|
||||
TokenRepository.instance.getByType(TokenType.XMAGE).forEach(token -> {
|
||||
// CHECK: xmage's tokens must be unique
|
||||
// how-to fix: edit TokenRepository->loadXmageTokens
|
||||
String needName = String.format("%s.%d", token.getName(), token.getImageNumber());
|
||||
if (usedNames.contains(needName)) {
|
||||
errorsList.add("error, xmage token's name and image number must be unique: "
|
||||
+ token.getName() + " - " + token.getImageNumber());
|
||||
} else {
|
||||
usedNames.add(needName);
|
||||
}
|
||||
|
||||
// CHECK: xmage's tokens must be downloadable
|
||||
// how-to fix: edit TokenRepository->loadXmageTokens
|
||||
if (token.getDownloadUrl().isEmpty()) {
|
||||
errorsList.add("error, xmage token's must have download url: "
|
||||
+ token.getName() + " - " + token.getImageNumber());
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: all sets must have full tokens data in tok file (token in every set)
|
||||
// 1. Download scryfall tokens list: https://api.scryfall.com/cards/search?q=t:token
|
||||
// 2. Proccess each token with all prints: read "prints_search_uri" field from token data and go to link like
|
||||
|
|
|
@ -1273,12 +1273,12 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
|
||||
public synchronized void addEffect(ContinuousEffect effect, Ability source) {
|
||||
if (effect == null) {
|
||||
logger.error("Effect is null: " + source.toString());
|
||||
return;
|
||||
} else if (source == null) {
|
||||
logger.warn("Adding effect without ability : " + effect);
|
||||
if (effect == null || source == null) {
|
||||
// addEffect(effect, source) need a non-null source
|
||||
throw new IllegalArgumentException("Wrong code usage. Effect and source can't be null here: "
|
||||
+ source + "; " + effect);
|
||||
}
|
||||
|
||||
switch (effect.getEffectType()) {
|
||||
case REPLACEMENT:
|
||||
case REDIRECTION:
|
||||
|
@ -1313,9 +1313,12 @@ public class ContinuousEffects implements Serializable {
|
|||
case CONTINUOUS_RULE_MODIFICATION:
|
||||
continuousRuleModifyingEffects.addEffect((ContinuousRuleModifyingEffect) effect, source);
|
||||
break;
|
||||
default:
|
||||
case CONTINUOUS:
|
||||
case ONESHOT:
|
||||
layeredEffects.addEffect(effect, source);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown effect type: " + effect.getEffectType());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,14 +7,16 @@ package mage.cards.repository;
|
|||
*/
|
||||
public class TokenInfo {
|
||||
|
||||
private TokenType tokenType;
|
||||
private String name;
|
||||
private String setCode;
|
||||
private Integer imageNumber = 1; // if one set contains diff images with same name
|
||||
private final TokenType tokenType;
|
||||
private final String name;
|
||||
private final String setCode;
|
||||
private final Integer imageNumber; // if one set contains diff images with same name
|
||||
|
||||
private String classFileName;
|
||||
private final String classFileName;
|
||||
|
||||
private String imageFileName;
|
||||
private final String imageFileName;
|
||||
|
||||
private String downloadUrl = "";
|
||||
|
||||
public TokenInfo(TokenType tokenType, String name, String setCode, Integer imageNumber) {
|
||||
this(tokenType, name, setCode, imageNumber, "", "");
|
||||
|
@ -31,7 +33,7 @@ public class TokenInfo {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s - %s - %d (%s)", this.setCode, this.name, this.imageNumber, this.classFileName);
|
||||
return String.format("%s - %s - %s - %d (%s)", this.tokenType, this.setCode, this.name, this.imageNumber, this.classFileName);
|
||||
}
|
||||
|
||||
public TokenType getTokenType() {
|
||||
|
@ -50,14 +52,19 @@ public class TokenInfo {
|
|||
return setCode;
|
||||
}
|
||||
|
||||
public String getClassFileName() {
|
||||
return classFileName;
|
||||
}
|
||||
|
||||
public Integer getImageNumber() {
|
||||
return imageNumber;
|
||||
}
|
||||
|
||||
public String getDownloadUrl() {
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
public TokenInfo withDownloadUrl(String downloadUrl) {
|
||||
this.downloadUrl = downloadUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getFullClassFileName() {
|
||||
String simpleName = classFileName.isEmpty() ? name.replaceAll("[^a-zA-Z0-9]", "") : classFileName;
|
||||
switch (this.tokenType) {
|
||||
|
@ -69,6 +76,8 @@ public class TokenInfo {
|
|||
return "mage.game.command.planes." + simpleName;
|
||||
case DUNGEON:
|
||||
return "mage.game.command.dungeons." + simpleName;
|
||||
case XMAGE:
|
||||
return classFileName;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown token type: " + this.tokenType);
|
||||
}
|
||||
|
|
|
@ -15,11 +15,13 @@ public enum TokenRepository {
|
|||
|
||||
instance;
|
||||
|
||||
public static final String XMAGE_TOKENS_SET_CODE = "XMAGE";
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TokenRepository.class);
|
||||
|
||||
private ArrayList<TokenInfo> allTokens = new ArrayList<>();
|
||||
private Map<String, List<TokenInfo>> indexByClassName = new HashMap<>();
|
||||
private Map<TokenType, List<TokenInfo>> indexByType = new HashMap<>();
|
||||
private final Map<String, List<TokenInfo>> indexByClassName = new HashMap<>();
|
||||
private final Map<TokenType, List<TokenInfo>> indexByType = new HashMap<>();
|
||||
|
||||
TokenRepository() {
|
||||
}
|
||||
|
@ -29,7 +31,9 @@ public enum TokenRepository {
|
|||
return;
|
||||
}
|
||||
|
||||
allTokens = loadAllTokens();
|
||||
// tokens
|
||||
allTokens = loadMtgTokens();
|
||||
allTokens.addAll(loadXmageTokens());
|
||||
|
||||
// index
|
||||
allTokens.forEach(token -> {
|
||||
|
@ -72,7 +76,7 @@ public enum TokenRepository {
|
|||
return indexByClassName.getOrDefault(fullClassName, new ArrayList<>());
|
||||
}
|
||||
|
||||
private static ArrayList<TokenInfo> loadAllTokens() throws RuntimeException {
|
||||
private static ArrayList<TokenInfo> loadMtgTokens() throws RuntimeException {
|
||||
// Must load tokens data in strict mode (throw exception on any error)
|
||||
// Try to put verify checks here instead verify tests
|
||||
String dbSource = "tokens-database.txt";
|
||||
|
@ -203,4 +207,72 @@ public enum TokenRepository {
|
|||
|
||||
return list;
|
||||
}
|
||||
|
||||
public Map<String, String> prepareScryfallDownloadList() {
|
||||
init();
|
||||
|
||||
Map<String, String> res = new LinkedHashMap<>();
|
||||
|
||||
// format example:
|
||||
// put("ONC/Angel/1", "https://api.scryfall.com/cards/tonc/2/en?format=image");
|
||||
allTokens.stream()
|
||||
.filter(token -> token.getTokenType().equals(TokenType.XMAGE))
|
||||
.forEach(token -> {
|
||||
String code = String.format("%s/%s/%d", token.getSetCode(), token.getName(), token.getImageNumber());
|
||||
res.put(code, token.getDownloadUrl());
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
private static TokenInfo createXmageToken(String name, Integer imageNumber, String scryfallDownloadUrl) {
|
||||
return new TokenInfo(TokenType.XMAGE, name, XMAGE_TOKENS_SET_CODE, imageNumber)
|
||||
.withDownloadUrl(scryfallDownloadUrl);
|
||||
}
|
||||
|
||||
private static ArrayList<TokenInfo> loadXmageTokens() {
|
||||
// Create reminder/helper tokens (special images like Copy, Morph, Manifest, etc)
|
||||
// Search by
|
||||
// - https://tagger.scryfall.com/tags/card/assistant-cards
|
||||
// - https://scryfall.com/search?q=otag%3Aassistant-cards&unique=cards&as=grid&order=name
|
||||
// Must add only unique prints
|
||||
// TODO: add custom set in download window to download a custom tokens only
|
||||
// TODO: add custom set in card viewer to view a custom tokens only
|
||||
ArrayList<TokenInfo> res = new ArrayList<>();
|
||||
|
||||
// Copy
|
||||
// https://scryfall.com/search?q=include%3Aextras+unique%3Aprints+type%3Atoken+copy&unique=cards&as=grid&order=name
|
||||
res.add(createXmageToken("Copy", 1, "https://api.scryfall.com/cards/tclb/19/en?format=image"));
|
||||
res.add(createXmageToken("Copy", 2, "https://api.scryfall.com/cards/tsnc/1/en?format=image"));
|
||||
res.add(createXmageToken("Copy", 3, "https://api.scryfall.com/cards/tvow/19/en?format=image"));
|
||||
res.add(createXmageToken("Copy", 4, "https://api.scryfall.com/cards/tznr/12/en?format=image"));
|
||||
|
||||
// City's Blessing
|
||||
// https://scryfall.com/search?q=type%3Atoken+include%3Aextras+unique%3Aprints+City%27s+Blessing+&unique=cards&as=grid&order=name
|
||||
res.add(createXmageToken("City's Blessing", 1, "https://api.scryfall.com/cards/f18/2/en?format=image"));
|
||||
|
||||
// Day // Night
|
||||
// https://scryfall.com/search?q=include%3Aextras+unique%3Aprints+%22Day+%2F%2F+Night%22&unique=cards&as=grid&order=name
|
||||
res.add(createXmageToken("Day", 1, "https://api.scryfall.com/cards/tvow/21/en?format=image&face=front"));
|
||||
res.add(createXmageToken("Night", 1, "https://api.scryfall.com/cards/tvow/21/en?format=image&face=back"));
|
||||
|
||||
// Manifest
|
||||
// https://scryfall.com/search?q=Manifest+include%3Aextras+unique%3Aprints&unique=cards&as=grid&order=name
|
||||
res.add(createXmageToken("Manifest", 1, "https://api.scryfall.com/cards/tc19/28/en?format=image"));
|
||||
res.add(createXmageToken("Manifest", 2, "https://api.scryfall.com/cards/tc18/1/en?format=image"));
|
||||
res.add(createXmageToken("Manifest", 3, "https://api.scryfall.com/cards/tfrf/4/en?format=image"));
|
||||
res.add(createXmageToken("Manifest", 4, "https://api.scryfall.com/cards/tncc/3/en?format=image"));
|
||||
|
||||
// Morph
|
||||
// https://scryfall.com/search?q=Morph+unique%3Aprints+otag%3Aassistant-cards&unique=cards&as=grid&order=name
|
||||
res.add(createXmageToken("Morph", 1, "https://api.scryfall.com/cards/tktk/11/en?format=image"));
|
||||
res.add(createXmageToken("Morph", 2, "https://api.scryfall.com/cards/ta25/15/en?format=image"));
|
||||
res.add(createXmageToken("Morph", 3, "https://api.scryfall.com/cards/tc19/27/en?format=image"));
|
||||
|
||||
// The Monarch
|
||||
// https://scryfall.com/search?q=Monarch+unique%3Aprints+otag%3Aassistant-cards&unique=cards&as=grid&order=name
|
||||
res.add(createXmageToken("The Monarch", 1, "https://api.scryfall.com/cards/tonc/22/en?format=image"));
|
||||
res.add(createXmageToken("The Monarch", 2, "https://api.scryfall.com/cards/tcn2/1/en?format=image"));
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package mage.cards.repository;
|
||||
|
||||
/**
|
||||
* GUI related
|
||||
* XMage's token types for images
|
||||
*
|
||||
* @author JayDi85
|
||||
|
@ -10,6 +11,7 @@ public enum TokenType {
|
|||
TOKEN,
|
||||
EMBLEM,
|
||||
PLANE,
|
||||
DUNGEON
|
||||
DUNGEON,
|
||||
XMAGE // custom images for reminder cards like Copy, Manifest, etc
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package mage.game.permanent.token;
|
||||
|
||||
/**
|
||||
* Token container for copyable characteristics, don't put it to battlefield
|
||||
*
|
||||
* @author nantuko
|
||||
*/
|
||||
public final class EmptyToken extends TokenImpl {
|
||||
|
|
|
@ -167,14 +167,13 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// by set code
|
||||
// search by set code
|
||||
List<TokenInfo> possibleInfo = TokenRepository.instance.getByClassName(token.getClass().getName())
|
||||
.stream()
|
||||
.filter(info -> info.getSetCode().equals(setCode))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// by random set
|
||||
// search by random set
|
||||
if (possibleInfo.isEmpty()) {
|
||||
possibleInfo = new ArrayList<>(TokenRepository.instance.getByClassName(token.getClass().getName()));
|
||||
}
|
||||
|
@ -183,11 +182,13 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
|
|||
return RandomUtil.randomFromCollection(possibleInfo);
|
||||
}
|
||||
|
||||
// unknown token
|
||||
// TODO: download default tokens for xmage's set and use random images from it
|
||||
// example: TOK.zip/Creature.1.full.jpg
|
||||
// example: TOK.zip/Creature.2.full.jpg
|
||||
return new TokenInfo(TokenType.TOKEN, "Unknown", "XMAGE", 0);
|
||||
// TODO: implement auto-generate images for CreatureToken (search public tokens for same characteristics)
|
||||
// TODO: implement Copy image
|
||||
// TODO: implement Manifest image
|
||||
// TODO: implement Morph image
|
||||
|
||||
// unknown tokens
|
||||
return new TokenInfo(TokenType.TOKEN, "Unknown", TokenRepository.XMAGE_TOKENS_SET_CODE, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -406,7 +407,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set token index to search in card-pictures-tok.txt (if set have multiple
|
||||
* Set token index to search in tokens-database.txt (if set have multiple
|
||||
* tokens with same name) Default is 1
|
||||
*/
|
||||
@Override
|
||||
|
|
|
@ -10,6 +10,10 @@ import mage.game.permanent.token.TokenImpl;
|
|||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Token builder for token effects
|
||||
*
|
||||
* Use it for custom tokens (tokens without public class and image)
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public final class CreatureToken extends TokenImpl {
|
||||
|
|
|
@ -976,7 +976,7 @@ public final class CardUtil {
|
|||
|| text.startsWith("any ")) {
|
||||
return text;
|
||||
}
|
||||
return vowels.contains(text.substring(0, 1)) ? "an " + text : "a " + text;
|
||||
return (!text.isEmpty() && vowels.contains(text.substring(0, 1))) ? "an " + text : "a " + text;
|
||||
}
|
||||
|
||||
public static String italicizeWithEmDash(String text) {
|
||||
|
|
Loading…
Reference in a new issue