Compare commits

...

5 Commits

Author SHA1 Message Date
Hans Mackowiak
b5938e5609 Add UrlEncoder for crazy collectorNumber 2025-07-07 07:14:16 +02:00
Hans Mackowiak
15dd063078 move CollectorNumberSuffix to PaperCard because it depends on the SplitType for Meld 2025-07-07 07:14:16 +02:00
Hans Mackowiak
210fd6dbe3 Add more logic to DownloadUrl and its Downloaded Path 2025-07-07 07:14:16 +02:00
Hans Mackowiak
313d1b849e fix import 2025-07-07 07:14:16 +02:00
Hans Mackowiak
0b4ab12f47 add ImageKey class 2025-07-07 07:14:16 +02:00
6 changed files with 153 additions and 2 deletions

View File

@@ -39,6 +39,9 @@ public final class ImageKeys {
public static final String SPECFACE_R = "$rspec"; public static final String SPECFACE_R = "$rspec";
public static final String SPECFACE_G = "$gspec"; public static final String SPECFACE_G = "$gspec";
private static final String URL_SCRYFALL = "https://api.scryfall.com";
public static final String URL_PIC_SCRYFALL_DOWNLOAD = URL_SCRYFALL + "/cards/";
private static String CACHE_CARD_PICS_DIR, CACHE_TOKEN_PICS_DIR, CACHE_ICON_PICS_DIR, CACHE_BOOSTER_PICS_DIR, private static String CACHE_CARD_PICS_DIR, CACHE_TOKEN_PICS_DIR, CACHE_ICON_PICS_DIR, CACHE_BOOSTER_PICS_DIR,
CACHE_FATPACK_PICS_DIR, CACHE_BOOSTERBOX_PICS_DIR, CACHE_PRECON_PICS_DIR, CACHE_TOURNAMENTPACK_PICS_DIR; CACHE_FATPACK_PICS_DIR, CACHE_BOOSTERBOX_PICS_DIR, CACHE_PRECON_PICS_DIR, CACHE_TOURNAMENTPACK_PICS_DIR;
public static String ADVENTURE_CARD_PICS_DIR; public static String ADVENTURE_CARD_PICS_DIR;

View File

@@ -0,0 +1,13 @@
package forge.item;
public enum ArtStyle {
Normal("fullborder", "normal"),
Crop("artcrop", "art_crop");
public final String filename;
public final String scryfall;
ArtStyle(String filename, String scryfall) {
this.filename = filename;
this.scryfall = scryfall;
}
}

View File

@@ -0,0 +1,100 @@
package forge.item;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.Lists;
import forge.ImageKeys;
import forge.StaticData;
import forge.card.CardEdition;
import forge.card.CardStateName;
public record ImageKey(String setCode, String name, String collectorNumber, String artistName, CardStateName state, ImageType type, boolean custom) implements Serializable
{
public List<String> getFilename(ArtStyle art) {
List<String> result = Lists.newArrayList();
String cn = collectorNumber;
if (type == ImageType.Token) {
if (ImageKeys.HIDDEN_CARD.equals(name)) {
// hidden only exist as png
result.add("hidden.png");
return result;
}
// TODO Token doesn't use fullborder or artcrop ArtStyle yet
if (!StringUtils.isEmpty(setCode) && !setCode.equals(CardEdition.UNKNOWN_CODE)) {
if (!StringUtils.isEmpty(cn) && !cn.equals(IPaperCard.NO_COLLECTOR_NUMBER)) {
result.add(setCode + "/" + cn + "_" + name);
}
result.add(setCode + "/" + name);
}
result.add(name);
} else if (type == ImageType.Card) {
if (!StringUtils.isEmpty(setCode) && !setCode.equals(CardEdition.UNKNOWN_CODE)) {
if (!StringUtils.isEmpty(cn) && !cn.equals(IPaperCard.NO_COLLECTOR_NUMBER)) {
result.add(setCode + "/" + cn + "_" + name + "." + art.filename);
}
result.add(setCode + "/" + name + "." + art.filename);
}
result.add(name + "." + art.filename);
}
return result;
}
public Pair<String, String> getDownloadUrl(ArtStyle art) {
if (custom) {
return null;
}
if (type == ImageType.Token && ImageKeys.HIDDEN_CARD.equals(name)) {
return Pair.of("hidden.png", "https://cards.scryfall.io/back.png");
}
if (StringUtils.isEmpty(collectorNumber) || collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER)) {
return null;
}
// Scryfall only for Cards or Tokens
if (type != ImageType.Card && type != ImageType.Token) {
return null;
}
if (StringUtils.isEmpty(setCode) || setCode.equals(CardEdition.UNKNOWN_CODE)) {
return null;
}
CardEdition edition = StaticData.instance().getCardEdition(setCode);
if (edition == null || edition.getType() == CardEdition.Type.CUSTOM_SET) return null;
// differ token code
String setCode = type == ImageType.Card ? edition.getScryfallCode() : edition.getTokensCode();
String langCode = edition.getCardsLangCode();
String faceParam = "";
switch(state) {
case Modal:
case Secondary:
case Transformed:
faceParam = "&face=back";
break;
default:
faceParam = "&face=front";
break;
}
String ext = art.scryfall == "png" ? ".png" : ".jpg";
String filepath = setCode + "/" + collectorNumber + "_" + name + ext;
// TODO make scryfall art_crop of split cards separate
String collectorNumberEncoded;
try {
// encode with Charset isn't supported on Android
collectorNumberEncoded = URLEncoder.encode(collectorNumber, "UTF-8");
} catch (UnsupportedEncodingException e) {
collectorNumberEncoded = collectorNumber;
}
String url = ImageKeys.URL_PIC_SCRYFALL_DOWNLOAD + String.format(
"%s/%s/%s?format=image&version=%s%s", setCode, collectorNumberEncoded, langCode, art.scryfall, faceParam
);
return Pair.of(filepath, url);
}
}

View File

@@ -0,0 +1,12 @@
package forge.item;
public enum ImageType {
Card,
Token,
Booster,
FatPack,
BoosterBox,
Precon,
TournamentPack,
;
}

View File

@@ -523,6 +523,29 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
return StaticData.instance().isRebalanced(name); return StaticData.instance().isRebalanced(name);
} }
public String getCollectorNumberSuffix(CardStateName state) {
if (this.collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER)) {
return "";
}
if (getRules().getSplitType() == CardSplitType.Meld) {
return state == CardStateName.Meld ? "b" : "a";
}
switch(state) {
case SpecializeB:
return "b";
case SpecializeG:
return "g";
case SpecializeR:
return "r";
case SpecializeU:
return "u";
case SpecializeW:
return "w";
default:
return "";
}
}
/** /**
* Contains properties of a card which distinguish it from an otherwise identical copy of the card with the same * Contains properties of a card which distinguish it from an otherwise identical copy of the card with the same
* name, edition, and collector number. Examples include permanent markings on the card, and flags for Adventure * name, edition, and collector number. Examples include permanent markings on the card, and flags for Adventure

View File

@@ -106,7 +106,7 @@ public class MainWorldDuelReader extends StorageReaderFolder<QuestEventDuel> {
qc.setDifficulty(QuestEventDifficulty.fromString(sectionMeta.get("Difficulty"))); qc.setDifficulty(QuestEventDifficulty.fromString(sectionMeta.get("Difficulty")));
qc.setDescription(sectionMeta.get("Description", "").replace("\\n", "\n")); qc.setDescription(sectionMeta.get("Description", "").replace("\\n", "\n"));
qc.setCardReward(sectionMeta.get("Card Reward")); qc.setCardReward(sectionMeta.get("Card Reward"));
qc.setIconImageKey(ImageKeys.ICON_PREFIX + sectionMeta.get("Icon")); qc.setIconImageKey(ImageKeys.ICON_PREFIX + sectionMeta.get("Icon", WILD_DEFAULT_ICON_NAME));
if (sectionMeta.contains("Profile")) { if (sectionMeta.contains("Profile")) {
qc.setProfile(sectionMeta.get("Profile")); qc.setProfile(sectionMeta.get("Profile"));
} }
@@ -114,7 +114,7 @@ public class MainWorldDuelReader extends StorageReaderFolder<QuestEventDuel> {
qc.setDifficulty(QuestEventDifficulty.WILD); qc.setDifficulty(QuestEventDifficulty.WILD);
qc.setTitle(sectionMeta.get("Title") != null ? sectionMeta.get("Title") : qc.getName()); qc.setTitle(sectionMeta.get("Title") != null ? sectionMeta.get("Title") : qc.getName());
qc.setDescription(sectionMeta.get("Description") != null ? sectionMeta.get("Description") : "Wild opponent"); qc.setDescription(sectionMeta.get("Description") != null ? sectionMeta.get("Description") : "Wild opponent");
qc.setIconImageKey(ImageKeys.ICON_PREFIX + (sectionMeta.get("Icon") != null ? sectionMeta.get("Icon") : WILD_DEFAULT_ICON_NAME)); qc.setIconImageKey(ImageKeys.ICON_PREFIX + sectionMeta.get("Icon", WILD_DEFAULT_ICON_NAME));
} }
// Deck // Deck