mirror of
https://github.com/correl/mage.git
synced 2024-11-24 19:19:56 +00:00
Images: added direct image links support in scryfall, added SLD's alternative images from second sides (example: Zndrsplt, Eye of Wisdom);
This commit is contained in:
parent
903a9215cc
commit
a6b2bea8af
8 changed files with 105 additions and 45 deletions
|
@ -74,16 +74,22 @@ public enum ScryfallImageSource implements CardImageSource {
|
|||
|
||||
// CARDS TRY
|
||||
|
||||
// direct links to images via hardcoded API path. Used for cards with non-ASCII collector numbers
|
||||
// direct links to images via hardcoded API path
|
||||
// used for cards with non-ASCII collector numbers or another use cases
|
||||
if (baseUrl == null) {
|
||||
String apiUrl = ScryfallImageSupportCards.findDirectDownloadLink(card.getSet(), card.getName(), card.getCollectorId());
|
||||
if (apiUrl != null) {
|
||||
baseUrl = apiUrl + localizedCode + "?format=image";
|
||||
alternativeUrl = apiUrl + defaultCode + "?format=image";
|
||||
|
||||
// workaround to use cards without english images (some promos or special cards)
|
||||
if (Objects.equals(baseUrl, alternativeUrl) && baseUrl.endsWith("/en?format=image")) {
|
||||
alternativeUrl = alternativeUrl.replace("/en?format=image", "/?format=image");
|
||||
String link = ScryfallImageSupportCards.findDirectDownloadLink(card.getSet(), card.getName(), card.getCollectorId());
|
||||
if (link != null) {
|
||||
if (ScryfallImageSupportCards.isApiLink(link)) {
|
||||
// api
|
||||
baseUrl = link + localizedCode + "?format=image";
|
||||
alternativeUrl = link + defaultCode + "?format=image";
|
||||
// workaround to use cards without english images (some promos or special cards)
|
||||
if (Objects.equals(baseUrl, alternativeUrl) && baseUrl.endsWith("/en?format=image")) {
|
||||
alternativeUrl = alternativeUrl.replace("/en?format=image", "/?format=image");
|
||||
}
|
||||
} else {
|
||||
// image
|
||||
baseUrl = link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,18 +109,22 @@ public enum ScryfallImageSource implements CardImageSource {
|
|||
// basic cards by api call (redirect to img link)
|
||||
// example: https://api.scryfall.com/cards/xln/121/en?format=image
|
||||
if (baseUrl == null) {
|
||||
baseUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/"
|
||||
+ card.getCollectorId() + "/" + localizedCode + "?format=image";
|
||||
alternativeUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/"
|
||||
+ card.getCollectorId() + "/" + defaultCode + "?format=image";
|
||||
|
||||
baseUrl = String.format("https://api.scryfall.com/cards/%s/%s/%s?format=image",
|
||||
formatSetName(card.getSet(), isToken),
|
||||
card.getCollectorId(),
|
||||
localizedCode);
|
||||
alternativeUrl = String.format("https://api.scryfall.com/cards/%s/%s/%s?format=image",
|
||||
formatSetName(card.getSet(), isToken),
|
||||
card.getCollectorId(),
|
||||
defaultCode);
|
||||
// workaround to use cards without english images (some promos or special cards)
|
||||
// bug: https://github.com/magefree/mage/issues/6829
|
||||
// example: Mysterious Egg from IKO https://api.scryfall.com/cards/iko/385/?format=image
|
||||
if (Objects.equals(baseUrl, alternativeUrl)) {
|
||||
// without loc code scryfall must return first available image
|
||||
alternativeUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/"
|
||||
+ card.getCollectorId() + "/?format=image";
|
||||
alternativeUrl = String.format("https://api.scryfall.com/cards/%s/%s/?format=image",
|
||||
formatSetName(card.getSet(), isToken),
|
||||
card.getCollectorId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,6 +136,7 @@ public enum ScryfallImageSource implements CardImageSource {
|
|||
final String localizedCode = languageAliases.getOrDefault(this.getCurrentLanguage(), defaultCode);
|
||||
|
||||
List<String> needUrls = new ArrayList<>();
|
||||
int needFaceIndex = card.isSecondSide() ? 1 : 0;
|
||||
|
||||
String apiUrl = ScryfallImageSupportCards.findDirectDownloadLink(card.getSet(), card.getName(), card.getCollectorId());
|
||||
if (apiUrl != null) {
|
||||
|
@ -143,11 +154,15 @@ public enum ScryfallImageSource implements CardImageSource {
|
|||
} else {
|
||||
// BY CARD NUMBER
|
||||
// localized and default
|
||||
needUrls.add("https://api.scryfall.com/cards/"
|
||||
+ formatSetName(card.getSet(), isToken) + "/" + card.getCollectorId() + "/" + localizedCode);
|
||||
needUrls.add(String.format("https://api.scryfall.com/cards/%s/%s/%s",
|
||||
formatSetName(card.getSet(), isToken),
|
||||
card.getCollectorId(),
|
||||
localizedCode));
|
||||
if (!localizedCode.equals(defaultCode)) {
|
||||
needUrls.add("https://api.scryfall.com/cards/"
|
||||
+ formatSetName(card.getSet(), isToken) + "/" + card.getCollectorId() + "/" + defaultCode);
|
||||
needUrls.add(String.format("https://api.scryfall.com/cards/%s/%s/%s",
|
||||
formatSetName(card.getSet(), isToken),
|
||||
card.getCollectorId(),
|
||||
defaultCode));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,8 +193,11 @@ public enum ScryfallImageSource implements CardImageSource {
|
|||
if (jsonFaces == null) {
|
||||
throw new MageException("Couldn't find card_faces in card's JSON data: " + jsonUrl);
|
||||
}
|
||||
if (jsonFaces.size() < needFaceIndex + 1) {
|
||||
throw new MageException("card_faces doesn't contains face index in card's JSON data: " + jsonUrl);
|
||||
}
|
||||
|
||||
JsonObject jsonFace = jsonFaces.get(card.isSecondSide() ? 1 : 0).getAsJsonObject();
|
||||
JsonObject jsonFace = jsonFaces.get(needFaceIndex).getAsJsonObject();
|
||||
JsonObject jsonImages = JsonUtil.getAsObject(jsonFace, "image_uris");
|
||||
if (jsonImages == null) {
|
||||
throw new MageException("Couldn't find image_uris in card's JSON data: " + jsonUrl);
|
||||
|
|
|
@ -508,21 +508,23 @@ public class ScryfallImageSupportCards {
|
|||
|
||||
private static final Map<String, String> directDownloadLinks = new HashMap<String, String>() {
|
||||
{
|
||||
// xmage card -> api link:
|
||||
// examples:
|
||||
// api example: https://api.scryfall.com/cards/trix/6/
|
||||
// api format is primary
|
||||
// xmage card -> api or image link
|
||||
// WARNING, try use api links as much as possible (it supports build-in translation)
|
||||
//
|
||||
// code form for one card:
|
||||
// example:
|
||||
// api link: https://api.scryfall.com/cards/trix/6/
|
||||
// image link: https://c1.scryfall.com/file/scryfall-cards/large/back/d/5/d5dfd236-b1da-4552-b94f-ebf6bb9dafdf.jpg
|
||||
//
|
||||
// key for one card:
|
||||
// set/card_name
|
||||
//
|
||||
// code form for same name cards (alternative images):
|
||||
// set/card_name/card_number
|
||||
// set/card_name/card_number
|
||||
|
||||
// key for same name cards (alternative images):
|
||||
// set/card_name/card_number_1
|
||||
// set/card_name/card_number_2
|
||||
//
|
||||
// Cards with non-ASCII collector numbers must use direct download (cause xmage uses different card number)
|
||||
// Verify checks must check and show missing data from that list
|
||||
// WARNING, must use as API link, not image
|
||||
|
||||
// 10E
|
||||
put("10E/Air Elemental/64*", "https://api.scryfall.com/cards/10e/64★/");
|
||||
put("10E/Anaba Bodyguard/187*", "https://api.scryfall.com/cards/10e/187★/");
|
||||
|
@ -786,10 +788,6 @@ public class ScryfallImageSupportCards {
|
|||
put("PM14/Hive Stirrings/21*", "https://api.scryfall.com/cards/pm14/21★/");
|
||||
put("PM14/Megantic Sliver/185*", "https://api.scryfall.com/cards/pm14/185★/");
|
||||
put("PM14/Ratchet Bomb/215*", "https://api.scryfall.com/cards/pm14/215★/");
|
||||
// PROE
|
||||
put("PROE/Emrakul, the Aeons Torn/4*", "https://api.scryfall.com/cards/proe/4★/");
|
||||
put("PROE/Lord of Shatterskull Pass/156*", "https://api.scryfall.com/cards/proe/156★/");
|
||||
//
|
||||
// PMBS
|
||||
put("PMBS/Glissa, the Traitor/96*", "https://api.scryfall.com/cards/pmbs/96★/");
|
||||
put("PMBS/Hero of Bladehold/8*", "https://api.scryfall.com/cards/pmbs/8★/");
|
||||
|
@ -948,6 +946,18 @@ public class ScryfallImageSupportCards {
|
|||
put("WAR/Ugin, the Ineffable/2*", "https://api.scryfall.com/cards/war/2★/");
|
||||
put("WAR/Vivien, Champion of the Wilds/180*", "https://api.scryfall.com/cards/war/180★/");
|
||||
put("WAR/Vraska, Swarm's Eminence/236*", "https://api.scryfall.com/cards/war/236★/");
|
||||
// SLD
|
||||
// TODO: update direct image links in 2022 for HQ images
|
||||
put("SLD/Zndrsplt, Eye of Wisdom/379", "https://api.scryfall.com/cards/sld/379/");
|
||||
put("SLD/Zndrsplt, Eye of Wisdom/379b", "https://c1.scryfall.com/file/scryfall-cards/large/back/d/5/d5dfd236-b1da-4552-b94f-ebf6bb9dafdf.jpg");
|
||||
put("SLD/Krark's Thumb/383", "https://api.scryfall.com/cards/sld/383/");
|
||||
put("SLD/Krark's Thumb/383b", "https://c1.scryfall.com/file/scryfall-cards/large/back/9/f/9f63277b-e139-46c8-b9e3-0cfb647f44cc.jpg");
|
||||
put("SLD/Okaun, Eye of Chaos/380", "https://api.scryfall.com/cards/sld/380/");
|
||||
put("SLD/Okaun, Eye of Chaos/380b", "https://c1.scryfall.com/file/scryfall-cards/large/back/9/4/94eea6e3-20bc-4dab-90ba-3113c120fb90.jpg");
|
||||
put("SLD/Propaganda/381", "https://api.scryfall.com/cards/sld/381/");
|
||||
put("SLD/Propaganda/381b", "https://c1.scryfall.com/file/scryfall-cards/large/back/3/e/3e3f0bcd-0796-494d-bf51-94b33c1671e9.jpg");
|
||||
put("SLD/Stitch in Time/382", "https://api.scryfall.com/cards/sld/382/");
|
||||
put("SLD/Stitch in Time/382b", "https://c1.scryfall.com/file/scryfall-cards/large/back/0/8/087c3a0d-c710-4451-989e-596b55352184.jpg");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1001,6 +1011,10 @@ public class ScryfallImageSupportCards {
|
|||
return "";
|
||||
}
|
||||
|
||||
public static boolean isApiLink(String link) {
|
||||
return !link.endsWith(".jpg") && !link.endsWith(".png");
|
||||
}
|
||||
|
||||
public static Map<String, String> getDirectDownloadLinks() {
|
||||
return directDownloadLinks;
|
||||
}
|
||||
|
|
|
@ -436,6 +436,8 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
String cardName = card.getName();
|
||||
boolean isType2 = type2SetsFilter.contains(card.getSetCode());
|
||||
CardDownloadData url = new CardDownloadData(cardName, card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), card.isNightCard());
|
||||
|
||||
// variations must have diff file names with additional postfix
|
||||
if (url.getUsesVariousArt()) {
|
||||
url.setDownloadName(createDownloadName(card));
|
||||
}
|
||||
|
@ -444,7 +446,10 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
url.setSplitCard(card.isSplitCard());
|
||||
url.setType2(isType2);
|
||||
|
||||
// main side
|
||||
allCardsUrls.add(url);
|
||||
|
||||
// second side (xmage's set doesn't have info about it, so generate it here)
|
||||
if (card.isDoubleFaced()) {
|
||||
if (card.getSecondSideName() == null || card.getSecondSideName().trim().isEmpty()) {
|
||||
throw new IllegalStateException("Second side card can't have empty name.");
|
||||
|
|
|
@ -71,7 +71,7 @@ class EmpoweredAutogeneratorManaEffect extends ManaEffect {
|
|||
public List<Mana> getNetMana(Game game, Ability source) {
|
||||
List<Mana> netMana = new ArrayList<>();
|
||||
if (game != null) {
|
||||
Permanent sourcePermanent = game.getState().getPermanent(source.getSourceId());
|
||||
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
||||
if (sourcePermanent != null) {
|
||||
int counters = sourcePermanent.getCounters(game).getCount(CounterType.CHARGE) + 1; // one counter will be added on real mana call
|
||||
if (counters > 0) {
|
||||
|
@ -89,7 +89,7 @@ class EmpoweredAutogeneratorManaEffect extends ManaEffect {
|
|||
return mana;
|
||||
}
|
||||
game.getState().processAction(game);
|
||||
Permanent sourcePermanent = game.getState().getPermanent(source.getSourceId());
|
||||
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
||||
if (sourcePermanent == null) {
|
||||
return mana;
|
||||
}
|
||||
|
|
|
@ -357,11 +357,16 @@ public class SecretLairDrop extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Craterhoof Behemoth", 376, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Metalwork Colossus", 377, Rarity.RARE, mage.cards.m.MetalworkColossus.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Metalwork Colossus", 378, Rarity.RARE, mage.cards.m.MetalworkColossus.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Zndrsplt, Eye of Wisdom", 379, Rarity.RARE, mage.cards.z.ZndrspltEyeOfWisdom.class));
|
||||
cards.add(new SetCardInfo("Okaun, Eye of Chaos", 380, Rarity.RARE, mage.cards.o.OkaunEyeOfChaos.class));
|
||||
cards.add(new SetCardInfo("Propaganda", 381, Rarity.RARE, mage.cards.p.Propaganda.class));
|
||||
cards.add(new SetCardInfo("Stitch in Time", 382, Rarity.RARE, mage.cards.s.StitchInTime.class));
|
||||
cards.add(new SetCardInfo("Krark's Thumb", 383, Rarity.RARE, mage.cards.k.KrarksThumb.class));
|
||||
cards.add(new SetCardInfo("Zndrsplt, Eye of Wisdom", 379, Rarity.RARE, mage.cards.z.ZndrspltEyeOfWisdom.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Zndrsplt, Eye of Wisdom", "379b", Rarity.RARE, mage.cards.z.ZndrspltEyeOfWisdom.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Okaun, Eye of Chaos", 380, Rarity.RARE, mage.cards.o.OkaunEyeOfChaos.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Okaun, Eye of Chaos", "380b", Rarity.RARE, mage.cards.o.OkaunEyeOfChaos.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Propaganda", 381, Rarity.RARE, mage.cards.p.Propaganda.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Propaganda", "381b", Rarity.RARE, mage.cards.p.Propaganda.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Stitch in Time", 382, Rarity.RARE, mage.cards.s.StitchInTime.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Stitch in Time", "382b", Rarity.RARE, mage.cards.s.StitchInTime.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Krark's Thumb", 383, Rarity.RARE, mage.cards.k.KrarksThumb.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Krark's Thumb", "383b", Rarity.RARE, mage.cards.k.KrarksThumb.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Path to Exile", 477, Rarity.RARE, mage.cards.p.PathToExile.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Rhystic Study", 478, Rarity.RARE, mage.cards.r.RhysticStudy.class));
|
||||
cards.add(new SetCardInfo("Counterflux", 482, Rarity.RARE, mage.cards.c.Counterflux.class));
|
||||
|
|
|
@ -40,6 +40,7 @@ public final class MtgJsonCard {
|
|||
|| "flip".equals(layout)
|
||||
|| "adventure".equals(layout)
|
||||
|| "modal_dfc".equals(layout)
|
||||
|| "reversible_card".equals(layout) // example: Zndrsplt, Eye of Wisdom
|
||||
|| "split".equals(layout)
|
||||
|| "aftermath".equals(layout)
|
||||
|| "meld".equals(layout)) { // mtgjson uses composite names for meld cards, but scryfall uses simple face names
|
||||
|
|
|
@ -196,6 +196,7 @@ public class VerifyCardDataTest {
|
|||
skipListAddName(SKIP_LIST_WRONG_CARD_NUMBERS, "UND"); // un-sets don't have full implementation of card variations
|
||||
skipListAddName(SKIP_LIST_WRONG_CARD_NUMBERS, "UST"); // un-sets don't have full implementation of card variations
|
||||
skipListAddName(SKIP_LIST_WRONG_CARD_NUMBERS, "SOI", "Tamiyo's Journal"); // not all variations implemented
|
||||
skipListAddName(SKIP_LIST_WRONG_CARD_NUMBERS, "SLD", "Zndrsplt, Eye of Wisdom"); // xmage adds additional card for alternative image (second side)
|
||||
|
||||
|
||||
// scryfall download sets (missing from scryfall website)
|
||||
|
@ -700,10 +701,16 @@ public class VerifyCardDataTest {
|
|||
}
|
||||
|
||||
for (ExpansionSet.SetCardInfo card : set.getSetCardInfo()) {
|
||||
if (skipListHaveName(SKIP_LIST_WRONG_CARD_NUMBERS, set.getCode(), card.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MtgJsonCard jsonCard = MtgJsonService.cardFromSet(set.getCode(), card.getName(), card.getCardNumber());
|
||||
if (jsonCard == null) {
|
||||
// see convertMtgJsonToXmageCardNumber for card number convert notation
|
||||
errorsList.add("Error: scryfall download can't find card from mtgjson " + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber());
|
||||
if (!skipListHaveName(SKIP_LIST_WRONG_CARD_NUMBERS, set.getCode(), card.getName())) {
|
||||
errorsList.add("Error: scryfall download can't find card from mtgjson " + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -718,6 +725,16 @@ public class VerifyCardDataTest {
|
|||
errorsList.add("Error: scryfall download can't find non-ascii card link in direct download list " + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + jsonCard.number);
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK: reversible_card must be in direct download list (xmage must have 2 cards with diff image face)
|
||||
if (jsonCard.layout.equals("reversible_card")) {
|
||||
String key = ScryfallImageSupportCards.findDirectDownloadKey(set.getCode(), card.getName(), card.getCardNumber());
|
||||
if (key != null) {
|
||||
foundedDirectDownloadKeys.add(key);
|
||||
} else {
|
||||
errorsList.add("Error: scryfall download can't find face image of reversible_card in direct download list " + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + jsonCard.number);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -731,7 +748,7 @@ public class VerifyCardDataTest {
|
|||
continue;
|
||||
}
|
||||
|
||||
// skip non implemented cards list
|
||||
// skip non-implemented cards list
|
||||
if (CardRepository.instance.findCard(cardName) == null) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -504,7 +504,7 @@ public enum CardRepository {
|
|||
}
|
||||
|
||||
/**
|
||||
* Warning, don't use db functions in card's code - it generate heavy db loading in AI simulations. If you
|
||||
* Warning, don't use db functions in card's code - it generates heavy db loading in AI simulations. If you
|
||||
* need that feature then check for simulation mode. See https://github.com/magefree/mage/issues/7014
|
||||
*
|
||||
* @param criteria
|
||||
|
|
Loading…
Reference in a new issue