Compare commits

..

4 Commits

Author SHA1 Message Date
Hans Mackowiak
a21656521f Update Card.java
fix
2025-07-07 13:10:09 +02:00
Hans Mackowiak
bdec4095c0 Use ImmutableList.Builder
Use ImmutableList.Builder instead of ImmutableList.of(Iterables.concat())
2025-07-07 12:58:05 +02:00
Eradev
8475456c0b Add missing radiation token (#7948) 2025-07-07 08:51:41 +00:00
Fulgur14
651caccd71 Dawnsire, Sunstar Dreadnought (EOE) (#7958) 2025-07-07 06:18:15 +00:00
18 changed files with 44 additions and 170 deletions

View File

@@ -39,9 +39,6 @@ public final class ImageKeys {
public static final String SPECFACE_R = "$rspec";
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,
CACHE_FATPACK_PICS_DIR, CACHE_BOOSTERBOX_PICS_DIR, CACHE_PRECON_PICS_DIR, CACHE_TOURNAMENTPACK_PICS_DIR;
public static String ADVENTURE_CARD_PICS_DIR;

View File

@@ -1,13 +0,0 @@
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

@@ -1,100 +0,0 @@
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

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

View File

@@ -523,29 +523,6 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
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
* name, edition, and collector number. Examples include permanent markings on the card, and flags for Adventure

View File

@@ -4234,12 +4234,12 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
return ImmutableList.of();
}
Iterable<CardChangedType> byText = changedTypeByText == null ? ImmutableList.of() : ImmutableList.of(this.changedTypeByText);
return ImmutableList.copyOf(Iterables.concat(
changedCardTypesByText.values(), // Layer 3
byText, // Layer 3 by Word Changes,
changedCardTypesCharacterDefining.values(), // Layer 4
changedCardTypes.values() // Layer 6
));
return ImmutableList.<CardChangedType>builder()
.addAll(changedCardTypesByText.values()) // Layer 3
.addAll(byText) // Layer 3 by Word Changes,
.addAll(changedCardTypesCharacterDefining.values()) // Layer 4
.addAll(changedCardTypes.values()) // Layer 6
.build();
}
public boolean clearChangedCardTypes() {

View File

@@ -904,10 +904,8 @@ public class Player extends GameEntity implements Comparable<Player> {
runParams.put(AbilityKey.CounterAmount, oldValue + i + 1);
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAdded, AbilityKey.newMap(runParams), false);
}
if (addAmount > 0) {
runParams.put(AbilityKey.CounterAmount, addAmount);
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAddedOnce, AbilityKey.newMap(runParams), false);
}
runParams.put(AbilityKey.CounterAmount, addAmount);
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAddedOnce, AbilityKey.newMap(runParams), false);
if (table != null) {
table.put(source, this, counterType, addAmount);
}
@@ -3605,7 +3603,7 @@ public class Player extends GameEntity implements Comparable<Player> {
}
String trigStr = "Mode$ Phase | Phase$ Main1 | ValidPlayer$ You | TriggerZones$ Command | TriggerDescription$ " +
"At the beginning of your precombat main phase, if you have any rad counters, mill that many cards. For each nonland card milled this way, you lose 1 life and a rad counter.";
"At the beginning of your precombat main phase, if you have any rad counters, mill that many cards. For each nonland card milled this way, you lose 1 life and a rad counter.";
Trigger tr = TriggerHandler.parseTrigger(trigStr, radiationEffect, true);
SpellAbility sa = AbilityFactory.getAbility("DB$ InternalRadiation", radiationEffect);

View File

@@ -2,6 +2,6 @@ Name:Channeled Force
ManaCost:2 U R
Types:Instant
A:SP$ Draw | Cost$ 2 U R Discard<X/Card/card> | CostDesc$ As an additional cost to cast this spell, discard X cards. | NumCards$ X | ValidTgts$ Player | TgtPrompt$ Choose a player | SubAbility$ DBDamage | SpellDescription$ Target player draws X cards. CARDNAME deals X damage to up to one target creature or planeswalker.
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature,Planeswalker | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select target creature or planeswalker. | NumDmg$ X
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature,Planeswalker | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select target creature or planeswalker | NumDmg$ X
SVar:X:Count$xPaid
Oracle:As an additional cost to cast this spell, discard X cards.\nTarget player draws X cards. Channeled Force deals X damage to up to one target creature or planeswalker.

View File

@@ -4,7 +4,7 @@ Types:Enchantment Aura
K:Enchant:Planeswalker
SVar:AttachAILogic:Pump
S:Mode$ Continuous | Affected$ Planeswalker.EnchantedBy | AddAbility$ TeferiUlt | Description$ Enchanted planeswalker has "[-12]: You get an emblem with 'You may activate loyalty abilities of planeswalkers you control on any player's turn any time you could cast an instant.'"
SVar:TeferiUlt:AB$ Effect | Cost$ SubCounter<12/LOYALTY> | Planeswalker$ True | Name$ Emblem — Teferi, Temporal Archmage | StaticAbilities$ InstantPlaneswalkers | Stackable$ False | Duration$ Permanent | AILogic$ Always | SpellDescription$ You get an emblem with "You may activate loyalty abilities of planeswalkers you control on any player's turn any time you could cast an instant."
SVar:TeferiUlt:AB$ Effect | Cost$ SubCounter<12/LOYALTY> | Planeswalker$ True | Name$ Emblem — Teferi, Temporal Archmage | Image$ emblem_teferis_talent | StaticAbilities$ InstantPlaneswalkers | Stackable$ False | Duration$ Permanent | AILogic$ Always | SpellDescription$ You get an emblem with "You may activate loyalty abilities of planeswalkers you control on any player's turn any time you could cast an instant."
SVar:InstantPlaneswalkers:Mode$ CastWithFlash | ValidCard$ Planeswalker.YouCtrl | ValidSA$ Activated.Loyalty | Caster$ You | Description$ You may activate loyalty abilities of planeswalkers you control on any player's turn any time you could cast an instant.
T:Mode$ Drawn | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you draw a card, put a loyalty counter on enchanted planeswalker.
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Enchanted | CounterType$ LOYALTY | CounterNum$ 1

View File

@@ -0,0 +1,10 @@
Name:Dawnsire, Sunstar Dreadnought
ManaCost:5
Types:Legendary Artifact Spacecraft
PT:20/20
K:Station:20
S:Mode$ Continuous | Affected$ Card.Self+counters_GE10_CHARGE | AddTrigger$ AttackTrig | Description$ STATION 10+ Whenever you attack, NICKNAME deals 100 damage to up to one target creature or planeswalker.
S:Mode$ Continuous | Affected$ Card.Self+counters_GE20_CHARGE | AddType$ Creature | AddKeyword$ Flying | Description$ STATION 20+ Flying
SVar:AttackTrig:Mode$ AttackersDeclared | AttackingPlayer$ You | Execute$ TrigDamage | TriggerZones$ Battlefield | TriggerDescription$ Whenever you attack, NICKNAME deals 100 damage to up to one target creature or planeswalker.
SVar:TrigDamage:DB$ DealDamage | ValidTgts$ Creature,Planeswalker | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select target creature or planeswalker | NumDmg$ 100
Oracle:Station (Tap another creature you control: Put charge counters equal to its power on this Spacecraft. Station only as a sorcery. It's an artifact creature at 20+.)\nSTATION 10+\nWhenever you attack, Dawnsire deals 100 damage to up to one target creature or planeswalker.\nSTATION 20+\nFlying

View File

@@ -447,4 +447,5 @@ ScryfallCode=C21
29 c_1_1_a_thopter_flying @Svetlin Velinov
[other]
eternalize_champion_of_wits
6 eternalize_champion_of_wits @Eliteven Amundsen
30 copy @David Palumbo

View File

@@ -352,4 +352,5 @@ ScryfallCode=AFC
12 c_1_1_a_thopter_flying @Richard Wright
[other]
eternalize_champion_of_wits
4 eternalize_champion_of_wits @Even Amundsen
13 monarch @Volkan Baǵa

View File

@@ -1095,3 +1095,6 @@ ScryfallCode=PIP
18 c_a_treasure_sac @Rafater
19 c_a_treasure_sac @Rafater
20 wasteland_survival_guide @Sergio Cosmai
[other]
22 radiation @Skinnyelbows

View File

@@ -500,3 +500,9 @@ ScryfallCode=MOC
40 c_x_x_a_phyrexian_horror @Scott Chou
41 replicated_ring @Olena Richards
42 c_1_1_a_servo @Victor Adame Minguez
[other]
43 emblem_elspeth_suns_champion @Tyler Jacobson
44 emblem_teferis_talent @Dominik Mayer
45 blessing @Yeong-Hao Han
46 monarch @Volkan Baǵa

View File

@@ -580,3 +580,6 @@ ScryfallCode=LTC
12 g_1_1_insect_flying_deathtouch @Oleg Bulakh
13 g_x_x_treefolk @Audrey Benjaminsen
14 br_6_6_dragon_flying_menace_steal @Crystal Sully
[other]
15 monarch @Audrey Benjaminsen

View File

@@ -393,3 +393,8 @@ ScryfallCode=LCC
13 g_3_3_frog_lizard @Jack Wang
14 wb_1_1_vampire_lifelink @Zoltan Boros
15 c_a_blood_draw @Miranda Meeks
[other]
16 emblem_sorin_lord_of_innistrad @Michael Komarck
17 blessing @Alfven Ato
18 monarch @Volkan Baǵa

View File

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

View File

@@ -192,7 +192,6 @@ public abstract class ImageFetcher {
updateLink = true;
} catch (Exception e) {
filename = paperCard.getCardAltImageKey();
updateLink = false;
}
}
destFile = new File(ForgeConstants.CACHE_CARD_PICS_DIR, filename + ".jpg");
@@ -271,7 +270,6 @@ public abstract class ImageFetcher {
return;
}
// Load the paper token from filename + edition
CardEdition edition = StaticData.instance().getEditions().get(setCode);
if (edition == null || edition.getType() == CardEdition.Type.CUSTOM_SET) return; //Custom set token, skip fetching.