Merge branch 'coremaster' into respectbanlist
@@ -91,9 +91,6 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
source.setCastSA(sa);
|
|
||||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
|
||||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
|
||||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,9 +216,6 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||||
source.setCastSA(sa);
|
|
||||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
|
||||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
|
||||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,9 +240,6 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||||
source.setCastSA(sa);
|
|
||||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
|
||||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
|
||||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,9 +258,6 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
final Card source = newSA.getHostCard();
|
final Card source = newSA.getHostCard();
|
||||||
if (newSA.isSpell() && !source.isCopiedSpell()) {
|
if (newSA.isSpell() && !source.isCopiedSpell()) {
|
||||||
source.setCastSA(newSA);
|
|
||||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
|
||||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
|
||||||
newSA.setHostCard(game.getAction().moveToStack(source, sa));
|
newSA.setHostCard(game.getAction().moveToStack(source, sa));
|
||||||
|
|
||||||
if (newSA.getApi() == ApiType.Charm && !newSA.isWrapper()) {
|
if (newSA.getApi() == ApiType.Charm && !newSA.isWrapper()) {
|
||||||
@@ -290,9 +278,6 @@ public class ComputerUtil {
|
|||||||
if (ComputerUtilCost.canPayCost(sa, ai)) {
|
if (ComputerUtilCost.canPayCost(sa, ai)) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||||
source.setCastSA(sa);
|
|
||||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
|
||||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
|
||||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -981,6 +981,17 @@ public abstract class GameState {
|
|||||||
spellDef = spellDef.substring(0, spellDef.indexOf("->")).trim();
|
spellDef = spellDef.substring(0, spellDef.indexOf("->")).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Card c = null;
|
||||||
|
|
||||||
|
if (StringUtils.isNumeric(spellDef)) {
|
||||||
|
// Precast from a specific host
|
||||||
|
c = idToCard.get(Integer.parseInt(spellDef));
|
||||||
|
if (c == null) {
|
||||||
|
System.err.println("ERROR: Could not find a card with ID " + spellDef + " to precast!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Precast from a card by name
|
||||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(spellDef);
|
PaperCard pc = StaticData.instance().getCommonCards().getCard(spellDef);
|
||||||
|
|
||||||
if (pc == null) {
|
if (pc == null) {
|
||||||
@@ -988,7 +999,9 @@ public abstract class GameState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card c = Card.fromPaperCard(pc, activator);
|
c = Card.fromPaperCard(pc, activator);
|
||||||
|
}
|
||||||
|
|
||||||
SpellAbility sa = null;
|
SpellAbility sa = null;
|
||||||
|
|
||||||
if (!scriptID.isEmpty()) {
|
if (!scriptID.isEmpty()) {
|
||||||
|
|||||||
@@ -113,7 +113,11 @@ public final class ImageKeys {
|
|||||||
}
|
}
|
||||||
//try fullborder...
|
//try fullborder...
|
||||||
if (filename.contains(".full")) {
|
if (filename.contains(".full")) {
|
||||||
file = findFile(dir, TextUtil.fastReplace(filename, ".full", ".fullborder"));
|
String fullborderFile = TextUtil.fastReplace(filename, ".full", ".fullborder");
|
||||||
|
file = findFile(dir, fullborderFile);
|
||||||
|
if (file != null) { return file; }
|
||||||
|
// if there's an art variant try without it
|
||||||
|
file = findFile(dir, TextUtil.fastReplace(fullborderFile, "1.fullborder", ".fullborder"));
|
||||||
if (file != null) { return file; }
|
if (file != null) { return file; }
|
||||||
}
|
}
|
||||||
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder
|
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder
|
||||||
|
|||||||
@@ -312,17 +312,21 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
return tryGetCard(request);
|
return tryGetCard(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCardCollectorNumber(String cardName, String reqEdition) {
|
public String getCardCollectorNumber(String cardName, String reqEdition, int artIndex) {
|
||||||
cardName = getName(cardName);
|
cardName = getName(cardName);
|
||||||
CardEdition edition = editions.get(reqEdition);
|
CardEdition edition = editions.get(reqEdition);
|
||||||
if (edition == null)
|
if (edition == null)
|
||||||
return -1;
|
return null;
|
||||||
|
int numMatches = 0;
|
||||||
for (CardInSet card : edition.getCards()) {
|
for (CardInSet card : edition.getCards()) {
|
||||||
if (card.name.equalsIgnoreCase(cardName)) {
|
if (card.name.equalsIgnoreCase(cardName)) {
|
||||||
|
numMatches += 1;
|
||||||
|
if (numMatches == artIndex) {
|
||||||
return card.collectorNumber;
|
return card.collectorNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PaperCard tryGetCard(CardRequest request) {
|
private PaperCard tryGetCard(CardRequest request) {
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ import java.text.ParseException;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,10 +77,10 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
|
|
||||||
public static class CardInSet {
|
public static class CardInSet {
|
||||||
public final CardRarity rarity;
|
public final CardRarity rarity;
|
||||||
public final int collectorNumber;
|
public final String collectorNumber;
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
public CardInSet(final String name, final int collectorNumber, final CardRarity rarity) {
|
public CardInSet(final String name, final String collectorNumber, final CardRarity rarity) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.collectorNumber = collectorNumber;
|
this.collectorNumber = collectorNumber;
|
||||||
this.rarity = rarity;
|
this.rarity = rarity;
|
||||||
@@ -86,7 +88,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (collectorNumber != -1) {
|
if (collectorNumber != null) {
|
||||||
sb.append(collectorNumber);
|
sb.append(collectorNumber);
|
||||||
sb.append(' ');
|
sb.append(' ');
|
||||||
}
|
}
|
||||||
@@ -190,6 +192,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
public boolean getSmallSetOverride() { return smallSetOverride; }
|
public boolean getSmallSetOverride() { return smallSetOverride; }
|
||||||
public String getBoosterMustContain() { return boosterMustContain; }
|
public String getBoosterMustContain() { return boosterMustContain; }
|
||||||
public CardInSet[] getCards() { return cards; }
|
public CardInSet[] getCards() { return cards; }
|
||||||
|
public boolean isModern() { return getDate().after(parseDate("2003-07-27")); } //8ED and above are modern except some promo cards and others
|
||||||
|
|
||||||
public Map<String, Integer> getTokens() { return tokenNormalized; }
|
public Map<String, Integer> getTokens() { return tokenNormalized; }
|
||||||
|
|
||||||
@@ -266,26 +269,35 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
Map<String, Integer> tokenNormalized = new HashMap<>();
|
Map<String, Integer> tokenNormalized = new HashMap<>();
|
||||||
List<CardEdition.CardInSet> processedCards = new ArrayList<>();
|
List<CardEdition.CardInSet> processedCards = new ArrayList<>();
|
||||||
if (contents.containsKey("cards")) {
|
if (contents.containsKey("cards")) {
|
||||||
|
final Pattern pattern = Pattern.compile(
|
||||||
|
/*
|
||||||
|
The following pattern will match the WAR Japanese art entries,
|
||||||
|
it should also match the Un-set and older alternate art cards
|
||||||
|
like Merseine from FEM (should the editions files ever be updated)
|
||||||
|
*/
|
||||||
|
//"(^(?<cnum>[0-9]+.?) )?((?<rarity>[SCURML]) )?(?<name>.*)$"
|
||||||
|
/* Ideally we'd use the named group above, but Android 6 and
|
||||||
|
earlier don't appear to support named groups.
|
||||||
|
So, untill support for those devices is officially dropped,
|
||||||
|
we'll have to suffice with numbered groups.
|
||||||
|
We are looking for:
|
||||||
|
* cnum - grouping #2
|
||||||
|
* rarity - grouping #4
|
||||||
|
* name - grouping #5
|
||||||
|
*/
|
||||||
|
"(^([0-9]+.?) )?(([SCURML]) )?(.*)$"
|
||||||
|
);
|
||||||
for(String line : contents.get("cards")) {
|
for(String line : contents.get("cards")) {
|
||||||
if (StringUtils.isBlank(line))
|
Matcher matcher = pattern.matcher(line);
|
||||||
continue;
|
if (matcher.matches()) {
|
||||||
|
String collectorNumber = matcher.group(2);
|
||||||
// Optional collector number at the start.
|
CardRarity r = CardRarity.smartValueOf(matcher.group(4));
|
||||||
String[] split = line.split(" ", 2);
|
String cardName = matcher.group(5);
|
||||||
int collectorNumber = -1;
|
|
||||||
if (split.length >= 2 && StringUtils.isNumeric(split[0])) {
|
|
||||||
collectorNumber = Integer.parseInt(split[0]);
|
|
||||||
line = split[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// You may omit rarity for early development
|
|
||||||
CardRarity r = CardRarity.smartValueOf(line.substring(0, 1));
|
|
||||||
boolean hadRarity = r != CardRarity.Unknown && line.charAt(1) == ' ';
|
|
||||||
String cardName = hadRarity ? line.substring(2) : line;
|
|
||||||
CardInSet cis = new CardInSet(cardName, collectorNumber, r);
|
CardInSet cis = new CardInSet(cardName, collectorNumber, r);
|
||||||
processedCards.add(cis);
|
processedCards.add(cis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (contents.containsKey("tokens")) {
|
if (contents.containsKey("tokens")) {
|
||||||
for(String line : contents.get("tokens")) {
|
for(String line : contents.get("tokens")) {
|
||||||
|
|||||||
@@ -222,7 +222,12 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
public boolean canBeBrawlCommander() {
|
public boolean canBeBrawlCommander() {
|
||||||
CardType type = mainPart.getType();
|
CardType type = mainPart.getType();
|
||||||
return (type.isLegendary() && type.isCreature()) || type.isPlaneswalker();
|
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canBeTinyLeadersCommander() {
|
||||||
|
CardType type = mainPart.getType();
|
||||||
|
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMeldWith() {
|
public String getMeldWith() {
|
||||||
|
|||||||
@@ -594,8 +594,10 @@ public final class CardRulesPredicates {
|
|||||||
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
|
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
|
||||||
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
|
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
|
||||||
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
|
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
|
||||||
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER,
|
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
|
||||||
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
|
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
|
||||||
|
public static final Predicate<CardRules> CAN_BE_TINY_LEADERS_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
|
||||||
|
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
|
||||||
|
|
||||||
/** The Constant IS_NON_CREATURE_SPELL. **/
|
/** The Constant IS_NON_CREATURE_SPELL. **/
|
||||||
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = com.google.common.base.Predicates
|
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = com.google.common.base.Predicates
|
||||||
|
|||||||
@@ -463,6 +463,9 @@ public enum DeckFormat {
|
|||||||
if (this.equals(DeckFormat.Brawl)) {
|
if (this.equals(DeckFormat.Brawl)) {
|
||||||
return rules.canBeBrawlCommander();
|
return rules.canBeBrawlCommander();
|
||||||
}
|
}
|
||||||
|
if (this.equals(DeckFormat.TinyLeaders)) {
|
||||||
|
return rules.canBeTinyLeadersCommander();
|
||||||
|
}
|
||||||
return rules.canBeCommander();
|
return rules.canBeCommander();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import forge.item.PaperCard;
|
|||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSortedMap;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -17,6 +19,22 @@ import java.util.Map.Entry;
|
|||||||
*/
|
*/
|
||||||
public class TextUtil {
|
public class TextUtil {
|
||||||
|
|
||||||
|
static ImmutableSortedMap<Integer,String> romanMap = ImmutableSortedMap.<Integer,String>naturalOrder()
|
||||||
|
.put(1000, "M").put(900, "CM")
|
||||||
|
.put(500, "D").put(400, "CD")
|
||||||
|
.put(100, "C").put(90, "XC")
|
||||||
|
.put(50, "L").put(40, "XL")
|
||||||
|
.put(10, "X").put(9, "IX")
|
||||||
|
.put(5, "V").put(4, "IV").put(1, "I").build();
|
||||||
|
|
||||||
|
public final static String toRoman(int number) {
|
||||||
|
if (number <= 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int l = romanMap.floorKey(number);
|
||||||
|
return romanMap.get(l) + toRoman(number-l);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Safely converts an object to a String.
|
* Safely converts an object to a String.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -547,6 +547,13 @@ public class GameAction {
|
|||||||
c.setCastSA(null);
|
c.setCastSA(null);
|
||||||
} else if (zoneTo.is(ZoneType.Stack)) {
|
} else if (zoneTo.is(ZoneType.Stack)) {
|
||||||
c.setCastFrom(zoneFrom.getZoneType());
|
c.setCastFrom(zoneFrom.getZoneType());
|
||||||
|
if (cause != null && cause.isSpell() && c.equals(cause.getHostCard()) && !c.isCopiedSpell()) {
|
||||||
|
cause.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||||
|
cause.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||||
|
c.setCastSA(cause);
|
||||||
|
} else {
|
||||||
|
c.setCastSA(null);
|
||||||
|
}
|
||||||
} else if (!(zoneTo.is(ZoneType.Battlefield) && zoneFrom.is(ZoneType.Stack))) {
|
} else if (!(zoneTo.is(ZoneType.Battlefield) && zoneFrom.is(ZoneType.Stack))) {
|
||||||
c.setCastFrom(null);
|
c.setCastFrom(null);
|
||||||
c.setCastSA(null);
|
c.setCastSA(null);
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ import com.google.common.collect.Iterables;
|
|||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import forge.card.MagicColor;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.card.mana.ManaCostParser;
|
import forge.card.mana.ManaCostParser;
|
||||||
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
@@ -32,9 +34,15 @@ import forge.game.cost.Cost;
|
|||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerController;
|
import forge.game.player.PlayerController;
|
||||||
|
import forge.game.replacement.ReplacementEffect;
|
||||||
|
import forge.game.replacement.ReplacementHandler;
|
||||||
|
import forge.game.replacement.ReplacementLayer;
|
||||||
import forge.game.spellability.*;
|
import forge.game.spellability.*;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
|
import forge.game.trigger.TriggerHandler;
|
||||||
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
import forge.util.Lang;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -363,10 +371,11 @@ public final class GameActionUtil {
|
|||||||
}
|
}
|
||||||
SpellAbility result = null;
|
SpellAbility result = null;
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
final Game game = host.getGame();
|
||||||
final Player activator = sa.getActivatingPlayer();
|
final Player activator = sa.getActivatingPlayer();
|
||||||
final PlayerController pc = activator.getController();
|
final PlayerController pc = activator.getController();
|
||||||
|
|
||||||
host.getGame().getAction().checkStaticAbilities(false);
|
game.getAction().checkStaticAbilities(false);
|
||||||
|
|
||||||
boolean reset = false;
|
boolean reset = false;
|
||||||
|
|
||||||
@@ -429,7 +438,60 @@ public final class GameActionUtil {
|
|||||||
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
|
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
|
||||||
|
|
||||||
if (v > 0) {
|
if (v > 0) {
|
||||||
host.addReplacementEffect(CardFactoryUtil.makeEtbCounter("etbCounter:P1P1:" + v, host, false));
|
|
||||||
|
final Card eff = new Card(game.nextCardId(), game);
|
||||||
|
eff.setTimestamp(game.getNextTimestamp());
|
||||||
|
eff.setName(c.getName() + "'s Effect");
|
||||||
|
eff.addType("Effect");
|
||||||
|
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
|
||||||
|
eff.setOwner(activator);
|
||||||
|
|
||||||
|
eff.setImageKey(c.getImageKey());
|
||||||
|
eff.setColor(MagicColor.COLORLESS);
|
||||||
|
eff.setImmutable(true);
|
||||||
|
// try to get the SpellAbility from the mana ability
|
||||||
|
//eff.setEffectSource((SpellAbility)null);
|
||||||
|
|
||||||
|
eff.addRemembered(host);
|
||||||
|
|
||||||
|
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ P1P1 | ETB$ True | CounterNum$ " + v;
|
||||||
|
|
||||||
|
SpellAbility saAb = AbilityFactory.getAbility(abStr, c);
|
||||||
|
|
||||||
|
CardFactoryUtil.setupETBReplacementAbility(saAb);
|
||||||
|
|
||||||
|
String desc = "It enters the battlefield with ";
|
||||||
|
desc += Lang.nounWithNumeral(v, CounterType.P1P1.getName() + " counter");
|
||||||
|
desc += " on it.";
|
||||||
|
|
||||||
|
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
||||||
|
|
||||||
|
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||||
|
re.setLayer(ReplacementLayer.Other);
|
||||||
|
re.setOverridingAbility(saAb);
|
||||||
|
|
||||||
|
eff.addReplacementEffect(re);
|
||||||
|
|
||||||
|
// Forgot Trigger
|
||||||
|
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
|
||||||
|
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||||
|
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||||
|
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||||
|
|
||||||
|
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
|
||||||
|
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
|
||||||
|
saForget.setSubAbility(saExile);
|
||||||
|
|
||||||
|
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
|
||||||
|
parsedTrigger.setOverridingAbility(saForget);
|
||||||
|
eff.addTrigger(parsedTrigger);
|
||||||
|
eff.updateStateForView();
|
||||||
|
|
||||||
|
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||||
|
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||||
|
game.getAction().moveTo(ZoneType.Command, eff, null);
|
||||||
|
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = sa.copy();
|
result = sa.copy();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6353,6 +6353,10 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
removeSVar("PayX"); // Temporary AI X announcement variable
|
removeSVar("PayX"); // Temporary AI X announcement variable
|
||||||
removeSVar("IsCastFromPlayEffect"); // Temporary SVar indicating that the spell is cast indirectly via AF Play
|
removeSVar("IsCastFromPlayEffect"); // Temporary SVar indicating that the spell is cast indirectly via AF Play
|
||||||
setSunburstValue(0); // Sunburst
|
setSunburstValue(0); // Sunburst
|
||||||
|
setXManaCostPaid(0);
|
||||||
|
setXManaCostPaidByColor(null);
|
||||||
|
setKickerMagnitude(0);
|
||||||
|
setPseudoMultiKickerMagnitude(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getFinalChapterNr() {
|
public final int getFinalChapterNr() {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ package forge.game.card;
|
|||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.base.Strings;
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
@@ -3010,24 +3009,43 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
inst.addTrigger(parsedTrigger);
|
inst.addTrigger(parsedTrigger);
|
||||||
} else if (keyword.startsWith("Saga")) {
|
} else if (keyword.startsWith("Saga")) {
|
||||||
// Saga there doesn't need Max value anymore?
|
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final String[] abs = k[2].split(",");
|
final List<String> abs = Arrays.asList(k[2].split(","));
|
||||||
|
if (abs.size() != Integer.valueOf(k[1])) {
|
||||||
|
throw new RuntimeException("Saga max differ from Ability amount");
|
||||||
|
}
|
||||||
|
|
||||||
int i = 1;
|
int idx = 0;
|
||||||
for (String ab : abs) {
|
int skipId = 0;
|
||||||
|
for(String ab : abs) {
|
||||||
|
idx += 1;
|
||||||
|
if (idx <= skipId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
skipId = idx + abs.subList(idx - 1, abs.size()).lastIndexOf(ab);
|
||||||
|
StringBuilder desc = new StringBuilder();
|
||||||
|
for (int i = idx; i <= skipId; i++) {
|
||||||
|
if (i != idx) {
|
||||||
|
desc.append(", ");
|
||||||
|
}
|
||||||
|
desc.append(TextUtil.toRoman(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = idx; i <= skipId; i++) {
|
||||||
SpellAbility sa = AbilityFactory.getAbility(card, ab);
|
SpellAbility sa = AbilityFactory.getAbility(card, ab);
|
||||||
sa.setChapter(i);
|
sa.setChapter(i);
|
||||||
|
|
||||||
// TODO better logic for Roman numbers
|
StringBuilder trigStr = new StringBuilder("Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield");
|
||||||
// In the Description try to merge Chapter trigger with the Same Effect
|
trigStr.append("| CounterType$ LORE | CounterAmount$ EQ").append(i);
|
||||||
String trigStr = "Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield"
|
if (i != idx) {
|
||||||
+ "| CounterType$ LORE | CounterAmount$ EQ" + i
|
trigStr.append(" | Secondary$ True");
|
||||||
+ "| TriggerDescription$ " + Strings.repeat("I", i) + " - " + sa.getDescription();
|
}
|
||||||
final Trigger t = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
trigStr.append("| TriggerDescription$ ").append(desc).append(" — ").append(sa.getDescription());
|
||||||
|
final Trigger t = TriggerHandler.parseTrigger(trigStr.toString(), card, intrinsic);
|
||||||
t.setOverridingAbility(sa);
|
t.setOverridingAbility(sa);
|
||||||
inst.addTrigger(t);
|
inst.addTrigger(t);
|
||||||
++i;
|
}
|
||||||
}
|
}
|
||||||
} else if (keyword.equals("Soulbond")) {
|
} else if (keyword.equals("Soulbond")) {
|
||||||
// Setup ETB trigger for card with Soulbond keyword
|
// Setup ETB trigger for card with Soulbond keyword
|
||||||
|
|||||||
@@ -882,6 +882,10 @@ public class Combat {
|
|||||||
return true; // is blocking something at the moment
|
return true; // is blocking something at the moment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!blocker.isLKI()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CombatLki lki = lkiCache.get(blocker);
|
CombatLki lki = lkiCache.get(blocker);
|
||||||
return null != lki && !lki.isAttacker; // was blocking something anyway
|
return null != lki && !lki.isAttacker; // was blocking something anyway
|
||||||
}
|
}
|
||||||
@@ -893,6 +897,10 @@ public class Combat {
|
|||||||
return true; // is blocking the attacker's band at the moment
|
return true; // is blocking the attacker's band at the moment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!blocker.isLKI()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CombatLki lki = lkiCache.get(blocker);
|
CombatLki lki = lkiCache.get(blocker);
|
||||||
return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band
|
return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mana.addsCounters(sa)) {
|
if (mana.addsCounters(sa)) {
|
||||||
mana.getManaAbility().createETBCounters(host);
|
mana.getManaAbility().createETBCounters(host, this.owner);
|
||||||
}
|
}
|
||||||
if (mana.triggersWhenSpent()) {
|
if (mana.triggersWhenSpent()) {
|
||||||
mana.getManaAbility().addTriggersWhenSpent(sa, host);
|
mana.getManaAbility().addTriggersWhenSpent(sa, host);
|
||||||
|
|||||||
@@ -19,9 +19,11 @@ package forge.game.spellability;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.card.ColorSet;
|
import forge.card.ColorSet;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.card.mana.ManaAtom;
|
import forge.card.mana.ManaAtom;
|
||||||
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -34,6 +36,8 @@ import forge.game.replacement.*;
|
|||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerHandler;
|
import forge.game.trigger.TriggerHandler;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
|
import forge.util.Lang;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -227,10 +231,26 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
/**
|
/**
|
||||||
* createETBCounters
|
* createETBCounters
|
||||||
*/
|
*/
|
||||||
public void createETBCounters(Card c) {
|
public void createETBCounters(Card c, Player controller) {
|
||||||
String[] parse = this.addsCounters.split("_");
|
String[] parse = this.addsCounters.split("_");
|
||||||
// Convert random SVars if there are other cards with this effect
|
// Convert random SVars if there are other cards with this effect
|
||||||
if (c.isValid(parse[0], c.getController(), c, null)) {
|
if (c.isValid(parse[0], c.getController(), c, null)) {
|
||||||
|
final Game game = this.sourceCard.getGame();
|
||||||
|
final Card eff = new Card(game.nextCardId(), game);
|
||||||
|
eff.setTimestamp(game.getNextTimestamp());
|
||||||
|
eff.setName(sourceCard.getName() + "'s Effect");
|
||||||
|
eff.addType("Effect");
|
||||||
|
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
|
||||||
|
eff.setOwner(controller);
|
||||||
|
|
||||||
|
eff.setImageKey(sourceCard.getImageKey());
|
||||||
|
eff.setColor(MagicColor.COLORLESS);
|
||||||
|
eff.setImmutable(true);
|
||||||
|
// try to get the SpellAbility from the mana ability
|
||||||
|
//eff.setEffectSource((SpellAbility)null);
|
||||||
|
|
||||||
|
eff.addRemembered(c);
|
||||||
|
|
||||||
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ " + parse[1]
|
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ " + parse[1]
|
||||||
+ " | ETB$ True | CounterNum$ " + parse[2];
|
+ " | ETB$ True | CounterNum$ " + parse[2];
|
||||||
|
|
||||||
@@ -240,15 +260,37 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
}
|
}
|
||||||
CardFactoryUtil.setupETBReplacementAbility(sa);
|
CardFactoryUtil.setupETBReplacementAbility(sa);
|
||||||
|
|
||||||
String repeffstr = "Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield "
|
String desc = "It enters the battlefield with ";
|
||||||
+ " | Secondary$ True | Description$ CARDNAME"
|
desc += Lang.nounWithNumeral(parse[2], CounterType.valueOf(parse[1]).getName() + " counter");
|
||||||
+ " enters the battlefield with " + CounterType.valueOf(parse[1]).getName() + " counters.";
|
desc += " on it.";
|
||||||
|
|
||||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, c, false);
|
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
||||||
|
|
||||||
|
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||||
re.setLayer(ReplacementLayer.Other);
|
re.setLayer(ReplacementLayer.Other);
|
||||||
re.setOverridingAbility(sa);
|
re.setOverridingAbility(sa);
|
||||||
|
|
||||||
c.addReplacementEffect(re);
|
eff.addReplacementEffect(re);
|
||||||
|
|
||||||
|
// Forgot Trigger
|
||||||
|
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
|
||||||
|
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||||
|
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||||
|
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||||
|
|
||||||
|
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
|
||||||
|
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
|
||||||
|
saForget.setSubAbility(saExile);
|
||||||
|
|
||||||
|
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
|
||||||
|
parsedTrigger.setOverridingAbility(saForget);
|
||||||
|
eff.addTrigger(parsedTrigger);
|
||||||
|
eff.updateStateForView();
|
||||||
|
|
||||||
|
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||||
|
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||||
|
game.getAction().moveTo(ZoneType.Command, eff, null);
|
||||||
|
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1299,6 +1299,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
String announce = getParam("Announce");
|
String announce = getParam("Announce");
|
||||||
if (StringUtils.isBlank(announce)) {
|
if (StringUtils.isBlank(announce)) {
|
||||||
mapParams.put("Announce", variable);
|
mapParams.put("Announce", variable);
|
||||||
|
originalMapParams.put("Announce", variable);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String[] announcedOnes = TextUtil.split(announce, ',');
|
String[] announcedOnes = TextUtil.split(announce, ',');
|
||||||
@@ -1308,6 +1309,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mapParams.put("Announce", announce + ";" + variable);
|
mapParams.put("Announce", announce + ";" + variable);
|
||||||
|
originalMapParams.put("Announce", announce + ";" + variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isXCost() {
|
public boolean isXCost() {
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ public class TrackableTypes {
|
|||||||
if (newCollection != null) {
|
if (newCollection != null) {
|
||||||
//swap in objects in old collection for objects in new collection
|
//swap in objects in old collection for objects in new collection
|
||||||
for (int i = 0; i < newCollection.size(); i++) {
|
for (int i = 0; i < newCollection.size(); i++) {
|
||||||
|
try {
|
||||||
T newObj = newCollection.get(i);
|
T newObj = newCollection.get(i);
|
||||||
if (newObj != null) {
|
if (newObj != null) {
|
||||||
T existingObj = from.getTracker().getObj(itemType, newObj.getId());
|
T existingObj = from.getTracker().getObj(itemType, newObj.getId());
|
||||||
@@ -112,6 +113,9 @@ public class TrackableTypes {
|
|||||||
from.getTracker().putObj(itemType, newObj.getId(), newObj);
|
from.getTracker().putObj(itemType, newObj.getId(), newObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
System.err.println("got an IndexOutOfBoundsException, trying to continue ...");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
to.set(prop, newCollection);
|
to.set(prop, newCollection);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="19"
|
android:minSdkVersion="19"
|
||||||
android:targetSdkVersion="21" />
|
android:targetSdkVersion="26" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- This one needs Android Runtime Permission for Android 6+ -->
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- This one needs Android Runtime Permission for Android 6+ -->
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|||||||
@@ -142,7 +142,7 @@
|
|||||||
<debug>true</debug>
|
<debug>true</debug>
|
||||||
</sign>
|
</sign>
|
||||||
<sdk>
|
<sdk>
|
||||||
<platform>25</platform>
|
<platform>26</platform>
|
||||||
</sdk>
|
</sdk>
|
||||||
<dexForceJumbo>true</dexForceJumbo>
|
<dexForceJumbo>true</dexForceJumbo>
|
||||||
<androidManifestFile>${project.basedir}/AndroidManifest.xml</androidManifestFile>
|
<androidManifestFile>${project.basedir}/AndroidManifest.xml</androidManifestFile>
|
||||||
@@ -183,7 +183,7 @@
|
|||||||
<debug>false</debug>
|
<debug>false</debug>
|
||||||
</sign>
|
</sign>
|
||||||
<sdk>
|
<sdk>
|
||||||
<platform>25</platform>
|
<platform>26</platform>
|
||||||
</sdk>
|
</sdk>
|
||||||
<zipalign>
|
<zipalign>
|
||||||
<verbose>false</verbose>
|
<verbose>false</verbose>
|
||||||
|
|||||||
@@ -9,4 +9,4 @@
|
|||||||
|
|
||||||
# Project target.
|
# Project target.
|
||||||
project.type=0
|
project.type=0
|
||||||
target=android-20
|
target=android-26
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#f0f0f0</color>
|
<color name="ic_launcher_background">#ffffff</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -103,7 +103,7 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
|
|||||||
case TinyLeaders:
|
case TinyLeaders:
|
||||||
allSections.add(DeckSection.Commander);
|
allSections.add(DeckSection.Commander);
|
||||||
|
|
||||||
commanderFilter = CardRulesPredicates.Presets.CAN_BE_COMMANDER;
|
commanderFilter = CardRulesPredicates.Presets.CAN_BE_TINY_LEADERS_COMMANDER;
|
||||||
commanderPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(Predicates.compose(commanderFilter, PaperCard.FN_GET_RULES)), PaperCard.class);
|
commanderPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(Predicates.compose(commanderFilter, PaperCard.FN_GET_RULES)), PaperCard.class);
|
||||||
normalPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(), PaperCard.class);
|
normalPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(), PaperCard.class);
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ public enum CSubmenuDownloaders implements ICDoc {
|
|||||||
VSubmenuDownloaders.SINGLETON_INSTANCE.showLicensing();
|
VSubmenuDownloaders.SINGLETON_INSTANCE.showLicensing();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private final UiCommand cmdCheckForUpdates = new UiCommand() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
new AutoUpdater(false).attemptToUpdate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final UiCommand cmdPicDownload = new UiCommand() {
|
private final UiCommand cmdPicDownload = new UiCommand() {
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
new GuiDownloader(new GuiDownloadPicturesLQ()).show();
|
new GuiDownloader(new GuiDownloadPicturesLQ()).show();
|
||||||
@@ -84,6 +91,7 @@ public enum CSubmenuDownloaders implements ICDoc {
|
|||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
final VSubmenuDownloaders view = VSubmenuDownloaders.SINGLETON_INSTANCE;
|
final VSubmenuDownloaders view = VSubmenuDownloaders.SINGLETON_INSTANCE;
|
||||||
|
view.setCheckForUpdatesCommand(cmdCheckForUpdates);
|
||||||
view.setDownloadPicsCommand(cmdPicDownload);
|
view.setDownloadPicsCommand(cmdPicDownload);
|
||||||
view.setDownloadPicsHQCommand(cmdPicDownloadHQ);
|
view.setDownloadPicsHQCommand(cmdPicDownloadHQ);
|
||||||
view.setDownloadSetPicsCommand(cmdSetDownload);
|
view.setDownloadSetPicsCommand(cmdSetDownload);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package forge.screens.home.settings;
|
|||||||
import forge.*;
|
import forge.*;
|
||||||
import forge.ai.AiProfileUtil;
|
import forge.ai.AiProfileUtil;
|
||||||
import forge.control.FControl.CloseAction;
|
import forge.control.FControl.CloseAction;
|
||||||
|
import forge.download.AutoUpdater;
|
||||||
import forge.game.GameLogEntryType;
|
import forge.game.GameLogEntryType;
|
||||||
import forge.gui.framework.FScreen;
|
import forge.gui.framework.FScreen;
|
||||||
import forge.gui.framework.ICDoc;
|
import forge.gui.framework.ICDoc;
|
||||||
@@ -225,6 +226,7 @@ public enum CSubmenuPreferences implements ICDoc {
|
|||||||
initializeGameLogVerbosityComboBox();
|
initializeGameLogVerbosityComboBox();
|
||||||
initializeCloseActionComboBox();
|
initializeCloseActionComboBox();
|
||||||
initializeDefaultFontSizeComboBox();
|
initializeDefaultFontSizeComboBox();
|
||||||
|
initializeAutoUpdaterComboBox();
|
||||||
initializeMulliganRuleComboBox();
|
initializeMulliganRuleComboBox();
|
||||||
initializeAiProfilesComboBox();
|
initializeAiProfilesComboBox();
|
||||||
initializeStackAdditionsComboBox();
|
initializeStackAdditionsComboBox();
|
||||||
@@ -378,6 +380,16 @@ public enum CSubmenuPreferences implements ICDoc {
|
|||||||
panel.setComboBox(comboBox, selectedItem);
|
panel.setComboBox(comboBox, selectedItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initializeAutoUpdaterComboBox() {
|
||||||
|
// TODO: Ideally we would filter out update paths based on the type of Forge people have
|
||||||
|
final String[] updatePaths = AutoUpdater.updateChannels;
|
||||||
|
final FPref updatePreference = FPref.AUTO_UPDATE;
|
||||||
|
final FComboBoxPanel<String> panel = this.view.getCbpAutoUpdater();
|
||||||
|
final FComboBox<String> comboBox = createComboBox(updatePaths, updatePreference);
|
||||||
|
final String selectedItem = this.prefs.getPref(updatePreference);
|
||||||
|
panel.setComboBox(comboBox, selectedItem);
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeMulliganRuleComboBox() {
|
private void initializeMulliganRuleComboBox() {
|
||||||
final String [] choices = MulliganDefs.getMulliganRuleNames();
|
final String [] choices = MulliganDefs.getMulliganRuleNames();
|
||||||
final FPref userSetting = FPref.MULLIGAN_RULE;
|
final FPref userSetting = FPref.MULLIGAN_RULE;
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
|||||||
private final JPanel pnlContent = new JPanel(new MigLayout("insets 0, gap 0, wrap, ay center"));
|
private final JPanel pnlContent = new JPanel(new MigLayout("insets 0, gap 0, wrap, ay center"));
|
||||||
private final FScrollPane scrContent = new FScrollPane(pnlContent, false);
|
private final FScrollPane scrContent = new FScrollPane(pnlContent, false);
|
||||||
|
|
||||||
|
private final FLabel btnCheckForUpdates = _makeButton(localizer.getMessage("btnCheckForUpdates"));
|
||||||
private final FLabel btnDownloadSetPics = _makeButton(localizer.getMessage("btnDownloadSetPics"));
|
private final FLabel btnDownloadSetPics = _makeButton(localizer.getMessage("btnDownloadSetPics"));
|
||||||
private final FLabel btnDownloadPics = _makeButton(localizer.getMessage("btnDownloadPics"));
|
private final FLabel btnDownloadPics = _makeButton(localizer.getMessage("btnDownloadPics"));
|
||||||
private final FLabel btnDownloadPicsHQ = _makeButton(localizer.getMessage("btnDownloadPicsHQ"));
|
private final FLabel btnDownloadPicsHQ = _makeButton(localizer.getMessage("btnDownloadPicsHQ"));
|
||||||
@@ -80,6 +81,9 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
|||||||
|
|
||||||
if (javaRecentEnough()) {
|
if (javaRecentEnough()) {
|
||||||
|
|
||||||
|
pnlContent.add(btnCheckForUpdates, constraintsBTN);
|
||||||
|
pnlContent.add(_makeLabel(localizer.getMessage("lblCheckForUpdates")), constraintsLBL);
|
||||||
|
|
||||||
pnlContent.add(btnDownloadPics, constraintsBTN);
|
pnlContent.add(btnDownloadPics, constraintsBTN);
|
||||||
pnlContent.add(_makeLabel(localizer.getMessage("lblDownloadPics")), constraintsLBL);
|
pnlContent.add(_makeLabel(localizer.getMessage("lblDownloadPics")), constraintsLBL);
|
||||||
|
|
||||||
@@ -162,6 +166,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
|||||||
return EMenuGroup.SETTINGS;
|
return EMenuGroup.SETTINGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCheckForUpdatesCommand(UiCommand command) { btnCheckForUpdates.setCommand(command); }
|
||||||
public void setDownloadPicsCommand(UiCommand command) { btnDownloadPics.setCommand(command); }
|
public void setDownloadPicsCommand(UiCommand command) { btnDownloadPics.setCommand(command); }
|
||||||
public void setDownloadPicsHQCommand(UiCommand command) { btnDownloadPicsHQ.setCommand(command); }
|
public void setDownloadPicsHQCommand(UiCommand command) { btnDownloadPicsHQ.setCommand(command); }
|
||||||
public void setDownloadSetPicsCommand(UiCommand command) { btnDownloadSetPics.setCommand(command); }
|
public void setDownloadSetPicsCommand(UiCommand command) { btnDownloadSetPics.setCommand(command); }
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import java.awt.event.FocusAdapter;
|
|||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.KeyAdapter;
|
import java.awt.event.KeyAdapter;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.util.*;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,6 +123,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
|||||||
private final FComboBoxPanel<String> cbpCounterDisplayLocation =new FComboBoxPanel<>(localizer.getMessage("cbpCounterDisplayLocation")+":");
|
private final FComboBoxPanel<String> cbpCounterDisplayLocation =new FComboBoxPanel<>(localizer.getMessage("cbpCounterDisplayLocation")+":");
|
||||||
private final FComboBoxPanel<String> cbpGraveyardOrdering = new FComboBoxPanel<>(localizer.getMessage("cbpGraveyardOrdering")+":");
|
private final FComboBoxPanel<String> cbpGraveyardOrdering = new FComboBoxPanel<>(localizer.getMessage("cbpGraveyardOrdering")+":");
|
||||||
private final FComboBoxPanel<String> cbpDefaultLanguage = new FComboBoxPanel<>(localizer.getMessage("cbpSelectLanguage")+":");
|
private final FComboBoxPanel<String> cbpDefaultLanguage = new FComboBoxPanel<>(localizer.getMessage("cbpSelectLanguage")+":");
|
||||||
|
private final FComboBoxPanel<String> cbpAutoUpdater = new FComboBoxPanel<>(localizer.getMessage("cbpAutoUpdater")+":");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@@ -157,6 +158,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
|||||||
pnlPrefs.add(new SectionLabel(localizer.getMessage("GeneralConfiguration")), sectionConstraints);
|
pnlPrefs.add(new SectionLabel(localizer.getMessage("GeneralConfiguration")), sectionConstraints);
|
||||||
|
|
||||||
// language
|
// language
|
||||||
|
|
||||||
|
pnlPrefs.add(cbpAutoUpdater, comboBoxConstraints);
|
||||||
|
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlAutoUpdater")), descriptionConstraints);
|
||||||
|
|
||||||
pnlPrefs.add(cbpDefaultLanguage, comboBoxConstraints);
|
pnlPrefs.add(cbpDefaultLanguage, comboBoxConstraints);
|
||||||
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlSelectLanguage")), descriptionConstraints);
|
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlSelectLanguage")), descriptionConstraints);
|
||||||
|
|
||||||
@@ -531,6 +536,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final FComboBoxPanel<String> getCbpAutoUpdater() {
|
||||||
|
return cbpAutoUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return {@link javax.swing.JCheckBox} */
|
/** @return {@link javax.swing.JCheckBox} */
|
||||||
public final JCheckBox getCbCompactMainMenu() {
|
public final JCheckBox getCbCompactMainMenu() {
|
||||||
return cbCompactMainMenu;
|
return cbCompactMainMenu;
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public final class Main {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
System.out.println("Unknown mode.\nKnown mode is 'sim' ");
|
System.out.println("Unknown mode.\nKnown mode is 'sim', 'parse' ");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ public class FSkin {
|
|||||||
textures.put(f6.path(), textures.get(f3.path()));
|
textures.put(f6.path(), textures.get(f3.path()));
|
||||||
}
|
}
|
||||||
if (f7.exists()){
|
if (f7.exists()){
|
||||||
Texture t = new Texture(f7, false);
|
Texture t = new Texture(f7, true);
|
||||||
//t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
//t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||||
textures.put(f7.path(), t);
|
textures.put(f7.path(), t);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,6 +193,15 @@ public enum FSkinImage implements FImage {
|
|||||||
QUEST_BIG_SWORD (FSkinProp.ICO_QUEST_BIG_SWORD, SourceFile.ICONS),
|
QUEST_BIG_SWORD (FSkinProp.ICO_QUEST_BIG_SWORD, SourceFile.ICONS),
|
||||||
QUEST_BIG_BAG (FSkinProp.ICO_QUEST_BIG_BAG, SourceFile.ICONS),
|
QUEST_BIG_BAG (FSkinProp.ICO_QUEST_BIG_BAG, SourceFile.ICONS),
|
||||||
|
|
||||||
|
//menu icon
|
||||||
|
MENU_GALAXY (FSkinProp.ICO_MENU_GALAXY, SourceFile.ICONS),
|
||||||
|
MENU_STATS (FSkinProp.ICO_MENU_STATS, SourceFile.ICONS),
|
||||||
|
MENU_PUZZLE (FSkinProp.ICO_MENU_PUZZLE, SourceFile.ICONS),
|
||||||
|
MENU_GAUNTLET (FSkinProp.ICO_MENU_GAUNTLET, SourceFile.ICONS),
|
||||||
|
MENU_SEALED (FSkinProp.ICO_MENU_SEALED, SourceFile.ICONS),
|
||||||
|
MENU_DRAFT (FSkinProp.ICO_MENU_DRAFT, SourceFile.ICONS),
|
||||||
|
MENU_CONSTRUCTED (FSkinProp.ICO_MENU_CONSTRUCTED, SourceFile.ICONS),
|
||||||
|
|
||||||
//Interface icons
|
//Interface icons
|
||||||
QUESTION (FSkinProp.ICO_QUESTION, SourceFile.ICONS),
|
QUESTION (FSkinProp.ICO_QUESTION, SourceFile.ICONS),
|
||||||
INFORMATION (FSkinProp.ICO_INFORMATION, SourceFile.ICONS),
|
INFORMATION (FSkinProp.ICO_INFORMATION, SourceFile.ICONS),
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static forge.card.CardRenderer.CROP_MULTIPLIER;
|
||||||
|
import static forge.card.CardRenderer.isModernFrame;
|
||||||
|
|
||||||
public class CardImageRenderer {
|
public class CardImageRenderer {
|
||||||
private static final float BASE_IMAGE_WIDTH = 360;
|
private static final float BASE_IMAGE_WIDTH = 360;
|
||||||
private static final float BASE_IMAGE_HEIGHT = 504;
|
private static final float BASE_IMAGE_HEIGHT = 504;
|
||||||
@@ -357,13 +360,19 @@ public class CardImageRenderer {
|
|||||||
float new_yRotate = (dispH - new_w) /2;
|
float new_yRotate = (dispH - new_w) /2;
|
||||||
boolean rotateSplit = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS);
|
boolean rotateSplit = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS);
|
||||||
boolean rotatePlane = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON);
|
boolean rotatePlane = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON);
|
||||||
|
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
|
||||||
|
float minusxy = isModernFrame(card) ? 0.0f : 0.13f*radius;
|
||||||
|
if (card.getCurrentState().getSetCode().equals("LEA")||card.getCurrentState().getSetCode().equals("LEB")) {
|
||||||
|
croppedArea = 0.975f;
|
||||||
|
minusxy = 0.135f*radius;
|
||||||
|
}
|
||||||
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) {
|
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) {
|
||||||
if (Forge.enableUIMask){
|
if (Forge.enableUIMask){
|
||||||
if (ImageCache.isExtendedArt(card))
|
if (ImageCache.isExtendedArt(card))
|
||||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||||
else {
|
else {
|
||||||
g.drawRotatedImage(FSkin.getBorders().get(0), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
g.drawRotatedImage(FSkin.getBorders().get(0), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x+radius/2, new_y+radius/2, new_w*0.96f, new_h*0.96f, (new_x+radius/2) + (new_w*0.96f) / 2, (new_y+radius/2) + (new_h*0.96f) / 2, -90);
|
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x+radius/2-minusxy, new_y+radius/2-minusxy, new_w*croppedArea, new_h*croppedArea, (new_x+radius/2-minusxy) + (new_w*croppedArea) / 2, (new_y+radius/2-minusxy) + (new_h*croppedArea) / 2, -90);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||||
@@ -374,7 +383,7 @@ public class CardImageRenderer {
|
|||||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||||
else {
|
else {
|
||||||
g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x + radius / 2, new_y + radius / 2, new_w * 0.96f, new_h * 0.96f, (new_x + radius / 2) + (new_w * 0.96f) / 2, (new_y + radius / 2) + (new_h * 0.96f) / 2, isAftermath ? 90 : -90);
|
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x + radius / 2-minusxy, new_y + radius / 2-minusxy, new_w * croppedArea, new_h * croppedArea, (new_x + radius / 2-minusxy) + (new_w * croppedArea) / 2, (new_y + radius / 2-minusxy) + (new_h * croppedArea) / 2, isAftermath ? 90 : -90);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||||
@@ -384,7 +393,7 @@ public class CardImageRenderer {
|
|||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
g.drawImage(ImageCache.getBorderImage(card, canshow), x, y, w, h);
|
g.drawImage(ImageCache.getBorderImage(card, canshow), x, y, w, h);
|
||||||
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (canshow && !ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE).equals(card.getState(altState).getImageKey()))
|
if (canshow && !ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE).equals(card.getState(altState).getImageKey()))
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ public class CardRenderer {
|
|||||||
private static final float NAME_COST_THRESHOLD = Utils.scale(200);
|
private static final float NAME_COST_THRESHOLD = Utils.scale(200);
|
||||||
private static final float BORDER_THICKNESS = Utils.scale(1);
|
private static final float BORDER_THICKNESS = Utils.scale(1);
|
||||||
public static final float PADDING_MULTIPLIER = 0.021f;
|
public static final float PADDING_MULTIPLIER = 0.021f;
|
||||||
|
public static final float CROP_MULTIPLIER = 0.96f;
|
||||||
|
|
||||||
private static Map<Integer, BitmapFont> counterFonts = new HashMap<>();
|
private static Map<Integer, BitmapFont> counterFonts = new HashMap<>();
|
||||||
private static final Color counterBackgroundColor = new Color(0f, 0f, 0f, 0.9f);
|
private static final Color counterBackgroundColor = new Color(0f, 0f, 0f, 0.9f);
|
||||||
@@ -142,6 +143,49 @@ public class CardRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isModernFrame(IPaperCard c) {
|
||||||
|
if (c == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
CardEdition ed = FModel.getMagicDb().getEditions().get(c.getEdition());
|
||||||
|
if (ed != null) {
|
||||||
|
switch (ed.getCode()) {
|
||||||
|
case "MED":
|
||||||
|
case "ME2":
|
||||||
|
case "ME3":
|
||||||
|
case "ME4":
|
||||||
|
case "TSB":
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return ed.isModern();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isModernFrame(CardView c) {
|
||||||
|
if (c == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
CardView.CardStateView state = c.getCurrentState();
|
||||||
|
CardEdition ed = FModel.getMagicDb().getEditions().get(state.getSetCode());
|
||||||
|
if (ed != null) {
|
||||||
|
switch (ed.getCode()) {
|
||||||
|
case "MED":
|
||||||
|
case "ME2":
|
||||||
|
case "ME3":
|
||||||
|
case "ME4":
|
||||||
|
case "TSB":
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return ed.isModern();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static float getCardListItemHeight(boolean compactMode) {
|
public static float getCardListItemHeight(boolean compactMode) {
|
||||||
if (compactMode) {
|
if (compactMode) {
|
||||||
return MANA_SYMBOL_SIZE + 2 * FList.PADDING;
|
return MANA_SYMBOL_SIZE + 2 * FList.PADDING;
|
||||||
@@ -402,7 +446,12 @@ public class CardRenderer {
|
|||||||
public static void drawCard(Graphics g, IPaperCard pc, float x, float y, float w, float h, CardStackPosition pos) {
|
public static void drawCard(Graphics g, IPaperCard pc, float x, float y, float w, float h, CardStackPosition pos) {
|
||||||
Texture image = new RendererCachedCardImage(pc, false).getImage();
|
Texture image = new RendererCachedCardImage(pc, false).getImage();
|
||||||
float radius = (h - w)/8;
|
float radius = (h - w)/8;
|
||||||
|
float croppedArea = isModernFrame(pc) ? CROP_MULTIPLIER : 0.97f;
|
||||||
|
float minusxy = isModernFrame(pc) ? 0.0f : 0.13f*radius;
|
||||||
|
if (pc.getEdition().equals("LEA")||pc.getEdition().equals("LEB")) {
|
||||||
|
croppedArea = 0.975f;
|
||||||
|
minusxy = 0.135f*radius;
|
||||||
|
}
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
if (image == ImageCache.defaultImage) {
|
if (image == ImageCache.defaultImage) {
|
||||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
|
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
|
||||||
@@ -413,7 +462,7 @@ public class CardRenderer {
|
|||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
g.drawImage(ImageCache.getBorderImage(pc), x, y, w, h);
|
g.drawImage(ImageCache.getBorderImage(pc), x, y, w, h);
|
||||||
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
@@ -437,7 +486,12 @@ public class CardRenderer {
|
|||||||
Texture image = new RendererCachedCardImage(card, false).getImage();
|
Texture image = new RendererCachedCardImage(card, false).getImage();
|
||||||
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
|
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
|
||||||
float radius = (h - w)/8;
|
float radius = (h - w)/8;
|
||||||
|
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
|
||||||
|
float minusxy = isModernFrame(card) ? 0.0f : 0.13f*radius;
|
||||||
|
if (card.getCurrentState().getSetCode().equals("LEA")||card.getCurrentState().getSetCode().equals("LEB")) {
|
||||||
|
croppedArea = 0.975f;
|
||||||
|
minusxy = 0.135f*radius;
|
||||||
|
}
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
if (image == ImageCache.defaultImage) {
|
if (image == ImageCache.defaultImage) {
|
||||||
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
|
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
|
||||||
@@ -450,7 +504,7 @@ public class CardRenderer {
|
|||||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||||
else {
|
else {
|
||||||
g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90);
|
g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), x+radius/2.3f, y+radius/2, w*0.96f, h*0.96f, (x+radius/2.3f) + (w*0.96f) / 2, (y+radius/2) + (h*0.96f) / 2, -90);
|
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), x+radius/2.3f-minusxy, y+radius/2-minusxy, w*croppedArea, h*croppedArea, (x+radius/2.3f-minusxy) + (w*croppedArea) / 2, (y+radius/2-minusxy) + (h*croppedArea) / 2, -90);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||||
@@ -461,7 +515,7 @@ public class CardRenderer {
|
|||||||
else {
|
else {
|
||||||
boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
|
boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
|
||||||
g.drawBorderImage(ImageCache.getBorderImage(card, canshow), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors
|
g.drawBorderImage(ImageCache.getBorderImage(card, canshow), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors
|
||||||
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (canshow)
|
if (canshow)
|
||||||
@@ -1119,8 +1173,14 @@ public class CardRenderer {
|
|||||||
|
|
||||||
public static void drawFoilEffect(Graphics g, CardView card, float x, float y, float w, float h, boolean inZoomer) {
|
public static void drawFoilEffect(Graphics g, CardView card, float x, float y, float w, float h, boolean inZoomer) {
|
||||||
float new_x = x; float new_y = y; float new_w = w; float new_h = h; float radius = (h - w)/8;
|
float new_x = x; float new_y = y; float new_w = w; float new_h = h; float radius = (h - w)/8;
|
||||||
|
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
|
||||||
|
float minusxy = isModernFrame(card) ? 0.0f : 0.13f*radius;
|
||||||
|
if (card.getCurrentState().getSetCode().equals("LEA")||card.getCurrentState().getSetCode().equals("LEB")) {
|
||||||
|
croppedArea = 0.975f;
|
||||||
|
minusxy = 0.135f*radius;
|
||||||
|
}
|
||||||
if (Forge.enableUIMask) {
|
if (Forge.enableUIMask) {
|
||||||
new_x += radius/2.4f; new_y += radius/2; new_w = w * 0.96f; new_h = h * 0.96f;
|
new_x += radius/2.4f-minusxy; new_y += radius/2-minusxy; new_w = w * croppedArea; new_h = h * croppedArea;
|
||||||
}
|
}
|
||||||
if (isPreferenceEnabled(FPref.UI_OVERLAY_FOIL_EFFECT) && MatchController.instance.mayView(card)) {
|
if (isPreferenceEnabled(FPref.UI_OVERLAY_FOIL_EFFECT) && MatchController.instance.mayView(card)) {
|
||||||
boolean rotateSplit = isPreferenceEnabled(FPref.UI_ROTATE_SPLIT_CARDS) && card.isSplitCard() && inZoomer;
|
boolean rotateSplit = isPreferenceEnabled(FPref.UI_ROTATE_SPLIT_CARDS) && card.isSplitCard() && inZoomer;
|
||||||
|
|||||||
@@ -219,7 +219,22 @@ public class CardZoom extends FOverlay {
|
|||||||
float w = getWidth();
|
float w = getWidth();
|
||||||
float h = getHeight();
|
float h = getHeight();
|
||||||
float messageHeight = FDialog.MSG_HEIGHT;
|
float messageHeight = FDialog.MSG_HEIGHT;
|
||||||
float maxCardHeight = h - 2 * messageHeight;
|
float AspectRatioMultiplier = 2;
|
||||||
|
switch (Forge.extrawide) {
|
||||||
|
case "default":
|
||||||
|
AspectRatioMultiplier = 3; //good for tablets with 16:10 or similar
|
||||||
|
break;
|
||||||
|
case "wide":
|
||||||
|
AspectRatioMultiplier = 2.5f;
|
||||||
|
break;
|
||||||
|
case "extrawide":
|
||||||
|
AspectRatioMultiplier = 2; //good for tall phones with 21:9 or similar
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
AspectRatioMultiplier = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
float maxCardHeight = h - AspectRatioMultiplier * messageHeight; //maxheight of currently zoomed card
|
||||||
|
|
||||||
float cardWidth, cardHeight, y;
|
float cardWidth, cardHeight, y;
|
||||||
|
|
||||||
|
|||||||
@@ -856,6 +856,9 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
|||||||
case Brawl:
|
case Brawl:
|
||||||
isLegalCommander = card.getRules().canBeBrawlCommander();
|
isLegalCommander = card.getRules().canBeBrawlCommander();
|
||||||
break;
|
break;
|
||||||
|
case TinyLeaders:
|
||||||
|
isLegalCommander = card.getRules().canBeTinyLeadersCommander();
|
||||||
|
break;
|
||||||
case Oathbreaker:
|
case Oathbreaker:
|
||||||
isLegalCommander = card.getRules().canBeOathbreaker();
|
isLegalCommander = card.getRules().canBeOathbreaker();
|
||||||
captionSuffix = localizer.getMessage("lblOathbreaker");
|
captionSuffix = localizer.getMessage("lblOathbreaker");
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ import forge.util.Localizer;
|
|||||||
|
|
||||||
public class LoadGameMenu extends FPopupMenu {
|
public class LoadGameMenu extends FPopupMenu {
|
||||||
public enum LoadGameScreen {
|
public enum LoadGameScreen {
|
||||||
BoosterDraft("lblBoosterDraft", FSkinImage.HAND, LoadDraftScreen.class),
|
BoosterDraft("lblBoosterDraft", FSkinImage.MENU_DRAFT, LoadDraftScreen.class),
|
||||||
SealedDeck("lblSealedDeck", FSkinImage.PACK, LoadSealedScreen.class),
|
SealedDeck("lblSealedDeck", FSkinImage.MENU_SEALED, LoadSealedScreen.class),
|
||||||
QuestMode("lblQuestMode", FSkinImage.QUEST_ZEP, LoadQuestScreen.class),
|
QuestMode("lblQuestMode", FSkinImage.QUEST_ZEP, LoadQuestScreen.class),
|
||||||
PlanarConquest("lblPlanarConquest", FSkinImage.MULTIVERSE, LoadConquestScreen.class),
|
PlanarConquest("lblPlanarConquest", FSkinImage.MENU_GALAXY, LoadConquestScreen.class),
|
||||||
Gauntlet("lblGauntlet", FSkinImage.ALPHASTRIKE, LoadGauntletScreen.class);
|
Gauntlet("lblGauntlet", FSkinImage.MENU_GAUNTLET, LoadGauntletScreen.class);
|
||||||
|
|
||||||
private final FMenuItem item;
|
private final FMenuItem item;
|
||||||
private final Class<? extends FScreen> screenClass;
|
private final Class<? extends FScreen> screenClass;
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ public class NewGameMenu extends FPopupMenu {
|
|||||||
final static Localizer localizer = Localizer.getInstance();
|
final static Localizer localizer = Localizer.getInstance();
|
||||||
|
|
||||||
public enum NewGameScreen {
|
public enum NewGameScreen {
|
||||||
Constructed(localizer.getMessage("lblConstructed"), FSkinImage.DECKLIST, ConstructedScreen.class),
|
Constructed(localizer.getMessage("lblConstructed"), FSkinImage.MENU_CONSTRUCTED, ConstructedScreen.class),
|
||||||
BoosterDraft(localizer.getMessage("lblBoosterDraft"), FSkinImage.HAND, NewDraftScreen.class),
|
BoosterDraft(localizer.getMessage("lblBoosterDraft"), FSkinImage.MENU_DRAFT, NewDraftScreen.class),
|
||||||
SealedDeck(localizer.getMessage("lblSealedDeck"), FSkinImage.PACK, NewSealedScreen.class),
|
SealedDeck(localizer.getMessage("lblSealedDeck"), FSkinImage.MENU_SEALED, NewSealedScreen.class),
|
||||||
QuestMode(localizer.getMessage("lblQuestMode"), FSkinImage.QUEST_ZEP, NewQuestScreen.class),
|
QuestMode(localizer.getMessage("lblQuestMode"), FSkinImage.QUEST_ZEP, NewQuestScreen.class),
|
||||||
PuzzleMode(localizer.getMessage("lblPuzzleMode"), FSkinImage.QUEST_BOOK, PuzzleScreen.class),
|
PuzzleMode(localizer.getMessage("lblPuzzleMode"), FSkinImage.MENU_PUZZLE, PuzzleScreen.class),
|
||||||
PlanarConquest(localizer.getMessage("lblPlanarConquest"), FSkinImage.MULTIVERSE, NewConquestScreen.class),
|
PlanarConquest(localizer.getMessage("lblPlanarConquest"), FSkinImage.MENU_GALAXY, NewConquestScreen.class),
|
||||||
Gauntlet(localizer.getMessage("lblGauntlet"), FSkinImage.ALPHASTRIKE, NewGauntletScreen.class);
|
Gauntlet(localizer.getMessage("lblGauntlet"), FSkinImage.MENU_GAUNTLET, NewGauntletScreen.class);
|
||||||
|
|
||||||
private final FMenuItem item;
|
private final FMenuItem item;
|
||||||
private final Class<? extends FScreen> screenClass;
|
private final Class<? extends FScreen> screenClass;
|
||||||
|
|||||||
@@ -54,9 +54,10 @@ public class PuzzleScreen extends LaunchScreen {
|
|||||||
final ArrayList<Puzzle> puzzles = PuzzleIO.loadPuzzles();
|
final ArrayList<Puzzle> puzzles = PuzzleIO.loadPuzzles();
|
||||||
Collections.sort(puzzles);
|
Collections.sort(puzzles);
|
||||||
|
|
||||||
GuiChoose.one(Localizer.getInstance().getMessage("lblChooseAPuzzle"), puzzles, new Callback<Puzzle>() {
|
GuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblChooseAPuzzle"), puzzles, new Callback<Puzzle>() {
|
||||||
@Override
|
@Override
|
||||||
public void run(final Puzzle chosen) {
|
public void run(final Puzzle chosen) {
|
||||||
|
if (chosen != null) {
|
||||||
LoadingOverlay.show(Localizer.getInstance().getMessage("lblLoadingThePuzzle"), new Runnable() {
|
LoadingOverlay.show(Localizer.getInstance().getMessage("lblLoadingThePuzzle"), new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -69,6 +70,13 @@ public class PuzzleScreen extends LaunchScreen {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
hostedMatch.setEndGameHook((new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
chosen.savePuzzleSolve(hostedMatch.getGame().getOutcome().isWinner(GamePlayerUtil.getGuiPlayer()));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
final List<RegisteredPlayer> players = new ArrayList<>();
|
final List<RegisteredPlayer> players = new ArrayList<>();
|
||||||
final RegisteredPlayer human = new RegisteredPlayer(new Deck()).setPlayer(GamePlayerUtil.getGuiPlayer());
|
final RegisteredPlayer human = new RegisteredPlayer(new Deck()).setPlayer(GamePlayerUtil.getGuiPlayer());
|
||||||
human.setStartingHand(0);
|
human.setStartingHand(0);
|
||||||
@@ -85,6 +93,7 @@ public class PuzzleScreen extends LaunchScreen {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class ConquestMenu extends FPopupMenu {
|
|||||||
setCurrentScreen(collectionScreen);
|
setCurrentScreen(collectionScreen);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
private static final FMenuItem statsItem = new FMenuItem(Localizer.getInstance().getMessage("lblStatistics"), FSkinImage.HDMULTI, new FEventHandler() {
|
private static final FMenuItem statsItem = new FMenuItem(Localizer.getInstance().getMessage("lblStatistics"), FSkinImage.MENU_STATS, new FEventHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleEvent(FEvent e) {
|
public void handleEvent(FEvent e) {
|
||||||
setCurrentScreen(statsScreen);
|
setCurrentScreen(statsScreen);
|
||||||
|
|||||||
@@ -244,10 +244,7 @@ public class ConquestRewardDialog extends FScrollPane {
|
|||||||
//ensure current card in view
|
//ensure current card in view
|
||||||
if (getScrollHeight() > getHeight() && index < cardCount) {
|
if (getScrollHeight() > getHeight() && index < cardCount) {
|
||||||
CardRevealer currentCard = cardRevealers.get(index);
|
CardRevealer currentCard = cardRevealers.get(index);
|
||||||
if (!Forge.extrawide.equals("default"))
|
|
||||||
scrollIntoView(currentCard, currentCard.getHeight() / (columnCount * PADDING) / 2);
|
scrollIntoView(currentCard, currentCard.getHeight() / (columnCount * PADDING) / 2);
|
||||||
else
|
|
||||||
scrollIntoView(currentCard, currentCard.getHeight() / 2 + PADDING); //show half of the card below
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class QuestMenu extends FPopupMenu implements IVQuestStats {
|
|||||||
setCurrentScreen(bazaarScreen);
|
setCurrentScreen(bazaarScreen);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
private static final FMenuItem statsItem = new FMenuItem(Localizer.getInstance().getMessage("lblStatistics"), FSkinImage.HDMULTI, new FEventHandler() {
|
private static final FMenuItem statsItem = new FMenuItem(Localizer.getInstance().getMessage("lblStatistics"), FSkinImage.MENU_STATS, new FEventHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleEvent(FEvent e) {
|
public void handleEvent(FEvent e) {
|
||||||
setCurrentScreen(statsScreen);
|
setCurrentScreen(statsScreen);
|
||||||
|
|||||||
@@ -84,3 +84,4 @@ Modern Horizons, 3/6/WAR, MH1
|
|||||||
Core Set 2020, 3/6/M20, M20
|
Core Set 2020, 3/6/M20, M20
|
||||||
Throne of Eldraine, 3/6/ELD, ELD
|
Throne of Eldraine, 3/6/ELD, ELD
|
||||||
Theros Beyond Death, 3/6/THB, THB
|
Theros Beyond Death, 3/6/THB, THB
|
||||||
|
Mystery Booster, 3/6/THB, MB1
|
||||||
@@ -4,8 +4,9 @@ Types:Creature Elemental Knight
|
|||||||
PT:4/6
|
PT:4/6
|
||||||
K:Vigilance
|
K:Vigilance
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroy | TriggerDescription$ When CARDNAME enters the battlefield, destroy up to one target nonland permanent. Its controller creates a 3/3 colorless Golem artifact creature token.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroy | TriggerDescription$ When CARDNAME enters the battlefield, destroy up to one target nonland permanent. Its controller creates a 3/3 colorless Golem artifact creature token.
|
||||||
SVar:TrigDestroy:DB$ Destroy | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | SubAbility$ DBToken
|
SVar:TrigDestroy:DB$ Destroy | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | RememberLKI$ True | SubAbility$ DBToken
|
||||||
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_3_3_a_golem | TokenOwner$ TargetedController | LegacyImage$ c 3 3 a golem m20
|
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_3_3_a_golem | TokenOwner$ RememberedController | LegacyImage$ c 3 3 a golem m20 | SubAbility$ DBCleanup
|
||||||
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, return target artifact or enchantment card from your graveyard to your hand.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, return target artifact or enchantment card from your graveyard to your hand.
|
||||||
SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Artifact.YouCtrl,Enchantment.YouCtrl
|
SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Artifact.YouCtrl,Enchantment.YouCtrl
|
||||||
Oracle:Vigilance\nWhen Cavalier of Dawn enters the battlefield, destroy up to one target nonland permanent. Its controller creates a 3/3 colorless Golem artifact creature token.\nWhen Cavalier of Dawn dies, return target artifact or enchantment card from your graveyard to your hand.
|
Oracle:Vigilance\nWhen Cavalier of Dawn enters the battlefield, destroy up to one target nonland permanent. Its controller creates a 3/3 colorless Golem artifact creature token.\nWhen Cavalier of Dawn dies, return target artifact or enchantment card from your graveyard to your hand.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Ghastly Demise
|
Name:Ghastly Demise
|
||||||
ManaCost:B
|
ManaCost:B
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ Destroy | Cost$ B | ValidTgts$ Creature.nonBlack+toughnessLEX | TgtPrompt$ Select target nonblack creature with toughness less than or equal to the number of cards in your graveyard. | References$ X | SpellDescription$ Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard.
|
A:SP$ Destroy | Cost$ B | ValidTgts$ Creature.nonBlack | TgtPrompt$ Select target nonblack creature | ConditionCheckSVar$ Y | ConditionSVarCompare$ LEX | References$ X,Y | StackDescription$ SpellDescription | SpellDescription$ Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard.
|
||||||
|
SVar:Y:Targeted$CardToughness
|
||||||
SVar:X:Count$InYourYard
|
SVar:X:Count$InYourYard
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/ghastly_demise.jpg
|
|
||||||
Oracle:Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard.
|
Oracle:Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Joust
|
Name:Joust
|
||||||
ManaCost:1 R
|
ManaCost:1 R
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ Pump | Cost$ 1 R | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumAtt$ +2 | NumDef$ +1 | ConditionDefined$ ThisTargetedCard | ConditionPresent$ Knight | SubAbility$ DBFight | SpellDescription$ Choose target creature you control and target creature you don't control. The creature you control gets +2/+1 until end of turn if it's a Knight. Then those creatures fight each other. (Each deals damage equal to its power to the other.)
|
A:SP$ Pump | Cost$ 1 R | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumAtt$ +2 | NumDef$ +1 | AILogic$ Fight | ConditionDefined$ ThisTargetedCard | ConditionPresent$ Knight | SubAbility$ DBFight | SpellDescription$ Choose target creature you control and target creature you don't control. The creature you control gets +2/+1 until end of turn if it's a Knight. Then those creatures fight each other. (Each deals damage equal to its power to the other.)
|
||||||
SVar:DBFight:DB$ Fight | Defined$ ParentTarget | ValidTgts$ Creature.YouDontCtrl | AILogic$ Always | TgtPrompt$ Choose target creature you don't control
|
SVar:DBFight:DB$ Fight | Defined$ ParentTarget | ValidTgts$ Creature.YouDontCtrl | AILogic$ Always | TgtPrompt$ Choose target creature you don't control
|
||||||
DeckHints:Type$Knight
|
DeckHints:Type$Knight
|
||||||
Oracle:Choose target creature you control and target creature you don't control. The creature you control gets +2/+1 until end of turn if it's a Knight. Then those creatures fight each other. (Each deals damage equal to its power to the other.)
|
Oracle:Choose target creature you control and target creature you don't control. The creature you control gets +2/+1 until end of turn if it's a Knight. Then those creatures fight each other. (Each deals damage equal to its power to the other.)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
Name:Nissa's Pilgrimage
|
Name:Nissa's Pilgrimage
|
||||||
ManaCost:2 G
|
ManaCost:2 G
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ ChangeZone | Cost$ 2 G | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic+Forest | ChangeNum$ 1 | SubAbility$ DBChangeZone1 | NoShuffle$ True | SpellDescription$ Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library. Spell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two.
|
A:SP$ ChangeZone | Cost$ 2 G | Origin$ Library | Destination$ Library | ChangeType$ Land.Basic+Forest | ChangeNum$ X | References$ X,Y | RememberChanged$ True | SubAbility$ DBBattlefield | Shuffle$ False | StackDescription$ SpellDescription | SpellDescription$ Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library. Spell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two.
|
||||||
SVar:DBChangeZone1:DB$ChangeZone | Origin$ Library | Destination$ Hand | SubAbility$ DBChangeZone2 | ChangeType$ Land.Basic+Forest | ChangeNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ LT2 | References$ X
|
SVar:DBBattlefield:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | Tapped$ True | SubAbility$ DBHand | ChangeType$ Card.IsRemembered | ChangeNum$ 1 | Mandatory$ True | NoLooking$ True | SelectPrompt$ Select a card to go to the battlefield | Shuffle$ False | StackDescription$ None
|
||||||
SVar:DBChangeZone2:DB$ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Basic+Forest | ChangeNum$ 2 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE2 | References$ X
|
SVar:DBHand:DB$ ChangeZone | Origin$ Library | Destination$ Hand | Defined$ Remembered | NoLooking$ True | StackDescription$ None | SubAbility$ DBCleanup
|
||||||
SVar:X:Count$ValidGraveyard Instant.YouOwn,Sorcery.YouOwn
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/nissas_pilgrimage.jpg
|
SVar:X:Count$Compare Y GE2.3.2
|
||||||
|
SVar:Y:Count$ValidGraveyard Instant.YouOwn,Sorcery.YouOwn
|
||||||
Oracle:Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library.\nSpell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two.
|
Oracle:Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library.\nSpell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Settle the Wreckage
|
|||||||
ManaCost:2 W W
|
ManaCost:2 W W
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ ChangeZoneAll | Cost$ 2 W W | ValidTgts$ Player | ChangeType$ Creature.attacking | TgtPrompt$ Select target player | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBGetLands | SpellDescription$ Exile all attacking creatures target player controls. That player may search their library for that many basic lands, put those cards onto the battlefield tapped, then shuffle their library.
|
A:SP$ ChangeZoneAll | Cost$ 2 W W | ValidTgts$ Player | ChangeType$ Creature.attacking | TgtPrompt$ Select target player | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBGetLands | SpellDescription$ Exile all attacking creatures target player controls. That player may search their library for that many basic lands, put those cards onto the battlefield tapped, then shuffle their library.
|
||||||
SVar:DBGetLands:DB$ ChangeZone | Optional$ True | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ X | References$ X | DefinedPlayer$ RememberedController | ShuffleNonMandatory$ True | SubAbility$ DBCleanup
|
SVar:DBGetLands:DB$ ChangeZone | Optional$ True | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ X | References$ X | DefinedPlayer$ TargetedPlayer | ShuffleNonMandatory$ True | SubAbility$ DBCleanup
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
SVar:X:Count$RememberedSize
|
SVar:X:Count$RememberedSize
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/settle_the_wreckage.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/settle_the_wreckage.jpg
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:1 W
|
|||||||
Types:Enchantment Saga
|
Types:Enchantment Saga
|
||||||
K:Saga:3:TrigChange,TrigToken,TrigGainLife
|
K:Saga:3:TrigChange,TrigToken,TrigGainLife
|
||||||
SVar:TrigChange:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Plains+Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library.
|
SVar:TrigChange:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Plains+Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library.
|
||||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_0_4_wall_defender | TokenOwner$ You | LegacyImage$ c 0 4 wall defender thb | SpellDescription$ Create a 0/4 colorless Wall artifact creature token with defender.
|
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_0_4_a_wall_defender | TokenOwner$ You | LegacyImage$ c 0 4 wall defender thb | SpellDescription$ Create a 0/4 colorless Wall artifact creature token with defender.
|
||||||
SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 | SpellDescription$ You gain 2 life.
|
SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 | SpellDescription$ You gain 2 life.
|
||||||
DeckHas:Ability$LifeGain & Ability$Token
|
DeckHas:Ability$LifeGain & Ability$Token
|
||||||
Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI - Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library.\nII - Create a 0/4 colorless Wall artifact creature token with defender.\nIII - You gain 2 life.
|
Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI - Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library.\nII - Create a 0/4 colorless Wall artifact creature token with defender.\nIII - You gain 2 life.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:The Triumph of Anax
|
Name:The Triumph of Anax
|
||||||
ManaCost:2 R
|
ManaCost:2 R
|
||||||
Types:Enchantment Saga
|
Types:Enchantment Saga
|
||||||
K:Saga:3:DBPump,DBPump,DBPump,DBPick
|
K:Saga:4:DBPump,DBPump,DBPump,DBPick
|
||||||
SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +X | References$ X | KW$ Trample | SpellDescription$ Until end of turn, target creature gains trample and gets +X/+0, where X is the number of lore counters on CARDNAME.
|
SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +X | References$ X | KW$ Trample | SpellDescription$ Until end of turn, target creature gains trample and gets +X/+0, where X is the number of lore counters on CARDNAME.
|
||||||
SVar:X:Count$CardCounters.LORE
|
SVar:X:Count$CardCounters.LORE
|
||||||
SVar:PlayMain1:TRUE
|
SVar:PlayMain1:TRUE
|
||||||
|
|||||||
@@ -196,14 +196,14 @@ AdditionalSetUnlockedInQuest=MPS_KLD
|
|||||||
183 C Welder Automaton
|
183 C Welder Automaton
|
||||||
184 R Spire of Industry
|
184 R Spire of Industry
|
||||||
185 M Ajani, Valiant Protector
|
185 M Ajani, Valiant Protector
|
||||||
186 R Ajani's Aid
|
186 C Inspiring Roar
|
||||||
187 U Ajani's Comrade
|
187 U Ajani's Comrade
|
||||||
188 C Inspiring Roar
|
188 R Ajani's Aid
|
||||||
189 C Tranquil Expanse
|
189 C Tranquil Expanse
|
||||||
190 M Tezzeret, Master of Metal
|
190 M Tezzeret, Master of Metal
|
||||||
191 R Tezzeret's Betrayal
|
191 R Tezzeret's Betrayal
|
||||||
192 U Tezzeret's Simulacrum
|
192 C Pendulum of Patterns
|
||||||
193 C Pendulum of Patterns
|
193 U Tezzeret's Simulacrum
|
||||||
194 C Submerged Boneyard
|
194 C Submerged Boneyard
|
||||||
|
|
||||||
[tokens]
|
[tokens]
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ Foil=NotSupported
|
|||||||
17 C Curfew
|
17 C Curfew
|
||||||
18 C Dark Ritual
|
18 C Dark Ritual
|
||||||
19 R Dirtcowl Wurm
|
19 R Dirtcowl Wurm
|
||||||
20 U Disenchant
|
20 C Disenchant
|
||||||
21 C Disruptive Student
|
21 C Disruptive Student
|
||||||
22 C Drifting Meadow
|
22 C Drifting Meadow
|
||||||
23 C Elvish Lyrist
|
23 U Elvish Lyrist
|
||||||
24 C Exhume
|
24 C Exhume
|
||||||
25 U Fecundity
|
25 U Fecundity
|
||||||
26 C Fertile Ground
|
26 C Fertile Ground
|
||||||
@@ -40,16 +40,16 @@ Foil=NotSupported
|
|||||||
30 C Gorilla Warrior
|
30 C Gorilla Warrior
|
||||||
31 C Healing Salve
|
31 C Healing Salve
|
||||||
32 C Heat Ray
|
32 C Heat Ray
|
||||||
33 R Hurricane
|
33 U Hurricane
|
||||||
34 C Infantry Veteran
|
34 C Infantry Veteran
|
||||||
35 R Land Tax
|
35 U Land Tax
|
||||||
36 R Lhurgoyf
|
36 R Lhurgoyf
|
||||||
37 C Lightning Elemental
|
37 C Lightning Elemental
|
||||||
38 R Living Death
|
38 R Living Death
|
||||||
39 C Llanowar Elves
|
39 C Llanowar Elves
|
||||||
40 C Man-o'-War
|
40 C Man-o'-War
|
||||||
41 C Mana Leak
|
41 C Mana Leak
|
||||||
42 U Maniacal Rage
|
42 C Maniacal Rage
|
||||||
43 C Manta Riders
|
43 C Manta Riders
|
||||||
44 C Master Decoy
|
44 C Master Decoy
|
||||||
45 U Mogg Hollows
|
45 U Mogg Hollows
|
||||||
@@ -59,7 +59,7 @@ Foil=NotSupported
|
|||||||
49 U Pestilence
|
49 U Pestilence
|
||||||
50 C Phyrexian Ghoul
|
50 C Phyrexian Ghoul
|
||||||
51 C Pincher Beetles
|
51 C Pincher Beetles
|
||||||
52 U Plated Rootwalla
|
52 C Plated Rootwalla
|
||||||
53 C Polluted Mire
|
53 C Polluted Mire
|
||||||
54 C Prodigal Sorcerer
|
54 C Prodigal Sorcerer
|
||||||
55 C Raging Goblin
|
55 C Raging Goblin
|
||||||
@@ -72,7 +72,7 @@ Foil=NotSupported
|
|||||||
62 C Sanctum Custodian
|
62 C Sanctum Custodian
|
||||||
63 U Sanctum Guardian
|
63 U Sanctum Guardian
|
||||||
64 C Sandstorm
|
64 C Sandstorm
|
||||||
65 U Scaled Wurm
|
65 C Scaled Wurm
|
||||||
66 C Scryb Sprites
|
66 C Scryb Sprites
|
||||||
67 U Seasoned Marshal
|
67 U Seasoned Marshal
|
||||||
68 C Seeker of Skybreak
|
68 C Seeker of Skybreak
|
||||||
@@ -83,7 +83,7 @@ Foil=NotSupported
|
|||||||
73 C Slippery Karst
|
73 C Slippery Karst
|
||||||
74 C Soltari Foot Soldier
|
74 C Soltari Foot Soldier
|
||||||
75 U Songstitcher
|
75 U Songstitcher
|
||||||
76 U Soul Warden
|
76 C Soul Warden
|
||||||
77 C Spike Colony
|
77 C Spike Colony
|
||||||
78 U Spike Feeder
|
78 U Spike Feeder
|
||||||
79 R Spike Weaver
|
79 R Spike Weaver
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ Booster=10 Common, 3 Uncommon, 1 fromSheet("BBD RareMythic"), 1 BasicLand
|
|||||||
125 C Omenspeaker
|
125 C Omenspeaker
|
||||||
126 U Opportunity
|
126 U Opportunity
|
||||||
127 U Oracle's Insight
|
127 U Oracle's Insight
|
||||||
128 C Peregrine Drake
|
128 U Peregrine Drake
|
||||||
129 U Phantom Warrior
|
129 U Phantom Warrior
|
||||||
130 U Reckless Scholar
|
130 U Reckless Scholar
|
||||||
131 R Sower of Temptation
|
131 R Sower of Temptation
|
||||||
@@ -203,7 +203,7 @@ Booster=10 Common, 3 Uncommon, 1 fromSheet("BBD RareMythic"), 1 BasicLand
|
|||||||
193 C Cowl Prowler
|
193 C Cowl Prowler
|
||||||
194 C Daggerback Basilisk
|
194 C Daggerback Basilisk
|
||||||
195 M Doubling Season
|
195 M Doubling Season
|
||||||
196 U Elvish Visionary
|
196 C Elvish Visionary
|
||||||
197 U Feral Hydra
|
197 U Feral Hydra
|
||||||
198 C Fertile Ground
|
198 C Fertile Ground
|
||||||
199 U Fertilid
|
199 U Fertilid
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ Foil=NotSupported
|
|||||||
66 C Yavimaya Wurm
|
66 C Yavimaya Wurm
|
||||||
67 U Diabolic Vision
|
67 U Diabolic Vision
|
||||||
68 U Segmented Wurm
|
68 U Segmented Wurm
|
||||||
69 C Clockwork Avian
|
69 R Clockwork Avian
|
||||||
70 R Clockwork Beast
|
70 R Clockwork Beast
|
||||||
71 U Dwarven Ruins
|
71 U Dwarven Ruins
|
||||||
72 U Ebon Stronghold
|
72 U Ebon Stronghold
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ Code2=CN2
|
|||||||
MciCode=cn2
|
MciCode=cn2
|
||||||
Type=Other
|
Type=Other
|
||||||
BoosterCovers=3
|
BoosterCovers=3
|
||||||
Booster=10 Common:!fromSheet("CN2 Draft Matters"), 3 Uncommon:!fromSheet("CN2 Draft Matters"), 1 RareMythic:!fromSheet("CN2 Draft Matters"), 1 fromSheet("CN2 Draft Matters")
|
Booster=10 Common:!fromSheet("CN2 Not In Normal Slots"), 3 Uncommon:!fromSheet("CN2 Not In Normal Slots"), 1 RareMythic:!fromSheet("CN2 Not In Normal Slots"), 1 fromSheet("CN2 Draft Matters")
|
||||||
|
AdditionalSheetForFoils=fromSheet("CN2 Foil Kaya")
|
||||||
|
|
||||||
[cards]
|
[cards]
|
||||||
1 C Adriana's Valor
|
1 C Adriana's Valor
|
||||||
@@ -230,6 +231,7 @@ Booster=10 Common:!fromSheet("CN2 Draft Matters"), 3 Uncommon:!fromSheet("CN2 Dr
|
|||||||
219 R Exotic Orchard
|
219 R Exotic Orchard
|
||||||
220 U Rogue's Passage
|
220 U Rogue's Passage
|
||||||
221 U Shimmering Grotto
|
221 U Shimmering Grotto
|
||||||
|
222 M Kaya, Ghost Assassin
|
||||||
|
|
||||||
[tokens]
|
[tokens]
|
||||||
w_1_1_soldier
|
w_1_1_soldier
|
||||||
|
|||||||
@@ -184,14 +184,14 @@ Booster=10 Common:!land, 3 Uncommon, 1 RareMythic, 1 fromSheet("FRF Lands"), 0 f
|
|||||||
174 C Tranquil Cove
|
174 C Tranquil Cove
|
||||||
175 C Wind-Scarred Crag
|
175 C Wind-Scarred Crag
|
||||||
176 L Plains
|
176 L Plains
|
||||||
176 L Plains
|
177 L Plains
|
||||||
179 L Island
|
178 L Island
|
||||||
179 L Island
|
179 L Island
|
||||||
|
180 L Swamp
|
||||||
181 L Swamp
|
181 L Swamp
|
||||||
181 L Swamp
|
182 L Mountain
|
||||||
183 L Mountain
|
183 L Mountain
|
||||||
183 L Mountain
|
184 L Forest
|
||||||
185 L Forest
|
|
||||||
185 L Forest
|
185 L Forest
|
||||||
|
|
||||||
[tokens]
|
[tokens]
|
||||||
|
|||||||
@@ -258,26 +258,26 @@ Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand KTK
|
|||||||
247 C Wind-Scarred Crag
|
247 C Wind-Scarred Crag
|
||||||
248 R Windswept Heath
|
248 R Windswept Heath
|
||||||
249 R Wooded Foothills
|
249 R Wooded Foothills
|
||||||
269 L Forest
|
|
||||||
269 L Forest
|
|
||||||
269 L Forest
|
|
||||||
269 L Forest
|
|
||||||
257 L Island
|
|
||||||
257 L Island
|
|
||||||
257 L Island
|
|
||||||
257 L Island
|
|
||||||
263 L Mountain
|
|
||||||
263 L Mountain
|
|
||||||
263 L Mountain
|
|
||||||
263 L Mountain
|
|
||||||
250 L Plains
|
|
||||||
250 L Plains
|
|
||||||
250 L Plains
|
|
||||||
250 L Plains
|
250 L Plains
|
||||||
|
251 L Plains
|
||||||
|
252 L Plains
|
||||||
|
253 L Plains
|
||||||
|
254 L Island
|
||||||
|
255 L Island
|
||||||
|
256 L Island
|
||||||
|
257 L Island
|
||||||
258 L Swamp
|
258 L Swamp
|
||||||
258 L Swamp
|
259 L Swamp
|
||||||
258 L Swamp
|
260 L Swamp
|
||||||
258 L Swamp
|
261 L Swamp
|
||||||
|
262 L Mountain
|
||||||
|
263 L Mountain
|
||||||
|
264 L Mountain
|
||||||
|
265 L Mountain
|
||||||
|
266 L Forest
|
||||||
|
267 L Forest
|
||||||
|
268 L Forest
|
||||||
|
269 L Forest
|
||||||
|
|
||||||
[tokens]
|
[tokens]
|
||||||
w_3_4_bird_flying
|
w_3_4_bird_flying
|
||||||
|
|||||||
@@ -238,26 +238,26 @@ Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand
|
|||||||
227 U Encroaching Wastes
|
227 U Encroaching Wastes
|
||||||
228 R Mutavault
|
228 R Mutavault
|
||||||
229 U Shimmering Grotto
|
229 U Shimmering Grotto
|
||||||
|
230 L Plains
|
||||||
231 L Plains
|
231 L Plains
|
||||||
231 L Plains
|
232 L Plains
|
||||||
231 L Plains
|
233 L Plains
|
||||||
231 L Plains
|
|
||||||
234 L Island
|
|
||||||
234 L Island
|
|
||||||
234 L Island
|
|
||||||
234 L Island
|
234 L Island
|
||||||
|
235 L Island
|
||||||
|
236 L Island
|
||||||
|
237 L Island
|
||||||
|
238 L Swamp
|
||||||
|
239 L Swamp
|
||||||
|
240 L Swamp
|
||||||
241 L Swamp
|
241 L Swamp
|
||||||
241 L Swamp
|
242 L Mountain
|
||||||
241 L Swamp
|
243 L Mountain
|
||||||
241 L Swamp
|
|
||||||
244 L Mountain
|
|
||||||
244 L Mountain
|
|
||||||
244 L Mountain
|
|
||||||
244 L Mountain
|
244 L Mountain
|
||||||
|
245 L Mountain
|
||||||
246 L Forest
|
246 L Forest
|
||||||
246 L Forest
|
247 L Forest
|
||||||
246 L Forest
|
248 L Forest
|
||||||
246 L Forest
|
249 L Forest
|
||||||
|
|
||||||
[tokens]
|
[tokens]
|
||||||
c_1_1_sliver
|
c_1_1_sliver
|
||||||
|
|||||||
@@ -258,26 +258,26 @@ Booster=10 Common:!fromSheet("M15 Sample Cards"), 3 Uncommon:!fromSheet("M15 Sam
|
|||||||
247 R Sliver Hive
|
247 R Sliver Hive
|
||||||
248 R Urborg, Tomb of Yawgmoth
|
248 R Urborg, Tomb of Yawgmoth
|
||||||
249 R Yavimaya Coast
|
249 R Yavimaya Coast
|
||||||
|
250 L Plains
|
||||||
251 L Plains
|
251 L Plains
|
||||||
251 L Plains
|
252 L Plains
|
||||||
251 L Plains
|
253 L Plains
|
||||||
251 L Plains
|
254 L Island
|
||||||
255 L Island
|
|
||||||
255 L Island
|
|
||||||
255 L Island
|
|
||||||
255 L Island
|
255 L Island
|
||||||
|
256 L Island
|
||||||
|
257 L Island
|
||||||
258 L Swamp
|
258 L Swamp
|
||||||
258 L Swamp
|
259 L Swamp
|
||||||
258 L Swamp
|
260 L Swamp
|
||||||
258 L Swamp
|
261 L Swamp
|
||||||
263 L Mountain
|
262 L Mountain
|
||||||
263 L Mountain
|
|
||||||
263 L Mountain
|
|
||||||
263 L Mountain
|
263 L Mountain
|
||||||
|
264 L Mountain
|
||||||
|
265 L Mountain
|
||||||
266 L Forest
|
266 L Forest
|
||||||
266 L Forest
|
267 L Forest
|
||||||
266 L Forest
|
268 L Forest
|
||||||
266 L Forest
|
269 L Forest
|
||||||
270 R Aegis Angel
|
270 R Aegis Angel
|
||||||
271 C Divine Verdict
|
271 C Divine Verdict
|
||||||
272 C Inspired Charge
|
272 C Inspired Charge
|
||||||
|
|||||||
128
forge-gui/res/editions/Mystery Booster Retail Edition Foils.txt
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
[metadata]
|
||||||
|
Code=FMB1
|
||||||
|
Date=2020-03-08
|
||||||
|
Name=Mystery Booster Retail Edition Foils
|
||||||
|
Type=Other
|
||||||
|
|
||||||
|
[cards]
|
||||||
|
1 U Not of This World
|
||||||
|
2 R Celestial Dawn
|
||||||
|
3 R Celestial Kirin
|
||||||
|
4 U Changeling Hero
|
||||||
|
5 U Council Guardian
|
||||||
|
6 U Eidolon of Rhetoric
|
||||||
|
7 R Isamaru, Hound of Konda
|
||||||
|
8 C Lapse of Certainty
|
||||||
|
9 C Lumithread Field
|
||||||
|
10 R Norn's Annex
|
||||||
|
11 R Proclamation of Rebirth
|
||||||
|
12 U Pull from Eternity
|
||||||
|
13 R Rune-Tail, Kitsune Ascendant
|
||||||
|
14 C Sinew Sliver
|
||||||
|
15 C Soul's Attendant
|
||||||
|
16 R Spelltithe Enforcer
|
||||||
|
17 U Springjack Shepherd
|
||||||
|
18 U Wall of Shards
|
||||||
|
19 U White Knight
|
||||||
|
20 C Blighted Agent
|
||||||
|
21 U Delay
|
||||||
|
22 R Fatespinner
|
||||||
|
23 U Frozen Aether
|
||||||
|
24 R Grand Architect
|
||||||
|
25 R Intruder Alarm
|
||||||
|
26 M Misthollow Griffin
|
||||||
|
27 U Paradox Haze
|
||||||
|
28 R Patron of the Moon
|
||||||
|
29 R Puca's Mischief
|
||||||
|
30 R Spellweaver Volute
|
||||||
|
31 C Storm Crow
|
||||||
|
32 R Zur's Weirding
|
||||||
|
33 R Bringer of the Black Dawn
|
||||||
|
34 C Chimney Imp
|
||||||
|
35 R Conspiracy
|
||||||
|
36 C Echoing Decay
|
||||||
|
37 R Funeral Charm
|
||||||
|
38 R Herald of Leshrac
|
||||||
|
39 R Marrow-Gnawer
|
||||||
|
40 R Nezumi Shortfang
|
||||||
|
41 R One with Nothing
|
||||||
|
42 U Ravenous Trap
|
||||||
|
43 U Rescue from the Underworld
|
||||||
|
44 R Undead Warchief
|
||||||
|
45 C Viscera Seer
|
||||||
|
46 U Balduvian Rage
|
||||||
|
47 R Braid of Fire
|
||||||
|
48 C Burning Inquiry
|
||||||
|
49 R Fiery Gambit
|
||||||
|
50 U Flamekin Harbinger
|
||||||
|
51 R Form of the Dragon
|
||||||
|
52 C Goblin Bushwhacker
|
||||||
|
53 U Guerrilla Tactics
|
||||||
|
54 U Lightning Storm
|
||||||
|
55 R Norin the Wary
|
||||||
|
56 C Ogre Gatecrasher
|
||||||
|
57 C Pyretic Ritual
|
||||||
|
58 M Scourge of the Throne
|
||||||
|
59 R Stigma Lasher
|
||||||
|
60 U Treasonous Ogre
|
||||||
|
61 R Allosaurus Rider
|
||||||
|
62 U Archetype of Endurance
|
||||||
|
63 C Boreal Druid
|
||||||
|
64 R Boundless Realms
|
||||||
|
65 U Bramblewood Paragon
|
||||||
|
66 R Fungusaur
|
||||||
|
67 C Game-Trail Changeling
|
||||||
|
68 C Gleeful Sabotage
|
||||||
|
69 C Greater Mossdog
|
||||||
|
70 R Helix Pinnacle
|
||||||
|
71 C Hornet Sting
|
||||||
|
72 U Manaweft Sliver
|
||||||
|
73 R Maro
|
||||||
|
74 R Myojin of Life's Web
|
||||||
|
75 R Panglacial Wurm
|
||||||
|
76 R Reki, the History of Kamigawa
|
||||||
|
77 R Rhox
|
||||||
|
78 C Sakura-Tribe Scout
|
||||||
|
79 U Scryb Ranger
|
||||||
|
80 U Sheltering Ancient
|
||||||
|
81 U Sosuke, Son of Seshiro
|
||||||
|
82 R Spike Feeder
|
||||||
|
83 M Aurelia's Fury
|
||||||
|
84 U Drogskol Captain
|
||||||
|
85 R Glittering Wish
|
||||||
|
86 U Harmonic Sliver
|
||||||
|
87 M Karrthus, Tyrant of Jund
|
||||||
|
88 M Maelstrom Nexus
|
||||||
|
89 U Mind Funeral
|
||||||
|
90 M Sarkhan the Mad
|
||||||
|
91 M Sen Triplets
|
||||||
|
92 R Yore-Tiller Nephilim
|
||||||
|
93 R Balefire Liege
|
||||||
|
94 U Gilder Bairn
|
||||||
|
95 U Kulrath Knight
|
||||||
|
96 C Noggle Bandit
|
||||||
|
97 U Wear // Tear
|
||||||
|
98 R Amulet of Vigor
|
||||||
|
99 U Blasting Station
|
||||||
|
100 U Codex Shredder
|
||||||
|
101 U Geth's Grimoire
|
||||||
|
102 C Iron Myr
|
||||||
|
103 R Knowledge Pool
|
||||||
|
104 U Lantern of Insight
|
||||||
|
105 R Leveler
|
||||||
|
106 M Lich's Mirror
|
||||||
|
107 U Magewright's Stone
|
||||||
|
108 U Memnite
|
||||||
|
109 R Mindslaver
|
||||||
|
110 C Pili-Pala
|
||||||
|
111 R Reaper King
|
||||||
|
112 R Sundial of the Infinite
|
||||||
|
113 R Teferi's Puzzle Box
|
||||||
|
114 U Trailblazer's Boots
|
||||||
|
115 R Triskelion
|
||||||
|
116 R Witchbane Orb
|
||||||
|
117 R Alchemist's Refuge
|
||||||
|
118 R Minamo, School at Water's Edge
|
||||||
|
119 U Mirrodin's Core
|
||||||
|
120 R Shizo, Death's Storehouse
|
||||||
|
121 U Stalking Stones
|
||||||
1704
forge-gui/res/editions/Mystery Booster.txt
Normal file
@@ -367,17 +367,17 @@ Prerelease=6 Boosters, 1 RareMythic+
|
|||||||
350 R Temple of Malice
|
350 R Temple of Malice
|
||||||
351 R Temple of Plenty
|
351 R Temple of Plenty
|
||||||
#Bundle promo
|
#Bundle promo
|
||||||
R Arasta of the Endless Web
|
352 R Arasta of the Endless Web
|
||||||
#Promo Pack
|
#Promo Pack
|
||||||
U Alseid of Life's Bounty
|
353 U Alseid of Life's Bounty
|
||||||
C Thirst for Meaning
|
354 C Thirst for Meaning
|
||||||
U Gray Merchant of Asphodel
|
355 U Gray Merchant of Asphodel
|
||||||
C Thrill of Possibility
|
356 C Thrill of Possibility
|
||||||
U Wolfwillow Haven
|
357 U Wolfwillow Haven
|
||||||
|
|
||||||
[tokens]
|
[tokens]
|
||||||
b_2_2_zombie
|
b_2_2_zombie
|
||||||
c_0_4_wall_defender
|
c_0_4_a_wall_defender
|
||||||
g_1_2_spider_reach
|
g_1_2_spider_reach
|
||||||
g_2_2_wolf
|
g_2_2_wolf
|
||||||
r_x_1_elemental_trample_haste
|
r_x_1_elemental_trample_haste
|
||||||
|
|||||||
@@ -284,36 +284,36 @@ Prerelease=6 Boosters, 1 RareMythic+
|
|||||||
271 M Oko, Thief of Crowns
|
271 M Oko, Thief of Crowns
|
||||||
272 M The Royal Scions
|
272 M The Royal Scions
|
||||||
#Storybook Frames
|
#Storybook Frames
|
||||||
C Ardenvale Tactician
|
273 C Ardenvale Tactician
|
||||||
C Faerie Guidemother
|
274 C Faerie Guidemother
|
||||||
R Giant Killer
|
275 R Giant Killer
|
||||||
C Lonesome Unicorn
|
276 C Lonesome Unicorn
|
||||||
M Realm-Cloaked Giant
|
277 M Realm-Cloaked Giant
|
||||||
U Shepherd of the Flock
|
278 U Shepherd of the Flock
|
||||||
C Silverflame Squire
|
279 C Silverflame Squire
|
||||||
U Animating Faerie
|
280 U Animating Faerie
|
||||||
M Brazen Borrower
|
281 M Brazen Borrower
|
||||||
R Fae of Wishes
|
282 R Fae of Wishes
|
||||||
U Hypnotic Sprite
|
283 U Hypnotic Sprite
|
||||||
C Merfolk Secretkeeper
|
284 C Merfolk Secretkeeper
|
||||||
C Queen of Ice
|
285 C Queen of Ice
|
||||||
U Foulmire Knight
|
286 U Foulmire Knight
|
||||||
R Murderous Rider
|
287 R Murderous Rider
|
||||||
U Order of Midnight
|
288 U Order of Midnight
|
||||||
C Reaper of Night
|
289 C Reaper of Night
|
||||||
C Smitten Swordmaster
|
290 C Smitten Swordmaster
|
||||||
R Bonecrusher Giant
|
291 R Bonecrusher Giant
|
||||||
U Embereth Shieldbreaker
|
292 U Embereth Shieldbreaker
|
||||||
C Merchant of the Vale
|
293 C Merchant of the Vale
|
||||||
C Rimrock Knight
|
294 C Rimrock Knight
|
||||||
U Beanstalk Giant
|
295 U Beanstalk Giant
|
||||||
C Curious Pair
|
296 C Curious Pair
|
||||||
U Flaxen Intruder
|
297 U Flaxen Intruder
|
||||||
C Garenbrig Carver
|
298 C Garenbrig Carver
|
||||||
R Lovestruck Beast
|
299 R Lovestruck Beast
|
||||||
C Rosethorn Acolyte
|
300 C Rosethorn Acolyte
|
||||||
C Tuinvale Treefolk
|
301 C Tuinvale Treefolk
|
||||||
U Oakhame Ranger
|
302 U Oakhame Ranger
|
||||||
#Buy-A-Box Promo
|
#Buy-A-Box Promo
|
||||||
303 M Kenrith, the Returned King
|
303 M Kenrith, the Returned King
|
||||||
#Planeswalker Deck Cards
|
#Planeswalker Deck Cards
|
||||||
@@ -348,72 +348,72 @@ U Oakhame Ranger
|
|||||||
332 R Tome of Legends
|
332 R Tome of Legends
|
||||||
333 C Command Tower
|
333 C Command Tower
|
||||||
#Borderless art rares and mythics
|
#Borderless art rares and mythics
|
||||||
R Acclaimed Contender
|
334 R Acclaimed Contender
|
||||||
R Charming Prince
|
335 R Charming Prince
|
||||||
M The Circle of Loyalty
|
336 M The Circle of Loyalty
|
||||||
R Happily Ever After
|
337 R Happily Ever After
|
||||||
M Harmonious Archon
|
338 M Harmonious Archon
|
||||||
R Hushbringer
|
339 R Hushbringer
|
||||||
R Linden, the Steadfast Queen
|
340 R Linden, the Steadfast Queen
|
||||||
R Worthy Knight
|
341 R Worthy Knight
|
||||||
R Emry, Lurker of the Loch
|
342 R Emry, Lurker of the Loch
|
||||||
R Folio of Fancies
|
343 R Folio of Fancies
|
||||||
R Gadwick, the Wizened
|
344 R Gadwick, the Wizened
|
||||||
M The Magic Mirror
|
345 M The Magic Mirror
|
||||||
R Midnight Clock
|
346 R Midnight Clock
|
||||||
R Mirrormade
|
347 R Mirrormade
|
||||||
R Stolen by the Fae
|
348 R Stolen by the Fae
|
||||||
R Vantress Gargoyle
|
349 R Vantress Gargoyle
|
||||||
R Ayara, First of Locthwain
|
350 R Ayara, First of Locthwain
|
||||||
R Blacklance Paragon
|
351 R Blacklance Paragon
|
||||||
M The Cauldron of Eternity
|
352 M The Cauldron of Eternity
|
||||||
R Clackbridge Troll
|
353 R Clackbridge Troll
|
||||||
R Oathsworn Knight
|
354 R Oathsworn Knight
|
||||||
R Piper of the Swarm
|
355 R Piper of the Swarm
|
||||||
M Rankle, Master of Pranks
|
356 M Rankle, Master of Pranks
|
||||||
R Wishclaw Talisman
|
357 R Wishclaw Talisman
|
||||||
R Witch's Vengeance
|
358 R Witch's Vengeance
|
||||||
M Embercleave
|
359 M Embercleave
|
||||||
R Fervent Champion
|
360 R Fervent Champion
|
||||||
R Fires of Invention
|
361 R Fires of Invention
|
||||||
R Irencrag Feat
|
362 R Irencrag Feat
|
||||||
R Irencrag Pyromancer
|
363 R Irencrag Pyromancer
|
||||||
R Opportunistic Dragon
|
364 R Opportunistic Dragon
|
||||||
M Robber of the Rich
|
365 M Robber of the Rich
|
||||||
R Sundering Stroke
|
366 R Sundering Stroke
|
||||||
R Torbran, Thane of Red Fell
|
367 R Torbran, Thane of Red Fell
|
||||||
R Feasting Troll King
|
368 R Feasting Troll King
|
||||||
R Gilded Goose
|
369 R Gilded Goose
|
||||||
M The Great Henge
|
370 M The Great Henge
|
||||||
R Once Upon A Time
|
371 R Once Upon a Time
|
||||||
M Questing Beast
|
372 M Questing Beast
|
||||||
R Return of the Wildspeaker
|
373 R Return of the Wildspeaker
|
||||||
R Wicked Wolf
|
374 R Wicked Wolf
|
||||||
R Wildborn Preserver
|
375 R Wildborn Preserver
|
||||||
R Yorvo, Lord of Garenbrig
|
376 R Yorvo, Lord of Garenbrig
|
||||||
R Dance of the Manse
|
377 R Dance of the Manse
|
||||||
R Doom Foretold
|
378 R Doom Foretold
|
||||||
R Escape to the Wilds
|
379 R Escape to the Wilds
|
||||||
R Faeburrow Elder
|
380 R Faeburrow Elder
|
||||||
R Lochmere Serpent
|
381 R Lochmere Serpent
|
||||||
M Outlaws' Merriment
|
382 M Outlaws' Merriment
|
||||||
R Stormfist Crusader
|
383 R Stormfist Crusader
|
||||||
R Sorcerous Spyglass
|
384 R Sorcerous Spyglass
|
||||||
R Stonecoil Serpent
|
385 R Stonecoil Serpent
|
||||||
R Castle Ardenvale
|
386 R Castle Ardenvale
|
||||||
R Castle Embereth
|
387 R Castle Embereth
|
||||||
R Castle Garenbrig
|
388 R Castle Garenbrig
|
||||||
R Castle Locthwain
|
389 R Castle Locthwain
|
||||||
R Castle Vantress
|
390 R Castle Vantress
|
||||||
R Fabled Passage
|
391 R Fabled Passage
|
||||||
#Bundle promo
|
#Bundle promo
|
||||||
R Piper of the Swarm
|
392 R Piper of the Swarm
|
||||||
#Promo Pack
|
#Promo Pack
|
||||||
U Glass Casket
|
393 U Glass Casket
|
||||||
U Slaying Fire
|
394 U Slaying Fire
|
||||||
U Kenrith's Transformation
|
395 U Kenrith's Transformation
|
||||||
U Improbable Alliance
|
396 U Improbable Alliance
|
||||||
U Inspiring Veteran
|
397 U Inspiring Veteran
|
||||||
|
|
||||||
[tokens]
|
[tokens]
|
||||||
w_0_1_goat
|
w_0_1_goat
|
||||||
|
|||||||
@@ -178,5 +178,5 @@ R Winding Canyons
|
|||||||
R Xanthic Statue
|
R Xanthic Statue
|
||||||
C Zombie Scavengers
|
C Zombie Scavengers
|
||||||
|
|
||||||
[token]
|
[tokens]
|
||||||
g_1_1_squirrel
|
g_1_1_squirrel
|
||||||
@@ -4,4 +4,4 @@ Order:101
|
|||||||
Type:Casual
|
Type:Casual
|
||||||
Subtype:Commander
|
Subtype:Commander
|
||||||
Sets:GRN, RNA, WAR, M20, ELD, THB
|
Sets:GRN, RNA, WAR, M20, ELD, THB
|
||||||
Banned:Sorcerous Spyglass;Oko, Thief of Crowns
|
Banned:Golos, Tireless Pilgrim; Oko, Thief of Crowns; Sorcerous Spyglass
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ Name:Legacy
|
|||||||
Order:105
|
Order:105
|
||||||
Subtype:Legacy
|
Subtype:Legacy
|
||||||
Type:Sanctioned
|
Type:Sanctioned
|
||||||
Banned:Adriana's Valor; Advantageous Proclamation; Assemble the Rank and Vile; Backup Plan; Brago's Favor; Deathrite Shaman; Double Stroke; Echoing Boon; Emissary's Ploy; Gitaxian Probe; Hired Heist; Hold the Perimeter; Hymn of the Wilds; Immediate Action; Incendiary Dissent; Iterative Analysis; Muzzio's Preparations; Natural Unity; Power Play; Secret Summoning; Secrets of Paradise; Sentinel Dispatch; Sovereign's Realm; Summoner's Bond; Unexpected Potential; Weight Advantage; Worldknit; Amulet of Quoz; Bronze Tablet; Contract from Below; Darkpact; Demonic Attorney; Jeweled Bird; Rebirth; Tempest Efreet; Timmerian Fiends; Ancestral Recall; Balance; Bazaar of Baghdad; Black Lotus; Channel; Chaos Orb; Demonic Consultation; Demonic Tutor; Dig Through Time; Earthcraft; Falling Star; Fastbond; Flash; Frantic Search; Goblin Recruiter; Gush; Hermit Druid; Imperial Seal; Library of Alexandria; Mana Crypt; Mana Drain; Mana Vault; Memory Jar; Mental Misstep; Mind Twist; Mind's Desire; Mishra's Workshop; Mox Emerald; Mox Jet; Mox Pearl; Mox Ruby; Mox Sapphire; Mystical Tutor; Necropotence; Oath of Druids; Sensei's Divining Top; Shahrazad; Skullclamp; Sol Ring; Strip Mine; Survival of the Fittest; Time Vault; Time Walk; Timetwister; Tinker; Tolarian Academy; Treasure Cruise; Vampiric Tutor; Wheel of Fortune; Windfall; Wrenn and Six; Yawgmoth's Bargain; Yawgmoth's Will
|
Banned:Adriana's Valor; Advantageous Proclamation; Assemble the Rank and Vile; Backup Plan; Brago's Favor; Deathrite Shaman; Double Stroke; Echoing Boon; Emissary's Ploy; Gitaxian Probe; Hired Heist; Hold the Perimeter; Hymn of the Wilds; Immediate Action; Incendiary Dissent; Iterative Analysis; Muzzio's Preparations; Natural Unity; Power Play; Secret Summoning; Secrets of Paradise; Sentinel Dispatch; Sovereign's Realm; Summoner's Bond; Underworld Breach; Unexpected Potential; Weight Advantage; Worldknit; Amulet of Quoz; Bronze Tablet; Contract from Below; Darkpact; Demonic Attorney; Jeweled Bird; Rebirth; Tempest Efreet; Timmerian Fiends; Ancestral Recall; Balance; Bazaar of Baghdad; Black Lotus; Channel; Chaos Orb; Demonic Consultation; Demonic Tutor; Dig Through Time; Earthcraft; Falling Star; Fastbond; Flash; Frantic Search; Goblin Recruiter; Gush; Hermit Druid; Imperial Seal; Library of Alexandria; Mana Crypt; Mana Drain; Mana Vault; Memory Jar; Mental Misstep; Mind Twist; Mind's Desire; Mishra's Workshop; Mox Emerald; Mox Jet; Mox Pearl; Mox Ruby; Mox Sapphire; Mystical Tutor; Necropotence; Oath of Druids; Sensei's Divining Top; Shahrazad; Skullclamp; Sol Ring; Strip Mine; Survival of the Fittest; Time Vault; Time Walk; Timetwister; Tinker; Tolarian Academy; Treasure Cruise; Vampiric Tutor; Wheel of Fortune; Windfall; Wrenn and Six; Yawgmoth's Bargain; Yawgmoth's Will
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ Order:103
|
|||||||
Subtype:Modern
|
Subtype:Modern
|
||||||
Type:Sanctioned
|
Type:Sanctioned
|
||||||
Sets:8ED, MRD, DST, 5DN, CHK, BOK, SOK, 9ED, RAV, GPT, DIS, CSP, TSP, TSB, PLC, FUT, 10E, LRW, EVE, SHM, MOR, ALA, CFX, ARB, M10, ZEN, WWK, ROE, M11, SOM, MBS, NPH, M12, ISD, DKA, AVR, M13, RTR, GTC, DGM, M14, THS, BNG, JOU, M15, KTK, FRF, DTK, MM2, ORI, BFZ, OGW, SOI, EMN, KLD, AER, AKH, W17, HOU, XLN, RIX, DOM, M19, G18, GRN, RNA, WAR, MH1, M20, ELD, THB
|
Sets:8ED, MRD, DST, 5DN, CHK, BOK, SOK, 9ED, RAV, GPT, DIS, CSP, TSP, TSB, PLC, FUT, 10E, LRW, EVE, SHM, MOR, ALA, CFX, ARB, M10, ZEN, WWK, ROE, M11, SOM, MBS, NPH, M12, ISD, DKA, AVR, M13, RTR, GTC, DGM, M14, THS, BNG, JOU, M15, KTK, FRF, DTK, MM2, ORI, BFZ, OGW, SOI, EMN, KLD, AER, AKH, W17, HOU, XLN, RIX, DOM, M19, G18, GRN, RNA, WAR, MH1, M20, ELD, THB
|
||||||
Banned:Ancient Den; Birthing Pod; Blazing Shoal; Bridge from Below; Chrome Mox; Cloudpost; Dark Depths; Deathrite Shaman; Dig Through Time; Dread Return; Eye of Ugin; Faithless Looting; Gitaxian Probe; Glimpse of Nature; Golgari Grave-Troll; Great Furnace; Green Sun's Zenith; Hogaak, Arisen Necropolis; Hypergenesis; Krark-Clan Ironworks; Mental Misstep; Mox Opal; Mycosynth Lattice; Oko, Thief of Crowns; Ponder; Preordain; Punishing Fire; Rite of Flame; Seat of the Synod; Second Sunrise; Seething Song; Sensei's Divining Top; Skullclamp; Splinter Twin; Summer Bloom; Treasure Cruise; Tree of Tales; Umezawa's Jitte; Vault of Whispers
|
Banned:Ancient Den; Birthing Pod; Blazing Shoal; Bridge from Below; Chrome Mox; Cloudpost; Dark Depths; Deathrite Shaman; Dig Through Time; Dread Return; Eye of Ugin; Faithless Looting; Gitaxian Probe; Glimpse of Nature; Golgari Grave-Troll; Great Furnace; Green Sun's Zenith; Hogaak, Arisen Necropolis; Hypergenesis; Krark-Clan Ironworks; Mental Misstep; Mox Opal; Mycosynth Lattice; Oko, Thief of Crowns; Once Upon A Time; Ponder; Preordain; Punishing Fire; Rite of Flame; Seat of the Synod; Second Sunrise; Seething Song; Sensei's Divining Top; Skullclamp; Splinter Twin; Summer Bloom; Treasure Cruise; Tree of Tales; Umezawa's Jitte; Vault of Whispers
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ btnResetJavaFutureCompatibilityWarnings=Java-Kompatibilitätswarnung zurücksetz
|
|||||||
btnClearImageCache=Leere Bildspeicher
|
btnClearImageCache=Leere Bildspeicher
|
||||||
btnTokenPreviewer=Spielstein-Vorschau
|
btnTokenPreviewer=Spielstein-Vorschau
|
||||||
btnCopyToClipboard=In Zwischenablage kopieren
|
btnCopyToClipboard=In Zwischenablage kopieren
|
||||||
|
cbpAutoUpdater=Auto updater
|
||||||
|
nlAutoUpdater=Select the release channel to use for updating Forge
|
||||||
cbpSelectLanguage=Sprache
|
cbpSelectLanguage=Sprache
|
||||||
nlSelectLanguage=Wähle Sprache (Ist noch in Arbeit und nur teilweise umgesetzt.) (Neustart ist erforderlich.)
|
nlSelectLanguage=Wähle Sprache (Ist noch in Arbeit und nur teilweise umgesetzt.) (Neustart ist erforderlich.)
|
||||||
cbRemoveSmall=Entferne kleine Kreaturen
|
cbRemoveSmall=Entferne kleine Kreaturen
|
||||||
@@ -180,6 +182,7 @@ KeyboardShortcuts=Tastenkombinationen
|
|||||||
#VSubmenuAchievements.java
|
#VSubmenuAchievements.java
|
||||||
lblAchievements=Errungenschaften
|
lblAchievements=Errungenschaften
|
||||||
#VSubmenuDownloaders.java
|
#VSubmenuDownloaders.java
|
||||||
|
btnCheckForUpdates=Check for Updates
|
||||||
btnDownloadSetPics=Bilder(LQ) Sets herunterladen
|
btnDownloadSetPics=Bilder(LQ) Sets herunterladen
|
||||||
btnDownloadPicsHQ=Bilder(HQ) Karten herunterladen (Sehr langsam!)
|
btnDownloadPicsHQ=Bilder(HQ) Karten herunterladen (Sehr langsam!)
|
||||||
btnDownloadPics=Bilder(LQ) Karten herunterladen
|
btnDownloadPics=Bilder(LQ) Karten herunterladen
|
||||||
@@ -192,6 +195,7 @@ btnImportPictures=Daten importieren
|
|||||||
btnHowToPlay=Wie man spielt
|
btnHowToPlay=Wie man spielt
|
||||||
btnDownloadPrices=Kartenpreise herunterladen
|
btnDownloadPrices=Kartenpreise herunterladen
|
||||||
btnLicensing=Lizenzhinweis
|
btnLicensing=Lizenzhinweis
|
||||||
|
lblCheckForUpdates=Check Forge server to see if there's a more recent release
|
||||||
lblDownloadPics=Lädt ein Standardbild pro Karte.
|
lblDownloadPics=Lädt ein Standardbild pro Karte.
|
||||||
lblDownloadPicsHQ=Lädt ein HQ-Standardbild pro Karte.
|
lblDownloadPicsHQ=Lädt ein HQ-Standardbild pro Karte.
|
||||||
lblDownloadSetPics=Lädt alle Bilder pro Karte. Eines für jedes Set, in welchem die Karte auftauchte.
|
lblDownloadSetPics=Lädt alle Bilder pro Karte. Eines für jedes Set, in welchem die Karte auftauchte.
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ btnResetJavaFutureCompatibilityWarnings=Reset Java Compatibility Warnings
|
|||||||
btnClearImageCache=Clear Image Cache
|
btnClearImageCache=Clear Image Cache
|
||||||
btnTokenPreviewer=Token Previewer
|
btnTokenPreviewer=Token Previewer
|
||||||
btnCopyToClipboard=Copy to Clipboard
|
btnCopyToClipboard=Copy to Clipboard
|
||||||
|
cbpAutoUpdater=Auto updater
|
||||||
|
nlAutoUpdater=Select the release channel to use for updating Forge
|
||||||
cbpSelectLanguage=Language
|
cbpSelectLanguage=Language
|
||||||
nlSelectLanguage=Select Language (Excluded Game part. Still a work in progress) (RESTART REQUIRED)
|
nlSelectLanguage=Select Language (Excluded Game part. Still a work in progress) (RESTART REQUIRED)
|
||||||
cbRemoveSmall=Remove Small Creatures
|
cbRemoveSmall=Remove Small Creatures
|
||||||
@@ -180,6 +182,7 @@ KeyboardShortcuts=Keyboard Shortcuts
|
|||||||
#VSubmenuAchievements.java
|
#VSubmenuAchievements.java
|
||||||
lblAchievements=Achievements
|
lblAchievements=Achievements
|
||||||
#VSubmenuDownloaders.java
|
#VSubmenuDownloaders.java
|
||||||
|
btnCheckForUpdates=Check for Updates
|
||||||
btnDownloadSetPics=Download LQ Set Pictures
|
btnDownloadSetPics=Download LQ Set Pictures
|
||||||
btnDownloadPicsHQ=Download HQ Card Pictures (Very Slow!)
|
btnDownloadPicsHQ=Download HQ Card Pictures (Very Slow!)
|
||||||
btnDownloadPics=Download LQ Card Pictures
|
btnDownloadPics=Download LQ Card Pictures
|
||||||
@@ -192,6 +195,7 @@ btnImportPictures=Import Data
|
|||||||
btnHowToPlay=How To Play
|
btnHowToPlay=How To Play
|
||||||
btnDownloadPrices=Download Card Prices
|
btnDownloadPrices=Download Card Prices
|
||||||
btnLicensing=License Details
|
btnLicensing=License Details
|
||||||
|
lblCheckForUpdates=Check Forge server to see if there's a more recent release
|
||||||
lblDownloadPics=Download default card picture for each card.
|
lblDownloadPics=Download default card picture for each card.
|
||||||
lblDownloadPicsHQ=Download default card HQ picture for each card.
|
lblDownloadPicsHQ=Download default card HQ picture for each card.
|
||||||
lblDownloadSetPics=Download all pictures of each card (one for each set the card appeared in)
|
lblDownloadSetPics=Download all pictures of each card (one for each set the card appeared in)
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ btnResetJavaFutureCompatibilityWarnings=Restablecer las advertencias de compatib
|
|||||||
btnClearImageCache=Limpiar Caché de Imágenes
|
btnClearImageCache=Limpiar Caché de Imágenes
|
||||||
btnTokenPreviewer=Previsualizador de Fichas (Token)
|
btnTokenPreviewer=Previsualizador de Fichas (Token)
|
||||||
btnCopyToClipboard=Copiar al portapapeles
|
btnCopyToClipboard=Copiar al portapapeles
|
||||||
|
cbpAutoUpdater=Actualizar Forge
|
||||||
|
nlAutoUpdater=Selecciona la versión a utilizar para actualizar Forge
|
||||||
cbpSelectLanguage=Idioma
|
cbpSelectLanguage=Idioma
|
||||||
nlSelectLanguage=Seleccionar idioma (excepto partida). Todavía un trabajo en progreso) (Es necesario reiniciar Forge)
|
nlSelectLanguage=Seleccionar idioma (excepto partida). Todavía un trabajo en progreso) (Es necesario reiniciar Forge)
|
||||||
cbRemoveSmall=Eliminar Pequeñas Criaturas
|
cbRemoveSmall=Eliminar Pequeñas Criaturas
|
||||||
@@ -180,6 +182,7 @@ KeyboardShortcuts=Atajos de teclado
|
|||||||
#VSubmenuAchievements.java
|
#VSubmenuAchievements.java
|
||||||
lblAchievements=Logros
|
lblAchievements=Logros
|
||||||
#VSubmenuDownloaders.java
|
#VSubmenuDownloaders.java
|
||||||
|
btnCheckForUpdates=Comprobar Actualizaciones
|
||||||
btnDownloadSetPics=Descargar todas las Ediciones de Cartas
|
btnDownloadSetPics=Descargar todas las Ediciones de Cartas
|
||||||
btnDownloadPics=Descargar todas las Cartas
|
btnDownloadPics=Descargar todas las Cartas
|
||||||
btnDownloadPicsHQ=Descargar todas las Cartas en calidad alta (Muy lento!)
|
btnDownloadPicsHQ=Descargar todas las Cartas en calidad alta (Muy lento!)
|
||||||
@@ -192,6 +195,7 @@ btnImportPictures=Importar Datos
|
|||||||
btnHowToPlay=Cómo jugar (Inglés)
|
btnHowToPlay=Cómo jugar (Inglés)
|
||||||
btnDownloadPrices=Descargar los precios de las cartas
|
btnDownloadPrices=Descargar los precios de las cartas
|
||||||
btnLicensing=Detalles de la licencia
|
btnLicensing=Detalles de la licencia
|
||||||
|
lblCheckForUpdates=Comprueba si en el servidor de Forge existe alguna versión más reciente
|
||||||
lblDownloadPics=Descargar la imagen de la carta por defecto para cada carta.
|
lblDownloadPics=Descargar la imagen de la carta por defecto para cada carta.
|
||||||
lblDownloadPicsHQ=Descargar la imagen en calidad alta de la carta por defecto para cada carta.
|
lblDownloadPicsHQ=Descargar la imagen en calidad alta de la carta por defecto para cada carta.
|
||||||
lblDownloadSetPics=Descargue todas las imágenes de cada carta (una por cada edición donde apareció la carta)
|
lblDownloadSetPics=Descargue todas las imágenes de cada carta (una por cada edición donde apareció la carta)
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ btnResetJavaFutureCompatibilityWarnings=Ripristina avvisi di compatibilità Java
|
|||||||
btnClearImageCache=Cancella cache immagini
|
btnClearImageCache=Cancella cache immagini
|
||||||
btnTokenPreviewer=Anteprima token
|
btnTokenPreviewer=Anteprima token
|
||||||
btnCopyToClipboard=Copia negli appunti
|
btnCopyToClipboard=Copia negli appunti
|
||||||
|
cbpAutoUpdater=Auto updater
|
||||||
|
nlAutoUpdater=Select the release channel to use for updating Forge
|
||||||
cbpSelectLanguage=Lingua
|
cbpSelectLanguage=Lingua
|
||||||
nlSelectLanguage=Seleziona la lingua (parte di gioco esclusa. Ancora in fase di sviluppo) (RIAVVIO NECESSARIO)
|
nlSelectLanguage=Seleziona la lingua (parte di gioco esclusa. Ancora in fase di sviluppo) (RIAVVIO NECESSARIO)
|
||||||
cbRemoveSmall=Rimuovi le piccole creature
|
cbRemoveSmall=Rimuovi le piccole creature
|
||||||
@@ -180,6 +182,7 @@ KeyboardShortcuts=Tasti rapidi
|
|||||||
#VSubmenuAchievements.java
|
#VSubmenuAchievements.java
|
||||||
lblAchievements=realizzazioni
|
lblAchievements=realizzazioni
|
||||||
#VSubmenuDownloaders.java
|
#VSubmenuDownloaders.java
|
||||||
|
btnCheckForUpdates=Check for Updates
|
||||||
btnDownloadSetPics=Scarica LQ Set Pictures
|
btnDownloadSetPics=Scarica LQ Set Pictures
|
||||||
btnDownloadPicsHQ=Scarica le immagini della scheda HQ (molto lento!)
|
btnDownloadPicsHQ=Scarica le immagini della scheda HQ (molto lento!)
|
||||||
btnDownloadPics=Scarica LQ Card Pictures
|
btnDownloadPics=Scarica LQ Card Pictures
|
||||||
@@ -192,6 +195,7 @@ btnImportPictures=Importa dati
|
|||||||
btnHowToPlay=Come giocare
|
btnHowToPlay=Come giocare
|
||||||
btnDownloadPrices=Scarica i prezzi delle carte
|
btnDownloadPrices=Scarica i prezzi delle carte
|
||||||
btnLicensing=Dettagli della licenza
|
btnLicensing=Dettagli della licenza
|
||||||
|
lblCheckForUpdates=Check Forge server to see if there's a more recent release
|
||||||
lblDownloadPics=Scarica l''immagine della carta predefinita per ogni carta.
|
lblDownloadPics=Scarica l''immagine della carta predefinita per ogni carta.
|
||||||
lblDownloadPicsHQ=Scarica l''immagine HQ della scheda predefinita per ogni scheda.
|
lblDownloadPicsHQ=Scarica l''immagine HQ della scheda predefinita per ogni scheda.
|
||||||
lblDownloadSetPics=Scarica tutte le immagini di ogni carta (una per ogni set in cui è apparso la carta)
|
lblDownloadSetPics=Scarica tutte le immagini di ogni carta (una per ogni set in cui è apparso la carta)
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ btnResetJavaFutureCompatibilityWarnings=重置Java兼容性警告
|
|||||||
btnClearImageCache=清除图片缓存
|
btnClearImageCache=清除图片缓存
|
||||||
btnTokenPreviewer=衍生物预览器
|
btnTokenPreviewer=衍生物预览器
|
||||||
btnCopyToClipboard=复制到剪切板
|
btnCopyToClipboard=复制到剪切板
|
||||||
|
cbpAutoUpdater=Auto updater
|
||||||
|
nlAutoUpdater=Select the release channel to use for updating Forge
|
||||||
cbpSelectLanguage=语言
|
cbpSelectLanguage=语言
|
||||||
nlSelectLanguage=选择语言(除了正在进行中的游戏)(需要重新启动)
|
nlSelectLanguage=选择语言(除了正在进行中的游戏)(需要重新启动)
|
||||||
cbRemoveSmall=删除小生物
|
cbRemoveSmall=删除小生物
|
||||||
@@ -180,6 +182,7 @@ KeyboardShortcuts=键盘快捷键
|
|||||||
#VSubmenuAchievements.java
|
#VSubmenuAchievements.java
|
||||||
lblAchievements=成就
|
lblAchievements=成就
|
||||||
#VSubmenuDownloaders.java
|
#VSubmenuDownloaders.java
|
||||||
|
btnCheckForUpdates=Check for Updates
|
||||||
btnDownloadSetPics=下载低清系列图
|
btnDownloadSetPics=下载低清系列图
|
||||||
btnDownloadPicsHQ=下载高清卡图(这很慢!)
|
btnDownloadPicsHQ=下载高清卡图(这很慢!)
|
||||||
btnDownloadPics=下载低清卡图
|
btnDownloadPics=下载低清卡图
|
||||||
@@ -192,6 +195,7 @@ btnImportPictures=导入数据
|
|||||||
btnHowToPlay=如何玩
|
btnHowToPlay=如何玩
|
||||||
btnDownloadPrices=下载卡牌价格
|
btnDownloadPrices=下载卡牌价格
|
||||||
btnLicensing=许可证详情
|
btnLicensing=许可证详情
|
||||||
|
lblCheckForUpdates=Check Forge server to see if there's a more recent release
|
||||||
lblDownloadPics=下载缺省牌的图片
|
lblDownloadPics=下载缺省牌的图片
|
||||||
lblDownloadPicsHQ=下载缺省牌的高清图片
|
lblDownloadPicsHQ=下载缺省牌的高清图片
|
||||||
lblDownloadSetPics=下载每张牌的图片(每张牌出现一次)
|
lblDownloadSetPics=下载每张牌的图片(每张牌出现一次)
|
||||||
|
|||||||
18
forge-gui/res/puzzle/PS_THB6.pzl
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[metadata]
|
||||||
|
Name:Possibility Storm - Theros Beyond Death #06
|
||||||
|
URL:https://i0.wp.com/www.possibilitystorm.com/wp-content/uploads/2020/02/149.-THB6-1-scaled.jpg
|
||||||
|
Goal:Win
|
||||||
|
Turns:1
|
||||||
|
Difficulty:Uncommon
|
||||||
|
Description:Win this turn. Assume any unknown cards drawn by either player are not relevant to solving the puzzle. Your opponent starts with 13 cards in their library. Your opponent has a Pollenbright Druid on top of their library, and twelve other unknown cards in it.
|
||||||
|
[state]
|
||||||
|
humanlife=20
|
||||||
|
ailife=18
|
||||||
|
turn=1
|
||||||
|
activeplayer=human
|
||||||
|
activephase=MAIN1
|
||||||
|
humanhand=Assassin's Trophy;Unmoored Ego;Applied Biomancy;Underworld Dreams;Tyrant's Scorn
|
||||||
|
humanlibrary=Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt
|
||||||
|
humanbattlefield=Ob Nixilis, the Hate-Twisted|Counters:LOYALTY=2;Nessian Boar;Thief of Sanity;Thief of Sanity;Watery Grave|NoETBTrigs;Watery Grave|NoETBTrigs;Watery Grave|NoETBTrigs;Watery Grave|NoETBTrigs;Breeding Pool|NoETBTrigs;Breeding Pool|NoETBTrigs;Breeding Pool|NoETBTrigs
|
||||||
|
ailibrary=Pollenbright Druid;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt
|
||||||
|
aibattlefield=Silhana Wayfinder;Silhana Wayfinder;Blightbeetle;Wavebreak Hippocamp
|
||||||
16
forge-gui/res/puzzle/PS_THB7.pzl
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[metadata]
|
||||||
|
Name:Possibility Storm - Theros Beyond Death #07
|
||||||
|
URL:https://i2.wp.com/www.possibilitystorm.com/wp-content/uploads/2020/03/150.-THB7-scaled.jpg
|
||||||
|
Goal:Win
|
||||||
|
Turns:100
|
||||||
|
Difficulty:Mythic
|
||||||
|
Description:It's your OPPONENT'S turn (first main phase), and you need to win before you lose. Can you do it? Your opponent has no cards in hand and no available mana (assume they just tapped out to cast Goblin Assault Team). You control your opponent's Dreadhorde Butcher with The Akroan War's first chapter ability. Assume the puzzle starts with no cards in either player's graveyard.
|
||||||
|
[state]
|
||||||
|
humanlife=3
|
||||||
|
ailife=11
|
||||||
|
turn=1
|
||||||
|
activeplayer=ai
|
||||||
|
activephase=MAIN1
|
||||||
|
humanhand=Lazotep Plating;Slaying Fire;So Tiny;Shock;Gideon's Triumph;Aspect of Manticore
|
||||||
|
humanbattlefield=The Akroan War|Counters:LORE=2|ExecuteScript:DBGainControl->1;Blood Aspirant;Flux Channeler;Naiad of Hidden Coves;Temple of Enlightenment|NoETBTrigs;Temple of Enlightenment|NoETBTrigs;Sacred Foundry|NoETBTrigs;Sacred Foundry|NoETBTrigs
|
||||||
|
aibattlefield=Underworld Dreams;Underworld Dreams;Underworld Dreams;Ferocity of the Wilds;Goblin Assault Team;Temple Thief;Mire Triton;Dreadhorde Butcher|Id:1
|
||||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 163 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 534 KiB After Width: | Height: | Size: 533 KiB |
@@ -1,6 +1,6 @@
|
|||||||
Name:Wall
|
Name:Wall
|
||||||
ManaCost:no cost
|
ManaCost:no cost
|
||||||
Types:Creature Wall
|
Types:Artifact Creature Wall
|
||||||
PT:0/4
|
PT:0/4
|
||||||
K:Defender
|
K:Defender
|
||||||
Oracle:Defender
|
Oracle:Defender
|
||||||
@@ -230,6 +230,15 @@ public enum FSkinProp {
|
|||||||
ICO_QUEST_BIG_SWORD (new int[] {320, 1360, 160, 160}, PropType.ICON),
|
ICO_QUEST_BIG_SWORD (new int[] {320, 1360, 160, 160}, PropType.ICON),
|
||||||
ICO_QUEST_BIG_BAG (new int[] {480, 1360, 160, 160}, PropType.ICON),
|
ICO_QUEST_BIG_BAG (new int[] {480, 1360, 160, 160}, PropType.ICON),
|
||||||
|
|
||||||
|
//menu icon
|
||||||
|
ICO_MENU_GALAXY (new int[] {0, 1520, 80, 80}, PropType.ICON),
|
||||||
|
ICO_MENU_STATS (new int[] {80, 1520, 80, 80}, PropType.ICON),
|
||||||
|
ICO_MENU_PUZZLE (new int[] {160, 1520, 80, 80}, PropType.ICON),
|
||||||
|
ICO_MENU_GAUNTLET (new int[] {240, 1520, 80, 80}, PropType.ICON),
|
||||||
|
ICO_MENU_SEALED (new int[] {320, 1520, 80, 80}, PropType.ICON),
|
||||||
|
ICO_MENU_DRAFT (new int[] {400, 1520, 80, 80}, PropType.ICON),
|
||||||
|
ICO_MENU_CONSTRUCTED (new int[] {480, 1520, 80, 80}, PropType.ICON),
|
||||||
|
|
||||||
//interface icons
|
//interface icons
|
||||||
ICO_QUESTION (new int[] {560, 800, 32, 32}, PropType.ICON),
|
ICO_QUESTION (new int[] {560, 800, 32, 32}, PropType.ICON),
|
||||||
ICO_INFORMATION (new int[] {592, 800, 32, 32}, PropType.ICON),
|
ICO_INFORMATION (new int[] {592, 800, 32, 32}, PropType.ICON),
|
||||||
|
|||||||
247
forge-gui/src/main/java/forge/download/AutoUpdater.java
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
package forge.download;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import forge.GuiBase;
|
||||||
|
import forge.model.FModel;
|
||||||
|
import forge.properties.ForgePreferences;
|
||||||
|
import forge.util.BuildInfo;
|
||||||
|
import forge.util.FileUtil;
|
||||||
|
import forge.util.WaitCallback;
|
||||||
|
import forge.util.gui.SOptionPane;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class AutoUpdater {
|
||||||
|
private final String SNAPSHOT_VERSION_INDEX = "https://snapshots.cardforge.org/";
|
||||||
|
private final String SNAPSHOT_VERSION_URL = "https://snapshots.cardforge.org/version.txt";
|
||||||
|
private final String SNAPSHOT_PACKAGE = "https://snapshots.cardforge.org/latest/";
|
||||||
|
private final String RELEASE_VERSION_URL = "https://releases.cardforge.org/forge/forge-gui-desktop/version.txt";
|
||||||
|
private final String RELEASE_PACKAGE = "https://releases.cardforge.org/latest/";
|
||||||
|
private final String RELEASE_MAVEN_METADATA = "https://releases.cardforge.org/forge/forge-gui-desktop/maven-metadata.xml";
|
||||||
|
private static final boolean VERSION_FROM_METADATA = true;
|
||||||
|
private static final String TMP_DIR = "tmp/";
|
||||||
|
|
||||||
|
public static String[] updateChannels = new String[]{ "none", "snapshot", "release"};
|
||||||
|
|
||||||
|
private boolean isLoading;
|
||||||
|
private String updateChannel;
|
||||||
|
private String version;
|
||||||
|
private String buildVersion;
|
||||||
|
private String versionUrlString;
|
||||||
|
private String packageUrl;
|
||||||
|
private String packagePath;
|
||||||
|
|
||||||
|
public AutoUpdater(boolean loading) {
|
||||||
|
// What do I need? Preferences? Splashscreen? UI? Skins?
|
||||||
|
isLoading = loading;
|
||||||
|
updateChannel = FModel.getPreferences().getPref(ForgePreferences.FPref.AUTO_UPDATE);
|
||||||
|
buildVersion = BuildInfo.getVersionString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean attemptToUpdate() {
|
||||||
|
if (!verifyUpdateable()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (downloadUpdate()) {
|
||||||
|
extractAndRestart();
|
||||||
|
}
|
||||||
|
} catch(IOException | URISyntaxException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractAndRestart() {
|
||||||
|
extractUpdate();
|
||||||
|
restartForge();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verifyUpdateable() {
|
||||||
|
if (buildVersion.contains("GIT")) {
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
// TODO This doesn't work yet, because FSkin isn't loaded at the time.
|
||||||
|
return false;
|
||||||
|
} else if (updateChannel.equals("none")) {
|
||||||
|
String message = "You haven't set an update channel. Do you want to check a channel now?";
|
||||||
|
List<String> options = ImmutableList.of("Cancel", "release", "snapshot");
|
||||||
|
int option = SOptionPane.showOptionDialog(message, "Manual Check", null, options, 0);
|
||||||
|
if (option == 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
updateChannel = options.get(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildVersion.contains("SNAPSHOT")) {
|
||||||
|
if (!updateChannel.equals("snapshot")) {
|
||||||
|
System.out.println("Snapshot build versions must use snapshot update channel to work");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
versionUrlString = SNAPSHOT_VERSION_URL;
|
||||||
|
packageUrl = SNAPSHOT_PACKAGE;
|
||||||
|
} else {
|
||||||
|
versionUrlString = RELEASE_VERSION_URL;
|
||||||
|
packageUrl = RELEASE_PACKAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the internet connection
|
||||||
|
if (!testNetConnection()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download appropriate version file
|
||||||
|
return compareBuildWithLatestChannelVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean testNetConnection() {
|
||||||
|
try (Socket socket = new Socket()) {
|
||||||
|
InetSocketAddress address = new InetSocketAddress("releases.cardforge.org", 443);
|
||||||
|
socket.connect(address, 1000);
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false; // Either timeout or unreachable or failed DNS lookup.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean compareBuildWithLatestChannelVersion() {
|
||||||
|
try {
|
||||||
|
retrieveVersion();
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(version) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildVersion.equals(version)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// If version doesn't match, it's assummably newer.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void retrieveVersion() throws MalformedURLException {
|
||||||
|
if (VERSION_FROM_METADATA) {
|
||||||
|
if (updateChannel.equals("release")) {
|
||||||
|
extractVersionFromMavenRelease();
|
||||||
|
} else {
|
||||||
|
extractVersionFromSnapshotIndex();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
URL versionUrl = new URL(versionUrlString);
|
||||||
|
version = FileUtil.readFileToString(versionUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractVersionFromSnapshotIndex() throws MalformedURLException {
|
||||||
|
URL metadataUrl = new URL(SNAPSHOT_VERSION_INDEX);
|
||||||
|
String index = FileUtil.readFileToString(metadataUrl);
|
||||||
|
|
||||||
|
System.out.println(index);
|
||||||
|
Pattern p = Pattern.compile(">forge-(.*SNAPSHOT)");
|
||||||
|
Matcher m = p.matcher(index);
|
||||||
|
while(m.find()){
|
||||||
|
version = m.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractVersionFromMavenRelease() throws MalformedURLException {
|
||||||
|
URL metadataUrl = new URL(RELEASE_MAVEN_METADATA);
|
||||||
|
String xml = FileUtil.readFileToString(metadataUrl);
|
||||||
|
|
||||||
|
Pattern p = Pattern.compile("<release>(.*)</release>");
|
||||||
|
Matcher m = p.matcher(xml);
|
||||||
|
while(m.find()){
|
||||||
|
version = m.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean downloadUpdate() throws URISyntaxException, IOException {
|
||||||
|
// TODO Change the "auto" to be more auto.
|
||||||
|
if (isLoading) {
|
||||||
|
// We need to preload enough of a Skins to show a dialog and a button if we're in loading
|
||||||
|
// splashScreen.prepareForDialogs();
|
||||||
|
return downloadFromBrowser();
|
||||||
|
}
|
||||||
|
|
||||||
|
String message = "A new version of Forge is available (" + version + ").\n" +
|
||||||
|
"You are currently on version (" + buildVersion + ").\n\n" +
|
||||||
|
"Would you like to update to the new version now?";
|
||||||
|
|
||||||
|
final List<String> options = ImmutableList.of("Update Now", "Update Later");
|
||||||
|
if (SOptionPane.showOptionDialog(message, "New Version Available", null, options, 0) == 0) {
|
||||||
|
return downloadFromForge();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean downloadFromBrowser() throws URISyntaxException, IOException {
|
||||||
|
final Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
|
||||||
|
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
|
||||||
|
// Linking directly there will auto download, but won't auto-update
|
||||||
|
desktop.browse(new URI(packageUrl));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
System.out.println("Download latest version: " + packageUrl);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean downloadFromForge() {
|
||||||
|
WaitCallback<Boolean> callback = new WaitCallback<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
GuiBase.getInterface().download(new GuiDownloadZipService("Auto Updater", "Download the new version..", packageUrl, "tmp/", null, null) {
|
||||||
|
@Override
|
||||||
|
public void downloadAndUnzip() {
|
||||||
|
packagePath = download(version + "-upgrade.tar.bz2");
|
||||||
|
if (packagePath != null) {
|
||||||
|
extractAndRestart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(callback);
|
||||||
|
//
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractUpdate() {
|
||||||
|
// TODOD Something like https://stackoverflow.com/questions/315618/how-do-i-extract-a-tar-file-in-java
|
||||||
|
final Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
|
||||||
|
if (desktop != null) {
|
||||||
|
try {
|
||||||
|
desktop.open(new File(packagePath).getParentFile());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println(packagePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restartForge() {
|
||||||
|
if (isLoading || SOptionPane.showConfirmDialog("Forge has been downloaded. You should extract the package and restart Forge for the new version.", "Exit now?")) {
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -73,72 +73,7 @@ public class GuiDownloadZipService extends GuiDownloadService {
|
|||||||
String zipFilename = download("temp.zip");
|
String zipFilename = download("temp.zip");
|
||||||
if (zipFilename == null) { return; }
|
if (zipFilename == null) { return; }
|
||||||
|
|
||||||
//if assets.zip downloaded successfully, unzip into destination folder
|
extract(zipFilename);
|
||||||
try {
|
|
||||||
GuiBase.getInterface().preventSystemSleep(true); //prevent system from going into sleep mode while unzipping
|
|
||||||
|
|
||||||
if (deleteFolder != null) {
|
|
||||||
final File deleteDir = new File(deleteFolder);
|
|
||||||
if (deleteDir.exists()) {
|
|
||||||
//attempt to delete previous res directory if to be rebuilt
|
|
||||||
progressBar.reset();
|
|
||||||
progressBar.setDescription("Deleting old " + desc + "...");
|
|
||||||
if (deleteFolder.equals(destFolder)) { //move zip file to prevent deleting it
|
|
||||||
final String oldZipFilename = zipFilename;
|
|
||||||
zipFilename = deleteDir.getParentFile().getAbsolutePath() + File.separator + "temp.zip";
|
|
||||||
Files.move(new File(oldZipFilename), new File(zipFilename));
|
|
||||||
}
|
|
||||||
FileUtil.deleteDirectory(deleteDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final ZipFile zipFile = new ZipFile(zipFilename);
|
|
||||||
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
|
||||||
|
|
||||||
progressBar.reset();
|
|
||||||
progressBar.setPercentMode(true);
|
|
||||||
progressBar.setDescription("Extracting " + desc);
|
|
||||||
progressBar.setMaximum(zipFile.size());
|
|
||||||
|
|
||||||
FileUtil.ensureDirectoryExists(destFolder);
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
int failedCount = 0;
|
|
||||||
while (entries.hasMoreElements()) {
|
|
||||||
if (cancel) { break; }
|
|
||||||
|
|
||||||
try {
|
|
||||||
final ZipEntry entry = entries.nextElement();
|
|
||||||
|
|
||||||
final String path = destFolder + File.separator + entry.getName();
|
|
||||||
if (entry.isDirectory()) {
|
|
||||||
new File(path).mkdir();
|
|
||||||
progressBar.setValue(++count);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
copyInputStream(zipFile.getInputStream(entry), path);
|
|
||||||
progressBar.setValue(++count);
|
|
||||||
filesExtracted++;
|
|
||||||
}
|
|
||||||
catch (final Exception e) { //don't quit out completely if an entry is not UTF-8
|
|
||||||
progressBar.setValue(++count);
|
|
||||||
failedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failedCount > 0) {
|
|
||||||
Log.error("Downloading " + desc, failedCount + " " + desc + " could not be extracted");
|
|
||||||
}
|
|
||||||
|
|
||||||
zipFile.close();
|
|
||||||
new File(zipFilename).delete();
|
|
||||||
}
|
|
||||||
catch (final Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
GuiBase.getInterface().preventSystemSleep(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String download(final String filename) {
|
public String download(final String filename) {
|
||||||
@@ -211,6 +146,75 @@ public class GuiDownloadZipService extends GuiDownloadService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void extract(String zipFilename) {
|
||||||
|
//if assets.zip downloaded successfully, unzip into destination folder
|
||||||
|
try {
|
||||||
|
GuiBase.getInterface().preventSystemSleep(true); //prevent system from going into sleep mode while unzipping
|
||||||
|
|
||||||
|
if (deleteFolder != null) {
|
||||||
|
final File deleteDir = new File(deleteFolder);
|
||||||
|
if (deleteDir.exists()) {
|
||||||
|
//attempt to delete previous res directory if to be rebuilt
|
||||||
|
progressBar.reset();
|
||||||
|
progressBar.setDescription("Deleting old " + desc + "...");
|
||||||
|
if (deleteFolder.equals(destFolder)) { //move zip file to prevent deleting it
|
||||||
|
final String oldZipFilename = zipFilename;
|
||||||
|
zipFilename = deleteDir.getParentFile().getAbsolutePath() + File.separator + "temp.zip";
|
||||||
|
Files.move(new File(oldZipFilename), new File(zipFilename));
|
||||||
|
}
|
||||||
|
FileUtil.deleteDirectory(deleteDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final ZipFile zipFile = new ZipFile(zipFilename);
|
||||||
|
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||||
|
|
||||||
|
progressBar.reset();
|
||||||
|
progressBar.setPercentMode(true);
|
||||||
|
progressBar.setDescription("Extracting " + desc);
|
||||||
|
progressBar.setMaximum(zipFile.size());
|
||||||
|
|
||||||
|
FileUtil.ensureDirectoryExists(destFolder);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
int failedCount = 0;
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
if (cancel) { break; }
|
||||||
|
|
||||||
|
try {
|
||||||
|
final ZipEntry entry = entries.nextElement();
|
||||||
|
|
||||||
|
final String path = destFolder + File.separator + entry.getName();
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
new File(path).mkdir();
|
||||||
|
progressBar.setValue(++count);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
copyInputStream(zipFile.getInputStream(entry), path);
|
||||||
|
progressBar.setValue(++count);
|
||||||
|
filesExtracted++;
|
||||||
|
}
|
||||||
|
catch (final Exception e) { //don't quit out completely if an entry is not UTF-8
|
||||||
|
progressBar.setValue(++count);
|
||||||
|
failedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failedCount > 0) {
|
||||||
|
Log.error("Downloading " + desc, failedCount + " " + desc + " could not be extracted");
|
||||||
|
}
|
||||||
|
|
||||||
|
zipFile.close();
|
||||||
|
new File(zipFilename).delete();
|
||||||
|
}
|
||||||
|
catch (final Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
GuiBase.getInterface().preventSystemSleep(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void copyInputStream(final InputStream in, final String outPath) throws IOException {
|
protected void copyInputStream(final InputStream in, final String outPath) throws IOException {
|
||||||
final byte[] buffer = new byte[1024];
|
final byte[] buffer = new byte[1024];
|
||||||
int len;
|
int len;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import forge.card.CardType;
|
|||||||
import forge.deck.CardArchetypeLDAGenerator;
|
import forge.deck.CardArchetypeLDAGenerator;
|
||||||
import forge.deck.CardRelationMatrixGenerator;
|
import forge.deck.CardRelationMatrixGenerator;
|
||||||
import forge.deck.io.DeckPreferences;
|
import forge.deck.io.DeckPreferences;
|
||||||
|
import forge.download.AutoUpdater;
|
||||||
import forge.game.GameFormat;
|
import forge.game.GameFormat;
|
||||||
import forge.game.GameType;
|
import forge.game.GameType;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
@@ -117,7 +118,6 @@ public final class FModel {
|
|||||||
|
|
||||||
Localizer.getInstance().initialize(FModel.getPreferences().getPref(FPref.UI_LANGUAGE), ForgeConstants.LANG_DIR);
|
Localizer.getInstance().initialize(FModel.getPreferences().getPref(FPref.UI_LANGUAGE), ForgeConstants.LANG_DIR);
|
||||||
|
|
||||||
//load card database
|
|
||||||
final ProgressObserver progressBarBridge = (progressBar == null) ?
|
final ProgressObserver progressBarBridge = (progressBar == null) ?
|
||||||
ProgressObserver.emptyObserver : new ProgressObserver() {
|
ProgressObserver.emptyObserver : new ProgressObserver() {
|
||||||
@Override
|
@Override
|
||||||
@@ -143,6 +143,11 @@ public final class FModel {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (new AutoUpdater(true).attemptToUpdate()) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
//load card database
|
||||||
final CardStorageReader reader = new CardStorageReader(ForgeConstants.CARD_DATA_DIR, progressBarBridge,
|
final CardStorageReader reader = new CardStorageReader(ForgeConstants.CARD_DATA_DIR, progressBarBridge,
|
||||||
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
|
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
|
||||||
final CardStorageReader tokenReader = new CardStorageReader(ForgeConstants.TOKEN_DATA_DIR, progressBarBridge,
|
final CardStorageReader tokenReader = new CardStorageReader(ForgeConstants.TOKEN_DATA_DIR, progressBarBridge,
|
||||||
@@ -221,8 +226,6 @@ public final class FModel {
|
|||||||
achievements.put(GameType.PlanarConquest, new PlanarConquestAchievements());
|
achievements.put(GameType.PlanarConquest, new PlanarConquestAchievements());
|
||||||
achievements.put(GameType.Puzzle, new PuzzleAchievements());
|
achievements.put(GameType.Puzzle, new PuzzleAchievements());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//preload AI profiles
|
//preload AI profiles
|
||||||
AiProfileUtil.loadAllProfiles(ForgeConstants.AI_PROFILE_DIR);
|
AiProfileUtil.loadAllProfiles(ForgeConstants.AI_PROFILE_DIR);
|
||||||
|
|
||||||
|
|||||||