Merge branch 'coremaster' into respectbanlist

This commit is contained in:
maustin
2020-03-14 21:18:16 +00:00
104 changed files with 4775 additions and 432 deletions

View File

@@ -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));
}
@@ -219,9 +216,6 @@ public class ComputerUtil {
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
source.setCastSA(sa);
sa.setLastStateBattlefield(game.getLastStateBattlefield());
sa.setLastStateGraveyard(game.getLastStateGraveyard());
sa.setHostCard(game.getAction().moveToStack(source, sa));
}
@@ -246,9 +240,6 @@ public class ComputerUtil {
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
source.setCastSA(sa);
sa.setLastStateBattlefield(game.getLastStateBattlefield());
sa.setLastStateGraveyard(game.getLastStateGraveyard());
sa.setHostCard(game.getAction().moveToStack(source, sa));
}
@@ -267,9 +258,6 @@ public class ComputerUtil {
final Card source = newSA.getHostCard();
if (newSA.isSpell() && !source.isCopiedSpell()) {
source.setCastSA(newSA);
sa.setLastStateBattlefield(game.getLastStateBattlefield());
sa.setLastStateGraveyard(game.getLastStateGraveyard());
newSA.setHostCard(game.getAction().moveToStack(source, sa));
if (newSA.getApi() == ApiType.Charm && !newSA.isWrapper()) {
@@ -290,9 +278,6 @@ public class ComputerUtil {
if (ComputerUtilCost.canPayCost(sa, ai)) {
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
source.setCastSA(sa);
sa.setLastStateBattlefield(game.getLastStateBattlefield());
sa.setLastStateGraveyard(game.getLastStateGraveyard());
sa.setHostCard(game.getAction().moveToStack(source, sa));
}

View File

@@ -981,14 +981,27 @@ public abstract class GameState {
spellDef = spellDef.substring(0, spellDef.indexOf("->")).trim();
}
PaperCard pc = StaticData.instance().getCommonCards().getCard(spellDef);
Card c = null;
if (pc == null) {
System.err.println("ERROR: Could not find a card with name " + spellDef + " to precast!");
return;
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);
if (pc == null) {
System.err.println("ERROR: Could not find a card with name " + spellDef + " to precast!");
return;
}
c = Card.fromPaperCard(pc, activator);
}
Card c = Card.fromPaperCard(pc, activator);
SpellAbility sa = null;
if (!scriptID.isEmpty()) {

View File

@@ -113,7 +113,11 @@ public final class ImageKeys {
}
//try fullborder...
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 an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder

View File

@@ -312,17 +312,21 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
return tryGetCard(request);
}
public int getCardCollectorNumber(String cardName, String reqEdition) {
public String getCardCollectorNumber(String cardName, String reqEdition, int artIndex) {
cardName = getName(cardName);
CardEdition edition = editions.get(reqEdition);
if (edition == null)
return -1;
return null;
int numMatches = 0;
for (CardInSet card : edition.getCards()) {
if (card.name.equalsIgnoreCase(cardName)) {
return card.collectorNumber;
numMatches += 1;
if (numMatches == artIndex) {
return card.collectorNumber;
}
}
}
return -1;
return null;
}
private PaperCard tryGetCard(CardRequest request) {

View File

@@ -38,6 +38,8 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
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 final CardRarity rarity;
public final int collectorNumber;
public final String collectorNumber;
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.collectorNumber = collectorNumber;
this.rarity = rarity;
@@ -86,7 +88,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
public String toString() {
StringBuilder sb = new StringBuilder();
if (collectorNumber != -1) {
if (collectorNumber != null) {
sb.append(collectorNumber);
sb.append(' ');
}
@@ -190,6 +192,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
public boolean getSmallSetOverride() { return smallSetOverride; }
public String getBoosterMustContain() { return boosterMustContain; }
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; }
@@ -266,24 +269,33 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
Map<String, Integer> tokenNormalized = new HashMap<>();
List<CardEdition.CardInSet> processedCards = new ArrayList<>();
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")) {
if (StringUtils.isBlank(line))
continue;
// Optional collector number at the start.
String[] split = line.split(" ", 2);
int collectorNumber = -1;
if (split.length >= 2 && StringUtils.isNumeric(split[0])) {
collectorNumber = Integer.parseInt(split[0]);
line = split[1];
Matcher matcher = pattern.matcher(line);
if (matcher.matches()) {
String collectorNumber = matcher.group(2);
CardRarity r = CardRarity.smartValueOf(matcher.group(4));
String cardName = matcher.group(5);
CardInSet cis = new CardInSet(cardName, collectorNumber, r);
processedCards.add(cis);
}
// 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);
processedCards.add(cis);
}
}

View File

@@ -222,7 +222,12 @@ public final class CardRules implements ICardCharacteristics {
public boolean canBeBrawlCommander() {
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() {

View File

@@ -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_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> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER,
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.and(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. **/
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = com.google.common.base.Predicates

View File

@@ -463,6 +463,9 @@ public enum DeckFormat {
if (this.equals(DeckFormat.Brawl)) {
return rules.canBeBrawlCommander();
}
if (this.equals(DeckFormat.TinyLeaders)) {
return rules.canBeTinyLeadersCommander();
}
return rules.canBeCommander();
}

View File

@@ -5,6 +5,8 @@ import forge.item.PaperCard;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ImmutableSortedMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -17,6 +19,22 @@ import java.util.Map.Entry;
*/
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.
*

View File

@@ -547,6 +547,13 @@ public class GameAction {
c.setCastSA(null);
} else if (zoneTo.is(ZoneType.Stack)) {
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))) {
c.setCastFrom(null);
c.setCastSA(null);

View File

@@ -22,8 +22,10 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import forge.card.MagicColor;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.*;
@@ -32,9 +34,15 @@ import forge.game.cost.Cost;
import forge.game.keyword.KeywordInterface;
import forge.game.player.Player;
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.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.Lang;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
@@ -363,10 +371,11 @@ public final class GameActionUtil {
}
SpellAbility result = null;
final Card host = sa.getHostCard();
final Game game = host.getGame();
final Player activator = sa.getActivatingPlayer();
final PlayerController pc = activator.getController();
host.getGame().getAction().checkStaticAbilities(false);
game.getAction().checkStaticAbilities(false);
boolean reset = false;
@@ -429,7 +438,60 @@ public final class GameActionUtil {
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
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) {
result = sa.copy();
}

View File

@@ -6353,6 +6353,10 @@ public class Card extends GameEntity implements Comparable<Card> {
removeSVar("PayX"); // Temporary AI X announcement variable
removeSVar("IsCastFromPlayEffect"); // Temporary SVar indicating that the spell is cast indirectly via AF Play
setSunburstValue(0); // Sunburst
setXManaCostPaid(0);
setXManaCostPaidByColor(null);
setKickerMagnitude(0);
setPseudoMultiKickerMagnitude(0);
}
public final int getFinalChapterNr() {

View File

@@ -20,7 +20,6 @@ package forge.game.card;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -3010,24 +3009,43 @@ public class CardFactoryUtil {
inst.addTrigger(parsedTrigger);
} else if (keyword.startsWith("Saga")) {
// Saga there doesn't need Max value anymore?
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;
for (String ab : abs) {
SpellAbility sa = AbilityFactory.getAbility(card, ab);
sa.setChapter(i);
int idx = 0;
int skipId = 0;
for(String ab : abs) {
idx += 1;
if (idx <= skipId) {
continue;
}
// TODO better logic for Roman numbers
// In the Description try to merge Chapter trigger with the Same Effect
String trigStr = "Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield"
+ "| CounterType$ LORE | CounterAmount$ EQ" + i
+ "| TriggerDescription$ " + Strings.repeat("I", i) + " - " + sa.getDescription();
final Trigger t = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
t.setOverridingAbility(sa);
inst.addTrigger(t);
++i;
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);
sa.setChapter(i);
StringBuilder trigStr = new StringBuilder("Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield");
trigStr.append("| CounterType$ LORE | CounterAmount$ EQ").append(i);
if (i != idx) {
trigStr.append(" | Secondary$ True");
}
trigStr.append("| TriggerDescription$ ").append(desc).append("").append(sa.getDescription());
final Trigger t = TriggerHandler.parseTrigger(trigStr.toString(), card, intrinsic);
t.setOverridingAbility(sa);
inst.addTrigger(t);
}
}
} else if (keyword.equals("Soulbond")) {
// Setup ETB trigger for card with Soulbond keyword

View File

@@ -882,6 +882,10 @@ public class Combat {
return true; // is blocking something at the moment
}
if (!blocker.isLKI()) {
return false;
}
CombatLki lki = lkiCache.get(blocker);
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
}
if (!blocker.isLKI()) {
return false;
}
CombatLki lki = lkiCache.get(blocker);
return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band
}

View File

@@ -254,7 +254,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
}
}
if (mana.addsCounters(sa)) {
mana.getManaAbility().createETBCounters(host);
mana.getManaAbility().createETBCounters(host, this.owner);
}
if (mana.triggersWhenSpent()) {
mana.getManaAbility().addTriggersWhenSpent(sa, host);

View File

@@ -19,9 +19,11 @@ package forge.game.spellability;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.card.mana.ManaAtom;
import forge.game.Game;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
@@ -34,6 +36,8 @@ import forge.game.replacement.*;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.Lang;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
@@ -227,10 +231,26 @@ public class AbilityManaPart implements java.io.Serializable {
/**
* createETBCounters
*/
public void createETBCounters(Card c) {
public void createETBCounters(Card c, Player controller) {
String[] parse = this.addsCounters.split("_");
// Convert random SVars if there are other cards with this effect
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]
+ " | ETB$ True | CounterNum$ " + parse[2];
@@ -240,15 +260,37 @@ public class AbilityManaPart implements java.io.Serializable {
}
CardFactoryUtil.setupETBReplacementAbility(sa);
String repeffstr = "Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield "
+ " | Secondary$ True | Description$ CARDNAME"
+ " enters the battlefield with " + CounterType.valueOf(parse[1]).getName() + " counters.";
String desc = "It enters the battlefield with ";
desc += Lang.nounWithNumeral(parse[2], CounterType.valueOf(parse[1]).getName() + " counter");
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.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);
}
}

View File

@@ -1299,6 +1299,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
String announce = getParam("Announce");
if (StringUtils.isBlank(announce)) {
mapParams.put("Announce", variable);
originalMapParams.put("Announce", variable);
return;
}
String[] announcedOnes = TextUtil.split(announce, ',');
@@ -1308,6 +1309,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
}
}
mapParams.put("Announce", announce + ";" + variable);
originalMapParams.put("Announce", announce + ";" + variable);
}
public boolean isXCost() {

View File

@@ -100,17 +100,21 @@ public class TrackableTypes {
if (newCollection != null) {
//swap in objects in old collection for objects in new collection
for (int i = 0; i < newCollection.size(); i++) {
T newObj = newCollection.get(i);
if (newObj != null) {
T existingObj = from.getTracker().getObj(itemType, newObj.getId());
if (existingObj != null) { //if object exists already, update its changed properties
existingObj.copyChangedProps(newObj);
newCollection.remove(i);
newCollection.add(i, existingObj);
}
else { //if object is new, cache in object lookup
from.getTracker().putObj(itemType, newObj.getId(), newObj);
try {
T newObj = newCollection.get(i);
if (newObj != null) {
T existingObj = from.getTracker().getObj(itemType, newObj.getId());
if (existingObj != null) { //if object exists already, update its changed properties
existingObj.copyChangedProps(newObj);
newCollection.remove(i);
newCollection.add(i, existingObj);
}
else { //if object is new, cache in object lookup
from.getTracker().putObj(itemType, newObj.getId(), newObj);
}
}
} catch (IndexOutOfBoundsException e) {
System.err.println("got an IndexOutOfBoundsException, trying to continue ...");
}
}
}

View File

@@ -6,7 +6,7 @@
<uses-sdk
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.VIBRATE"/>
<uses-permission android:name="android.permission.INTERNET"/>

View File

@@ -142,7 +142,7 @@
<debug>true</debug>
</sign>
<sdk>
<platform>25</platform>
<platform>26</platform>
</sdk>
<dexForceJumbo>true</dexForceJumbo>
<androidManifestFile>${project.basedir}/AndroidManifest.xml</androidManifestFile>
@@ -183,7 +183,7 @@
<debug>false</debug>
</sign>
<sdk>
<platform>25</platform>
<platform>26</platform>
</sdk>
<zipalign>
<verbose>false</verbose>

View File

@@ -9,4 +9,4 @@
# Project target.
project.type=0
target=android-20
target=android-26

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#f0f0f0</color>
<color name="ic_launcher_background">#ffffff</color>
</resources>

View File

@@ -103,7 +103,7 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
case TinyLeaders:
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);
normalPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(), PaperCard.class);

View File

@@ -23,6 +23,13 @@ public enum CSubmenuDownloaders implements ICDoc {
VSubmenuDownloaders.SINGLETON_INSTANCE.showLicensing();
}
};
private final UiCommand cmdCheckForUpdates = new UiCommand() {
@Override
public void run() {
new AutoUpdater(false).attemptToUpdate();
}
};
private final UiCommand cmdPicDownload = new UiCommand() {
@Override public void run() {
new GuiDownloader(new GuiDownloadPicturesLQ()).show();
@@ -84,6 +91,7 @@ public enum CSubmenuDownloaders implements ICDoc {
@Override
public void initialize() {
final VSubmenuDownloaders view = VSubmenuDownloaders.SINGLETON_INSTANCE;
view.setCheckForUpdatesCommand(cmdCheckForUpdates);
view.setDownloadPicsCommand(cmdPicDownload);
view.setDownloadPicsHQCommand(cmdPicDownloadHQ);
view.setDownloadSetPicsCommand(cmdSetDownload);

View File

@@ -3,6 +3,7 @@ package forge.screens.home.settings;
import forge.*;
import forge.ai.AiProfileUtil;
import forge.control.FControl.CloseAction;
import forge.download.AutoUpdater;
import forge.game.GameLogEntryType;
import forge.gui.framework.FScreen;
import forge.gui.framework.ICDoc;
@@ -225,6 +226,7 @@ public enum CSubmenuPreferences implements ICDoc {
initializeGameLogVerbosityComboBox();
initializeCloseActionComboBox();
initializeDefaultFontSizeComboBox();
initializeAutoUpdaterComboBox();
initializeMulliganRuleComboBox();
initializeAiProfilesComboBox();
initializeStackAdditionsComboBox();
@@ -378,6 +380,16 @@ public enum CSubmenuPreferences implements ICDoc {
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() {
final String [] choices = MulliganDefs.getMulliganRuleNames();
final FPref userSetting = FPref.MULLIGAN_RULE;

View File

@@ -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 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 btnDownloadPics = _makeButton(localizer.getMessage("btnDownloadPics"));
private final FLabel btnDownloadPicsHQ = _makeButton(localizer.getMessage("btnDownloadPicsHQ"));
@@ -80,6 +81,9 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
if (javaRecentEnough()) {
pnlContent.add(btnCheckForUpdates, constraintsBTN);
pnlContent.add(_makeLabel(localizer.getMessage("lblCheckForUpdates")), constraintsLBL);
pnlContent.add(btnDownloadPics, constraintsBTN);
pnlContent.add(_makeLabel(localizer.getMessage("lblDownloadPics")), constraintsLBL);
@@ -162,6 +166,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
return EMenuGroup.SETTINGS;
}
public void setCheckForUpdatesCommand(UiCommand command) { btnCheckForUpdates.setCommand(command); }
public void setDownloadPicsCommand(UiCommand command) { btnDownloadPics.setCommand(command); }
public void setDownloadPicsHQCommand(UiCommand command) { btnDownloadPicsHQ.setCommand(command); }
public void setDownloadSetPicsCommand(UiCommand command) { btnDownloadSetPics.setCommand(command); }

View File

@@ -25,8 +25,8 @@ import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.*;
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> cbpGraveyardOrdering = new FComboBoxPanel<>(localizer.getMessage("cbpGraveyardOrdering")+":");
private final FComboBoxPanel<String> cbpDefaultLanguage = new FComboBoxPanel<>(localizer.getMessage("cbpSelectLanguage")+":");
private final FComboBoxPanel<String> cbpAutoUpdater = new FComboBoxPanel<>(localizer.getMessage("cbpAutoUpdater")+":");
/**
* Constructor.
@@ -157,6 +158,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
pnlPrefs.add(new SectionLabel(localizer.getMessage("GeneralConfiguration")), sectionConstraints);
// language
pnlPrefs.add(cbpAutoUpdater, comboBoxConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlAutoUpdater")), descriptionConstraints);
pnlPrefs.add(cbpDefaultLanguage, comboBoxConstraints);
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} */
public final JCheckBox getCbCompactMainMenu() {
return cbCompactMainMenu;

View File

@@ -81,7 +81,7 @@ public final class Main {
break;
default:
System.out.println("Unknown mode.\nKnown mode is 'sim' ");
System.out.println("Unknown mode.\nKnown mode is 'sim', 'parse' ");
break;
}

View File

@@ -209,7 +209,7 @@ public class FSkin {
textures.put(f6.path(), textures.get(f3.path()));
}
if (f7.exists()){
Texture t = new Texture(f7, false);
Texture t = new Texture(f7, true);
//t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
textures.put(f7.path(), t);
}

View File

@@ -193,6 +193,15 @@ public enum FSkinImage implements FImage {
QUEST_BIG_SWORD (FSkinProp.ICO_QUEST_BIG_SWORD, 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
QUESTION (FSkinProp.ICO_QUESTION, SourceFile.ICONS),
INFORMATION (FSkinProp.ICO_INFORMATION, SourceFile.ICONS),

View File

@@ -35,6 +35,9 @@ import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import static forge.card.CardRenderer.CROP_MULTIPLIER;
import static forge.card.CardRenderer.isModernFrame;
public class CardImageRenderer {
private static final float BASE_IMAGE_WIDTH = 360;
private static final float BASE_IMAGE_HEIGHT = 504;
@@ -357,13 +360,19 @@ public class CardImageRenderer {
float new_yRotate = (dispH - new_w) /2;
boolean rotateSplit = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS);
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 (Forge.enableUIMask){
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);
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(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
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);
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(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
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);
else {
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 {
if (canshow && !ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE).equals(card.getState(altState).getImageKey()))

View File

@@ -105,6 +105,7 @@ public class CardRenderer {
private static final float NAME_COST_THRESHOLD = Utils.scale(200);
private static final float BORDER_THICKNESS = Utils.scale(1);
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 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) {
if (compactMode) {
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) {
Texture image = new RendererCachedCardImage(pc, false).getImage();
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 == ImageCache.defaultImage) {
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);
else {
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
g.drawImage(image, x, y, w, h);
@@ -437,7 +486,12 @@ public class CardRenderer {
Texture image = new RendererCachedCardImage(card, false).getImage();
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
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 == ImageCache.defaultImage) {
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);
else {
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
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
@@ -461,7 +515,7 @@ public class CardRenderer {
else {
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.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 {
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) {
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) {
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)) {
boolean rotateSplit = isPreferenceEnabled(FPref.UI_ROTATE_SPLIT_CARDS) && card.isSplitCard() && inZoomer;

View File

@@ -219,7 +219,22 @@ public class CardZoom extends FOverlay {
float w = getWidth();
float h = getHeight();
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;

View File

@@ -856,6 +856,9 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
case Brawl:
isLegalCommander = card.getRules().canBeBrawlCommander();
break;
case TinyLeaders:
isLegalCommander = card.getRules().canBeTinyLeadersCommander();
break;
case Oathbreaker:
isLegalCommander = card.getRules().canBeOathbreaker();
captionSuffix = localizer.getMessage("lblOathbreaker");

View File

@@ -20,11 +20,11 @@ import forge.util.Localizer;
public class LoadGameMenu extends FPopupMenu {
public enum LoadGameScreen {
BoosterDraft("lblBoosterDraft", FSkinImage.HAND, LoadDraftScreen.class),
SealedDeck("lblSealedDeck", FSkinImage.PACK, LoadSealedScreen.class),
BoosterDraft("lblBoosterDraft", FSkinImage.MENU_DRAFT, LoadDraftScreen.class),
SealedDeck("lblSealedDeck", FSkinImage.MENU_SEALED, LoadSealedScreen.class),
QuestMode("lblQuestMode", FSkinImage.QUEST_ZEP, LoadQuestScreen.class),
PlanarConquest("lblPlanarConquest", FSkinImage.MULTIVERSE, LoadConquestScreen.class),
Gauntlet("lblGauntlet", FSkinImage.ALPHASTRIKE, LoadGauntletScreen.class);
PlanarConquest("lblPlanarConquest", FSkinImage.MENU_GALAXY, LoadConquestScreen.class),
Gauntlet("lblGauntlet", FSkinImage.MENU_GAUNTLET, LoadGauntletScreen.class);
private final FMenuItem item;
private final Class<? extends FScreen> screenClass;

View File

@@ -24,13 +24,13 @@ public class NewGameMenu extends FPopupMenu {
final static Localizer localizer = Localizer.getInstance();
public enum NewGameScreen {
Constructed(localizer.getMessage("lblConstructed"), FSkinImage.DECKLIST, ConstructedScreen.class),
BoosterDraft(localizer.getMessage("lblBoosterDraft"), FSkinImage.HAND, NewDraftScreen.class),
SealedDeck(localizer.getMessage("lblSealedDeck"), FSkinImage.PACK, NewSealedScreen.class),
Constructed(localizer.getMessage("lblConstructed"), FSkinImage.MENU_CONSTRUCTED, ConstructedScreen.class),
BoosterDraft(localizer.getMessage("lblBoosterDraft"), FSkinImage.MENU_DRAFT, NewDraftScreen.class),
SealedDeck(localizer.getMessage("lblSealedDeck"), FSkinImage.MENU_SEALED, NewSealedScreen.class),
QuestMode(localizer.getMessage("lblQuestMode"), FSkinImage.QUEST_ZEP, NewQuestScreen.class),
PuzzleMode(localizer.getMessage("lblPuzzleMode"), FSkinImage.QUEST_BOOK, PuzzleScreen.class),
PlanarConquest(localizer.getMessage("lblPlanarConquest"), FSkinImage.MULTIVERSE, NewConquestScreen.class),
Gauntlet(localizer.getMessage("lblGauntlet"), FSkinImage.ALPHASTRIKE, NewGauntletScreen.class);
PuzzleMode(localizer.getMessage("lblPuzzleMode"), FSkinImage.MENU_PUZZLE, PuzzleScreen.class),
PlanarConquest(localizer.getMessage("lblPlanarConquest"), FSkinImage.MENU_GALAXY, NewConquestScreen.class),
Gauntlet(localizer.getMessage("lblGauntlet"), FSkinImage.MENU_GAUNTLET, NewGauntletScreen.class);
private final FMenuItem item;
private final Class<? extends FScreen> screenClass;

View File

@@ -54,36 +54,45 @@ public class PuzzleScreen extends LaunchScreen {
final ArrayList<Puzzle> puzzles = PuzzleIO.loadPuzzles();
Collections.sort(puzzles);
GuiChoose.one(Localizer.getInstance().getMessage("lblChooseAPuzzle"), puzzles, new Callback<Puzzle>() {
GuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblChooseAPuzzle"), puzzles, new Callback<Puzzle>() {
@Override
public void run(final Puzzle chosen) {
LoadingOverlay.show(Localizer.getInstance().getMessage("lblLoadingThePuzzle"), new Runnable() {
@Override
public void run() {
// Load selected puzzle
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
hostedMatch.setStartGameHook(new Runnable() {
@Override
public final void run() {
chosen.applyToGame(hostedMatch.getGame());
}
});
if (chosen != null) {
LoadingOverlay.show(Localizer.getInstance().getMessage("lblLoadingThePuzzle"), new Runnable() {
@Override
public void run() {
// Load selected puzzle
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
hostedMatch.setStartGameHook(new Runnable() {
@Override
public final void run() {
chosen.applyToGame(hostedMatch.getGame());
}
});
final List<RegisteredPlayer> players = new ArrayList<>();
final RegisteredPlayer human = new RegisteredPlayer(new Deck()).setPlayer(GamePlayerUtil.getGuiPlayer());
human.setStartingHand(0);
players.add(human);
hostedMatch.setEndGameHook((new Runnable() {
@Override
public void run() {
chosen.savePuzzleSolve(hostedMatch.getGame().getOutcome().isWinner(GamePlayerUtil.getGuiPlayer()));
}
}));
final RegisteredPlayer ai = new RegisteredPlayer(new Deck()).setPlayer(GamePlayerUtil.createAiPlayer());
ai.setStartingHand(0);
players.add(ai);
final List<RegisteredPlayer> players = new ArrayList<>();
final RegisteredPlayer human = new RegisteredPlayer(new Deck()).setPlayer(GamePlayerUtil.getGuiPlayer());
human.setStartingHand(0);
players.add(human);
GameRules rules = new GameRules(GameType.Puzzle);
rules.setGamesPerMatch(1);
hostedMatch.startMatch(rules, null, players, human, GuiBase.getInterface().getNewGuiGame());
FOptionPane.showMessageDialog(chosen.getGoalDescription(), chosen.getName());
}
});
final RegisteredPlayer ai = new RegisteredPlayer(new Deck()).setPlayer(GamePlayerUtil.createAiPlayer());
ai.setStartingHand(0);
players.add(ai);
GameRules rules = new GameRules(GameType.Puzzle);
rules.setGamesPerMatch(1);
hostedMatch.startMatch(rules, null, players, human, GuiBase.getInterface().getNewGuiGame());
FOptionPane.showMessageDialog(chosen.getGoalDescription(), chosen.getName());
}
});
}
}
});

View File

@@ -56,7 +56,7 @@ public class ConquestMenu extends FPopupMenu {
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
public void handleEvent(FEvent e) {
setCurrentScreen(statsScreen);

View File

@@ -244,10 +244,7 @@ public class ConquestRewardDialog extends FScrollPane {
//ensure current card in view
if (getScrollHeight() > getHeight() && index < cardCount) {
CardRevealer currentCard = cardRevealers.get(index);
if (!Forge.extrawide.equals("default"))
scrollIntoView(currentCard, currentCard.getHeight() / (columnCount * PADDING) / 2);
else
scrollIntoView(currentCard, currentCard.getHeight() / 2 + PADDING); //show half of the card below
scrollIntoView(currentCard, currentCard.getHeight() / (columnCount * PADDING) / 2);
}
}

View File

@@ -79,7 +79,7 @@ public class QuestMenu extends FPopupMenu implements IVQuestStats {
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
public void handleEvent(FEvent e) {
setCurrentScreen(statsScreen);

View File

@@ -84,3 +84,4 @@ Modern Horizons, 3/6/WAR, MH1
Core Set 2020, 3/6/M20, M20
Throne of Eldraine, 3/6/ELD, ELD
Theros Beyond Death, 3/6/THB, THB
Mystery Booster, 3/6/THB, MB1

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,9 @@ Types:Creature Elemental Knight
PT:4/6
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.
SVar:TrigDestroy:DB$ Destroy | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | SubAbility$ DBToken
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_3_3_a_golem | TokenOwner$ TargetedController | LegacyImage$ c 3 3 a golem m20
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$ 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.
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.

View File

@@ -1,7 +1,7 @@
Name:Ghastly Demise
ManaCost:B
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: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.

View File

@@ -1,7 +1,7 @@
Name:Joust
ManaCost:1 R
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
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.)

View File

@@ -1,9 +1,10 @@
Name:Nissa's Pilgrimage
ManaCost:2 G
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.
SVar:DBChangeZone1:DB$ChangeZone | Origin$ Library | Destination$ Hand | SubAbility$ DBChangeZone2 | ChangeType$ Land.Basic+Forest | ChangeNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ LT2 | References$ X
SVar:DBChangeZone2:DB$ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Basic+Forest | ChangeNum$ 2 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE2 | References$ X
SVar:X:Count$ValidGraveyard Instant.YouOwn,Sorcery.YouOwn
SVar:Picture:http://www.wizards.com/global/images/magic/general/nissas_pilgrimage.jpg
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: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:DBHand:DB$ ChangeZone | Origin$ Library | Destination$ Hand | Defined$ Remembered | NoLooking$ True | StackDescription$ None | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
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.

View File

@@ -2,7 +2,7 @@ Name:Settle the Wreckage
ManaCost:2 W W
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.
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:X:Count$RememberedSize
SVar:Picture:http://www.wizards.com/global/images/magic/general/settle_the_wreckage.jpg

View File

@@ -3,7 +3,7 @@ ManaCost:1 W
Types:Enchantment Saga
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: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.
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.

View File

@@ -1,7 +1,7 @@
Name:The Triumph of Anax
ManaCost:2 R
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:X:Count$CardCounters.LORE
SVar:PlayMain1:TRUE

View File

@@ -196,14 +196,14 @@ AdditionalSetUnlockedInQuest=MPS_KLD
183 C Welder Automaton
184 R Spire of Industry
185 M Ajani, Valiant Protector
186 R Ajani's Aid
186 C Inspiring Roar
187 U Ajani's Comrade
188 C Inspiring Roar
188 R Ajani's Aid
189 C Tranquil Expanse
190 M Tezzeret, Master of Metal
191 R Tezzeret's Betrayal
192 U Tezzeret's Simulacrum
193 C Pendulum of Patterns
192 C Pendulum of Patterns
193 U Tezzeret's Simulacrum
194 C Submerged Boneyard
[tokens]

View File

@@ -27,10 +27,10 @@ Foil=NotSupported
17 C Curfew
18 C Dark Ritual
19 R Dirtcowl Wurm
20 U Disenchant
20 C Disenchant
21 C Disruptive Student
22 C Drifting Meadow
23 C Elvish Lyrist
23 U Elvish Lyrist
24 C Exhume
25 U Fecundity
26 C Fertile Ground
@@ -40,16 +40,16 @@ Foil=NotSupported
30 C Gorilla Warrior
31 C Healing Salve
32 C Heat Ray
33 R Hurricane
33 U Hurricane
34 C Infantry Veteran
35 R Land Tax
35 U Land Tax
36 R Lhurgoyf
37 C Lightning Elemental
38 R Living Death
39 C Llanowar Elves
40 C Man-o'-War
41 C Mana Leak
42 U Maniacal Rage
42 C Maniacal Rage
43 C Manta Riders
44 C Master Decoy
45 U Mogg Hollows
@@ -59,7 +59,7 @@ Foil=NotSupported
49 U Pestilence
50 C Phyrexian Ghoul
51 C Pincher Beetles
52 U Plated Rootwalla
52 C Plated Rootwalla
53 C Polluted Mire
54 C Prodigal Sorcerer
55 C Raging Goblin
@@ -72,7 +72,7 @@ Foil=NotSupported
62 C Sanctum Custodian
63 U Sanctum Guardian
64 C Sandstorm
65 U Scaled Wurm
65 C Scaled Wurm
66 C Scryb Sprites
67 U Seasoned Marshal
68 C Seeker of Skybreak
@@ -83,7 +83,7 @@ Foil=NotSupported
73 C Slippery Karst
74 C Soltari Foot Soldier
75 U Songstitcher
76 U Soul Warden
76 C Soul Warden
77 C Spike Colony
78 U Spike Feeder
79 R Spike Weaver

View File

@@ -135,7 +135,7 @@ Booster=10 Common, 3 Uncommon, 1 fromSheet("BBD RareMythic"), 1 BasicLand
125 C Omenspeaker
126 U Opportunity
127 U Oracle's Insight
128 C Peregrine Drake
128 U Peregrine Drake
129 U Phantom Warrior
130 U Reckless Scholar
131 R Sower of Temptation
@@ -203,7 +203,7 @@ Booster=10 Common, 3 Uncommon, 1 fromSheet("BBD RareMythic"), 1 BasicLand
193 C Cowl Prowler
194 C Daggerback Basilisk
195 M Doubling Season
196 U Elvish Visionary
196 C Elvish Visionary
197 U Feral Hydra
198 C Fertile Ground
199 U Fertilid

View File

@@ -76,7 +76,7 @@ Foil=NotSupported
66 C Yavimaya Wurm
67 U Diabolic Vision
68 U Segmented Wurm
69 C Clockwork Avian
69 R Clockwork Avian
70 R Clockwork Beast
71 U Dwarven Ruins
72 U Ebon Stronghold

View File

@@ -6,7 +6,8 @@ Code2=CN2
MciCode=cn2
Type=Other
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]
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
220 U Rogue's Passage
221 U Shimmering Grotto
222 M Kaya, Ghost Assassin
[tokens]
w_1_1_soldier

View File

@@ -184,14 +184,14 @@ Booster=10 Common:!land, 3 Uncommon, 1 RareMythic, 1 fromSheet("FRF Lands"), 0 f
174 C Tranquil Cove
175 C Wind-Scarred Crag
176 L Plains
176 L Plains
179 L Island
177 L Plains
178 L Island
179 L Island
180 L Swamp
181 L Swamp
181 L Swamp
182 L Mountain
183 L Mountain
183 L Mountain
185 L Forest
184 L Forest
185 L Forest
[tokens]

View File

@@ -258,26 +258,26 @@ Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand KTK
247 C Wind-Scarred Crag
248 R Windswept Heath
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
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
258 L Swamp
259 L Swamp
260 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]
w_3_4_bird_flying

View File

@@ -238,26 +238,26 @@ Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand
227 U Encroaching Wastes
228 R Mutavault
229 U Shimmering Grotto
230 L Plains
231 L Plains
231 L Plains
231 L Plains
231 L Plains
234 L Island
234 L Island
234 L Island
232 L Plains
233 L Plains
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
241 L Swamp
244 L Mountain
244 L Mountain
244 L Mountain
242 L Mountain
243 L Mountain
244 L Mountain
245 L Mountain
246 L Forest
246 L Forest
246 L Forest
246 L Forest
247 L Forest
248 L Forest
249 L Forest
[tokens]
c_1_1_sliver

View File

@@ -258,26 +258,26 @@ Booster=10 Common:!fromSheet("M15 Sample Cards"), 3 Uncommon:!fromSheet("M15 Sam
247 R Sliver Hive
248 R Urborg, Tomb of Yawgmoth
249 R Yavimaya Coast
250 L Plains
251 L Plains
251 L Plains
251 L Plains
251 L Plains
255 L Island
255 L Island
255 L Island
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
258 L Swamp
263 L Mountain
263 L Mountain
263 L Mountain
259 L Swamp
260 L Swamp
261 L Swamp
262 L Mountain
263 L Mountain
264 L Mountain
265 L Mountain
266 L Forest
266 L Forest
266 L Forest
266 L Forest
267 L Forest
268 L Forest
269 L Forest
270 R Aegis Angel
271 C Divine Verdict
272 C Inspired Charge

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -367,17 +367,17 @@ Prerelease=6 Boosters, 1 RareMythic+
350 R Temple of Malice
351 R Temple of Plenty
#Bundle promo
R Arasta of the Endless Web
352 R Arasta of the Endless Web
#Promo Pack
U Alseid of Life's Bounty
C Thirst for Meaning
U Gray Merchant of Asphodel
C Thrill of Possibility
U Wolfwillow Haven
353 U Alseid of Life's Bounty
354 C Thirst for Meaning
355 U Gray Merchant of Asphodel
356 C Thrill of Possibility
357 U Wolfwillow Haven
[tokens]
b_2_2_zombie
c_0_4_wall_defender
c_0_4_a_wall_defender
g_1_2_spider_reach
g_2_2_wolf
r_x_1_elemental_trample_haste

View File

@@ -284,36 +284,36 @@ Prerelease=6 Boosters, 1 RareMythic+
271 M Oko, Thief of Crowns
272 M The Royal Scions
#Storybook Frames
C Ardenvale Tactician
C Faerie Guidemother
R Giant Killer
C Lonesome Unicorn
M Realm-Cloaked Giant
U Shepherd of the Flock
C Silverflame Squire
U Animating Faerie
M Brazen Borrower
R Fae of Wishes
U Hypnotic Sprite
C Merfolk Secretkeeper
C Queen of Ice
U Foulmire Knight
R Murderous Rider
U Order of Midnight
C Reaper of Night
C Smitten Swordmaster
R Bonecrusher Giant
U Embereth Shieldbreaker
C Merchant of the Vale
C Rimrock Knight
U Beanstalk Giant
C Curious Pair
U Flaxen Intruder
C Garenbrig Carver
R Lovestruck Beast
C Rosethorn Acolyte
C Tuinvale Treefolk
U Oakhame Ranger
273 C Ardenvale Tactician
274 C Faerie Guidemother
275 R Giant Killer
276 C Lonesome Unicorn
277 M Realm-Cloaked Giant
278 U Shepherd of the Flock
279 C Silverflame Squire
280 U Animating Faerie
281 M Brazen Borrower
282 R Fae of Wishes
283 U Hypnotic Sprite
284 C Merfolk Secretkeeper
285 C Queen of Ice
286 U Foulmire Knight
287 R Murderous Rider
288 U Order of Midnight
289 C Reaper of Night
290 C Smitten Swordmaster
291 R Bonecrusher Giant
292 U Embereth Shieldbreaker
293 C Merchant of the Vale
294 C Rimrock Knight
295 U Beanstalk Giant
296 C Curious Pair
297 U Flaxen Intruder
298 C Garenbrig Carver
299 R Lovestruck Beast
300 C Rosethorn Acolyte
301 C Tuinvale Treefolk
302 U Oakhame Ranger
#Buy-A-Box Promo
303 M Kenrith, the Returned King
#Planeswalker Deck Cards
@@ -348,72 +348,72 @@ U Oakhame Ranger
332 R Tome of Legends
333 C Command Tower
#Borderless art rares and mythics
R Acclaimed Contender
R Charming Prince
M The Circle of Loyalty
R Happily Ever After
M Harmonious Archon
R Hushbringer
R Linden, the Steadfast Queen
R Worthy Knight
R Emry, Lurker of the Loch
R Folio of Fancies
R Gadwick, the Wizened
M The Magic Mirror
R Midnight Clock
R Mirrormade
R Stolen by the Fae
R Vantress Gargoyle
R Ayara, First of Locthwain
R Blacklance Paragon
M The Cauldron of Eternity
R Clackbridge Troll
R Oathsworn Knight
R Piper of the Swarm
M Rankle, Master of Pranks
R Wishclaw Talisman
R Witch's Vengeance
M Embercleave
R Fervent Champion
R Fires of Invention
R Irencrag Feat
R Irencrag Pyromancer
R Opportunistic Dragon
M Robber of the Rich
R Sundering Stroke
R Torbran, Thane of Red Fell
R Feasting Troll King
R Gilded Goose
M The Great Henge
R Once Upon A Time
M Questing Beast
R Return of the Wildspeaker
R Wicked Wolf
R Wildborn Preserver
R Yorvo, Lord of Garenbrig
R Dance of the Manse
R Doom Foretold
R Escape to the Wilds
R Faeburrow Elder
R Lochmere Serpent
M Outlaws' Merriment
R Stormfist Crusader
R Sorcerous Spyglass
R Stonecoil Serpent
R Castle Ardenvale
R Castle Embereth
R Castle Garenbrig
R Castle Locthwain
R Castle Vantress
R Fabled Passage
334 R Acclaimed Contender
335 R Charming Prince
336 M The Circle of Loyalty
337 R Happily Ever After
338 M Harmonious Archon
339 R Hushbringer
340 R Linden, the Steadfast Queen
341 R Worthy Knight
342 R Emry, Lurker of the Loch
343 R Folio of Fancies
344 R Gadwick, the Wizened
345 M The Magic Mirror
346 R Midnight Clock
347 R Mirrormade
348 R Stolen by the Fae
349 R Vantress Gargoyle
350 R Ayara, First of Locthwain
351 R Blacklance Paragon
352 M The Cauldron of Eternity
353 R Clackbridge Troll
354 R Oathsworn Knight
355 R Piper of the Swarm
356 M Rankle, Master of Pranks
357 R Wishclaw Talisman
358 R Witch's Vengeance
359 M Embercleave
360 R Fervent Champion
361 R Fires of Invention
362 R Irencrag Feat
363 R Irencrag Pyromancer
364 R Opportunistic Dragon
365 M Robber of the Rich
366 R Sundering Stroke
367 R Torbran, Thane of Red Fell
368 R Feasting Troll King
369 R Gilded Goose
370 M The Great Henge
371 R Once Upon a Time
372 M Questing Beast
373 R Return of the Wildspeaker
374 R Wicked Wolf
375 R Wildborn Preserver
376 R Yorvo, Lord of Garenbrig
377 R Dance of the Manse
378 R Doom Foretold
379 R Escape to the Wilds
380 R Faeburrow Elder
381 R Lochmere Serpent
382 M Outlaws' Merriment
383 R Stormfist Crusader
384 R Sorcerous Spyglass
385 R Stonecoil Serpent
386 R Castle Ardenvale
387 R Castle Embereth
388 R Castle Garenbrig
389 R Castle Locthwain
390 R Castle Vantress
391 R Fabled Passage
#Bundle promo
R Piper of the Swarm
392 R Piper of the Swarm
#Promo Pack
U Glass Casket
U Slaying Fire
U Kenrith's Transformation
U Improbable Alliance
U Inspiring Veteran
393 U Glass Casket
394 U Slaying Fire
395 U Kenrith's Transformation
396 U Improbable Alliance
397 U Inspiring Veteran
[tokens]
w_0_1_goat

View File

@@ -178,5 +178,5 @@ R Winding Canyons
R Xanthic Statue
C Zombie Scavengers
[token]
[tokens]
g_1_1_squirrel

View File

@@ -4,4 +4,4 @@ Order:101
Type:Casual
Subtype:Commander
Sets:GRN, RNA, WAR, M20, ELD, THB
Banned:Sorcerous Spyglass;Oko, Thief of Crowns
Banned:Golos, Tireless Pilgrim; Oko, Thief of Crowns; Sorcerous Spyglass

View File

@@ -3,4 +3,4 @@ Name:Legacy
Order:105
Subtype:Legacy
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

View File

@@ -4,4 +4,4 @@ Order:103
Subtype:Modern
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
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

View File

@@ -50,6 +50,8 @@ btnResetJavaFutureCompatibilityWarnings=Java-Kompatibilitätswarnung zurücksetz
btnClearImageCache=Leere Bildspeicher
btnTokenPreviewer=Spielstein-Vorschau
btnCopyToClipboard=In Zwischenablage kopieren
cbpAutoUpdater=Auto updater
nlAutoUpdater=Select the release channel to use for updating Forge
cbpSelectLanguage=Sprache
nlSelectLanguage=Wähle Sprache (Ist noch in Arbeit und nur teilweise umgesetzt.) (Neustart ist erforderlich.)
cbRemoveSmall=Entferne kleine Kreaturen
@@ -180,6 +182,7 @@ KeyboardShortcuts=Tastenkombinationen
#VSubmenuAchievements.java
lblAchievements=Errungenschaften
#VSubmenuDownloaders.java
btnCheckForUpdates=Check for Updates
btnDownloadSetPics=Bilder(LQ) Sets herunterladen
btnDownloadPicsHQ=Bilder(HQ) Karten herunterladen (Sehr langsam!)
btnDownloadPics=Bilder(LQ) Karten herunterladen
@@ -192,6 +195,7 @@ btnImportPictures=Daten importieren
btnHowToPlay=Wie man spielt
btnDownloadPrices=Kartenpreise herunterladen
btnLicensing=Lizenzhinweis
lblCheckForUpdates=Check Forge server to see if there's a more recent release
lblDownloadPics=Lädt ein 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.

View File

@@ -50,6 +50,8 @@ btnResetJavaFutureCompatibilityWarnings=Reset Java Compatibility Warnings
btnClearImageCache=Clear Image Cache
btnTokenPreviewer=Token Previewer
btnCopyToClipboard=Copy to Clipboard
cbpAutoUpdater=Auto updater
nlAutoUpdater=Select the release channel to use for updating Forge
cbpSelectLanguage=Language
nlSelectLanguage=Select Language (Excluded Game part. Still a work in progress) (RESTART REQUIRED)
cbRemoveSmall=Remove Small Creatures
@@ -180,6 +182,7 @@ KeyboardShortcuts=Keyboard Shortcuts
#VSubmenuAchievements.java
lblAchievements=Achievements
#VSubmenuDownloaders.java
btnCheckForUpdates=Check for Updates
btnDownloadSetPics=Download LQ Set Pictures
btnDownloadPicsHQ=Download HQ Card Pictures (Very Slow!)
btnDownloadPics=Download LQ Card Pictures
@@ -192,6 +195,7 @@ btnImportPictures=Import Data
btnHowToPlay=How To Play
btnDownloadPrices=Download Card Prices
btnLicensing=License Details
lblCheckForUpdates=Check Forge server to see if there's a more recent release
lblDownloadPics=Download default card 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)

View File

@@ -50,6 +50,8 @@ btnResetJavaFutureCompatibilityWarnings=Restablecer las advertencias de compatib
btnClearImageCache=Limpiar Caché de Imágenes
btnTokenPreviewer=Previsualizador de Fichas (Token)
btnCopyToClipboard=Copiar al portapapeles
cbpAutoUpdater=Actualizar Forge
nlAutoUpdater=Selecciona la versión a utilizar para actualizar Forge
cbpSelectLanguage=Idioma
nlSelectLanguage=Seleccionar idioma (excepto partida). Todavía un trabajo en progreso) (Es necesario reiniciar Forge)
cbRemoveSmall=Eliminar Pequeñas Criaturas
@@ -180,6 +182,7 @@ KeyboardShortcuts=Atajos de teclado
#VSubmenuAchievements.java
lblAchievements=Logros
#VSubmenuDownloaders.java
btnCheckForUpdates=Comprobar Actualizaciones
btnDownloadSetPics=Descargar todas las Ediciones de Cartas
btnDownloadPics=Descargar todas las Cartas
btnDownloadPicsHQ=Descargar todas las Cartas en calidad alta (Muy lento!)
@@ -192,6 +195,7 @@ btnImportPictures=Importar Datos
btnHowToPlay=Cómo jugar (Inglés)
btnDownloadPrices=Descargar los precios de las cartas
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.
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)

View File

@@ -50,6 +50,8 @@ btnResetJavaFutureCompatibilityWarnings=Ripristina avvisi di compatibilità Java
btnClearImageCache=Cancella cache immagini
btnTokenPreviewer=Anteprima token
btnCopyToClipboard=Copia negli appunti
cbpAutoUpdater=Auto updater
nlAutoUpdater=Select the release channel to use for updating Forge
cbpSelectLanguage=Lingua
nlSelectLanguage=Seleziona la lingua (parte di gioco esclusa. Ancora in fase di sviluppo) (RIAVVIO NECESSARIO)
cbRemoveSmall=Rimuovi le piccole creature
@@ -180,6 +182,7 @@ KeyboardShortcuts=Tasti rapidi
#VSubmenuAchievements.java
lblAchievements=realizzazioni
#VSubmenuDownloaders.java
btnCheckForUpdates=Check for Updates
btnDownloadSetPics=Scarica LQ Set Pictures
btnDownloadPicsHQ=Scarica le immagini della scheda HQ (molto lento!)
btnDownloadPics=Scarica LQ Card Pictures
@@ -192,6 +195,7 @@ btnImportPictures=Importa dati
btnHowToPlay=Come giocare
btnDownloadPrices=Scarica i prezzi delle carte
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.
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)

View File

@@ -50,6 +50,8 @@ btnResetJavaFutureCompatibilityWarnings=重置Java兼容性警告
btnClearImageCache=清除图片缓存
btnTokenPreviewer=衍生物预览器
btnCopyToClipboard=复制到剪切板
cbpAutoUpdater=Auto updater
nlAutoUpdater=Select the release channel to use for updating Forge
cbpSelectLanguage=语言
nlSelectLanguage=选择语言(除了正在进行中的游戏)(需要重新启动)
cbRemoveSmall=删除小生物
@@ -180,6 +182,7 @@ KeyboardShortcuts=键盘快捷键
#VSubmenuAchievements.java
lblAchievements=成就
#VSubmenuDownloaders.java
btnCheckForUpdates=Check for Updates
btnDownloadSetPics=下载低清系列图
btnDownloadPicsHQ=下载高清卡图(这很慢!)
btnDownloadPics=下载低清卡图
@@ -192,6 +195,7 @@ btnImportPictures=导入数据
btnHowToPlay=如何玩
btnDownloadPrices=下载卡牌价格
btnLicensing=许可证详情
lblCheckForUpdates=Check Forge server to see if there's a more recent release
lblDownloadPics=下载缺省牌的图片
lblDownloadPicsHQ=下载缺省牌的高清图片
lblDownloadSetPics=下载每张牌的图片(每张牌出现一次)

View 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

View 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 534 KiB

After

Width:  |  Height:  |  Size: 533 KiB

View File

@@ -1,6 +1,6 @@
Name:Wall
ManaCost:no cost
Types:Creature Wall
Types:Artifact Creature Wall
PT:0/4
K:Defender
Oracle:Defender

View File

@@ -230,6 +230,15 @@ public enum FSkinProp {
ICO_QUEST_BIG_SWORD (new int[] {320, 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
ICO_QUESTION (new int[] {560, 800, 32, 32}, PropType.ICON),
ICO_INFORMATION (new int[] {592, 800, 32, 32}, PropType.ICON),

View 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);
}
}
}

View File

@@ -73,72 +73,7 @@ public class GuiDownloadZipService extends GuiDownloadService {
String zipFilename = download("temp.zip");
if (zipFilename == null) { return; }
//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);
}
extract(zipFilename);
}
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 {
final byte[] buffer = new byte[1024];
int len;

View File

@@ -28,6 +28,7 @@ import forge.card.CardType;
import forge.deck.CardArchetypeLDAGenerator;
import forge.deck.CardRelationMatrixGenerator;
import forge.deck.io.DeckPreferences;
import forge.download.AutoUpdater;
import forge.game.GameFormat;
import forge.game.GameType;
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);
//load card database
final ProgressObserver progressBarBridge = (progressBar == null) ?
ProgressObserver.emptyObserver : new ProgressObserver() {
@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,
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
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.Puzzle, new PuzzleAchievements());
//preload AI profiles
AiProfileUtil.loadAllProfiles(ForgeConstants.AI_PROFILE_DIR);

Some files were not shown because too many files have changed in this diff Show More