mirror of
https://github.com/magefree/mage.git
synced 2025-12-29 23:12:10 -08: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
|
|
@ -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…
Add table
Add a link
Reference in a new issue