diff --git a/forge-ai/pom.xml b/forge-ai/pom.xml
index 3f176653a65..b8d21660c54 100644
--- a/forge-ai/pom.xml
+++ b/forge-ai/pom.xml
@@ -6,7 +6,7 @@
forgeforge
- 1.6.33-SNAPSHOT
+ 1.6.34-SNAPSHOTforge-ai
diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java
index ff73d875785..bec6e19c4d6 100644
--- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java
+++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java
@@ -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));
}
@@ -2338,7 +2323,7 @@ public class ComputerUtil {
return chosen;
}
- public static Object vote(Player ai, List
* @param saBeingPaid
- *
+ *
* @return a {@link java.lang.String} object.
*/
public boolean addKeywords(SpellAbility saBeingPaid) {
@@ -205,7 +209,7 @@ public class AbilityManaPart implements java.io.Serializable {
*
* getKeywords.
*
- *
+ *
* @return a {@link java.lang.String} object.
*/
public String getKeywords() {
@@ -217,7 +221,7 @@ public class AbilityManaPart implements java.io.Serializable {
* addsCounters.
*
* @param saBeingPaid
- *
+ *
* @return a {@link java.lang.String} object.
*/
public boolean addsCounters(SpellAbility saBeingPaid) {
@@ -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);
}
}
@@ -269,7 +311,7 @@ public class AbilityManaPart implements java.io.Serializable {
*
* getManaRestrictions.
*
- *
+ *
* @return a {@link java.lang.String} object.
*/
public String getManaRestrictions() {
@@ -280,7 +322,7 @@ public class AbilityManaPart implements java.io.Serializable {
*
* meetsManaRestrictions.
*
- *
+ *
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean.
@@ -296,7 +338,7 @@ public class AbilityManaPart implements java.io.Serializable {
if (restriction.equals("nonSpell")) {
return !sa.isSpell();
}
-
+
if (restriction.equals("CumulativeUpkeep")) {
if (sa.isCumulativeupkeep()) {
return true;
@@ -349,7 +391,7 @@ public class AbilityManaPart implements java.io.Serializable {
*
* mana.
*
- *
+ *
* @return a {@link java.lang.String} object.
*/
public final String mana() {
@@ -438,7 +480,7 @@ public class AbilityManaPart implements java.io.Serializable {
*
* canProduce.
*
- *
+ *
* @param s
* a {@link java.lang.String} object.
* @return a boolean.
@@ -468,7 +510,7 @@ public class AbilityManaPart implements java.io.Serializable {
*
* isBasic.
*
- *
+ *
* @return a boolean.
*/
public final boolean isBasic() {
@@ -541,7 +583,7 @@ public class AbilityManaPart implements java.io.Serializable {
public Card getSourceCard() {
return sourceCard;
}
-
+
public void setSourceCard(final Card host) {
sourceCard = host;
}
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
index 75479eaadfc..256bbf07a3a 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
@@ -32,6 +32,7 @@ import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardDamageMap;
import forge.game.card.CardFactory;
+import forge.game.card.CardPredicates;
import forge.game.card.CardZoneTable;
import forge.game.cost.Cost;
import forge.game.cost.CostPart;
@@ -46,6 +47,7 @@ import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.game.trigger.WrappedAbility;
import forge.game.zone.ZoneType;
+import forge.util.Aggregates;
import forge.util.Expressions;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
@@ -130,6 +132,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private final List payingMana = Lists.newArrayList();
private final List paidAbilities = Lists.newArrayList();
+ private Integer xManaCostPaid = null;
private HashMap paidLists = Maps.newHashMap();
@@ -447,6 +450,23 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
payCosts = abCost;
}
+ public boolean costHasX() {
+ if (getPayCosts() == null) {
+ return false;
+ }
+ return getPayCosts().hasXInAnyCostPart();
+ }
+
+ public boolean costHasManaX() {
+ if (getPayCosts() == null) {
+ return false;
+ }
+ if (getPayCosts().hasNoManaCost()) {
+ return false;
+ }
+ return getPayCosts().getCostMana().getAmountOfX() > 0;
+ }
+
public SpellAbilityRestriction getRestrictions() {
return restrictions;
}
@@ -1081,8 +1101,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
}
if (hasParam("MaxTotalTargetCMC") && entity instanceof Card) {
- final Card c = (Card) entity;
- if (c.getCMC() > tr.getMaxTotalCMC(c, this)) {
+ int soFar = Aggregates.sum(getTargets().getTargetCards(), CardPredicates.Accessors.fnGetCmc);
+ // only add if it isn't already targeting
+ if (!isTargeting(entity)) {
+ final Card c = (Card) entity;
+ soFar += c.getCMC();
+ }
+
+ if (soFar > tr.getMaxTotalCMC(getHostCard(), this)) {
return false;
}
}
@@ -1299,6 +1325,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 +1335,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
}
}
mapParams.put("Announce", announce + ";" + variable);
+ originalMapParams.put("Announce", announce + ";" + variable);
}
public boolean isXCost() {
@@ -1961,4 +1989,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public void setAlternativeCost(AlternativeCost ac) {
altCost = ac;
}
+
+ public Integer getXManaCostPaid() {
+ return xManaCostPaid;
+ }
+ public void setXManaCostPaid(final Integer n) {
+ xManaCostPaid = n;
+ }
}
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java
index f6f5954cdc7..1653c2066ca 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java
@@ -197,6 +197,10 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
this.setManaSpent(params.get("ConditionManaSpent"));
}
+ if (params.containsKey("ConditionManaNotSpent")) {
+ this.setManaNotSpent(params.get("ConditionManaNotSpent"));
+ }
+
if (params.containsKey("ConditionCheckSVar")) {
this.setSvarToCheck(params.get("ConditionCheckSVar"));
}
@@ -432,10 +436,21 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
}
}
- if (StringUtils.isNotEmpty(this.getManaSpent())) {
- byte manaSpent = MagicColor.fromName(getManaSpent()); // they always check for single color
- if( 0 == (manaSpent & sa.getHostCard().getColorsPaid())) // no match of colors
+ if (StringUtils.isNotEmpty(getManaSpent())) {
+ for (String s : getManaSpent().split(" ")) {
+ byte manaSpent = MagicColor.fromName(s);
+ if( 0 == (manaSpent & sa.getHostCard().getColorsPaid())) // no match of colors
+ return false;
+ }
+ }
+ if (StringUtils.isNotEmpty(getManaNotSpent())) {
+ byte toPay = 0;
+ for (String s : getManaNotSpent().split(" ")) {
+ toPay |= MagicColor.fromName(s);
+ }
+ if (toPay == (toPay & sa.getHostCard().getColorsPaid())) {
return false;
+ }
}
if (this.getsVarToCheck() != null) {
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java
index 15dd3573998..6a0b8b90de9 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java
@@ -83,7 +83,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
// private ArrayList payingMana = new ArrayList();
// private ArrayList paidAbilities = new
// ArrayList();
- private int xManaPaid = 0;
+ private Integer xManaPaid = null;
// Other Paid things
private final HashMap paidHash;
@@ -116,7 +116,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
splicedCards = sa.getSplicedCards();
// TODO getXManaCostPaid should be on the SA, not the Card
- xManaPaid = sa.getHostCard().getXManaCostPaid();
+ xManaPaid = sa.getXManaCostPaid();
// Triggering info
triggeringObjects = sa.getTriggeringObjects();
@@ -200,7 +200,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
// Set Cost specific things here
ability.setPaidHash(paidHash);
ability.setSplicedCards(splicedCards);
- ability.getHostCard().setXManaCostPaid(xManaPaid);
+ ability.setXManaCostPaid(xManaPaid);
// Triggered
ability.setTriggeringObjects(triggeringObjects);
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityVariables.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityVariables.java
index a40263e218f..11a6c93a998 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityVariables.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityVariables.java
@@ -89,6 +89,7 @@ public class SpellAbilityVariables implements Cloneable {
this.lifeTotal = sav.getLifeTotal();
this.lifeAmount = sav.getLifeAmount();
this.manaSpent = sav.getManaSpent();
+ this.manaNotSpent = sav.getManaNotSpent();
this.targetValidTargeting = sav.getTargetValidTargeting();
this.targetsSingleTarget = sav.targetsSingleTarget();
this.presenceCondition = sav.getPresenceCondition();
@@ -193,6 +194,7 @@ public class SpellAbilityVariables implements Cloneable {
/** The mana spent. */
private String manaSpent = "";
+ private String manaNotSpent = "";
/** The chosen colors string. */
private String chosenColors = null;
@@ -229,6 +231,13 @@ public class SpellAbilityVariables implements Cloneable {
return this.manaSpent;
}
+ public final void setManaNotSpent(final String s) {
+ this.manaNotSpent = s;
+ }
+ public final String getManaNotSpent() {
+ return this.manaNotSpent;
+ }
+
/**
*
* Setter for the field zone.
diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java
index f8442310ec0..421edc80b60 100644
--- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java
+++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java
@@ -181,15 +181,9 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
layers.add(StaticAbilityLayer.MODIFYPT);
}
- if (hasParam("AddHiddenKeyword")) {
- layers.add(StaticAbilityLayer.RULES);
- }
-
- if (hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount")) {
- layers.add(StaticAbilityLayer.RULES);
- }
-
- if (hasParam("AdjustLandPlays")) {
+ if (hasParam("AddHiddenKeyword")
+ || hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount")
+ || hasParam("AdjustLandPlays") || hasParam("ControlVote") || hasParam("AdditionalVote") || hasParam("AdditionalOptionalVote")) {
layers.add(StaticAbilityLayer.RULES);
}
diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java
index f0b0813bfc3..f6d8a9b6984 100644
--- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java
+++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java
@@ -513,6 +513,20 @@ public final class StaticAbilityContinuous {
}
}
+ if (params.containsKey("ControlVote")) {
+ p.addControlVote(se.getTimestamp());
+ }
+ if (params.containsKey("AdditionalVote")) {
+ String mhs = params.get("AdditionalVote");
+ int add = AbilityUtils.calculateAmount(hostCard, mhs, stAb);
+ p.addAdditionalVote(se.getTimestamp(), add);
+ }
+ if (params.containsKey("AdditionalOptionalVote")) {
+ String mhs = params.get("AdditionalOptionalVote");
+ int add = AbilityUtils.calculateAmount(hostCard, mhs, stAb);
+ p.addAdditionalOptionalVote(se.getTimestamp(), add);
+ }
+
if (params.containsKey("RaiseMaxHandSize")) {
String rmhs = params.get("RaiseMaxHandSize");
int rmax = AbilityUtils.calculateAmount(hostCard, rmhs, stAb);
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerCycled.java b/forge-game/src/main/java/forge/game/trigger/TriggerCycled.java
index 3fc236f94bc..88311eadac2 100644
--- a/forge-game/src/main/java/forge/game/trigger/TriggerCycled.java
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerCycled.java
@@ -53,7 +53,7 @@ public class TriggerCycled extends Trigger {
/** {@inheritDoc} */
@Override
public final void setTriggeringObjects(final SpellAbility sa, Map runParams) {
- sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card);
+ sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card, AbilityKey.Cause);
}
@Override
diff --git a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java
index 1a3a9e84fe2..55532468591 100644
--- a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java
+++ b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java
@@ -473,13 +473,13 @@ public class WrappedAbility extends Ability {
TriggerHandler th = game.getTriggerHandler();
+ // set Trigger
+ sa.setTrigger(regtrig);
+
if (decider != null && !decider.getController().confirmTrigger(this, triggerParams, this.isMandatory())) {
return;
}
- // set Trigger
- sa.setTrigger(regtrig);
-
if (!triggerParams.containsKey("NoTimestampCheck")) {
timestampCheck();
}
@@ -556,4 +556,11 @@ public class WrappedAbility extends Ability {
public void setAlternativeCost(AlternativeCost ac) {
sa.setAlternativeCost(ac);
}
+
+ public Integer getXManaCostPaid() {
+ return sa.getXManaCostPaid();
+ }
+ public void setXManaCostPaid(final Integer n) {
+ sa.setXManaCostPaid(n);
+ }
}
\ No newline at end of file
diff --git a/forge-game/src/main/java/forge/game/zone/MagicStack.java b/forge-game/src/main/java/forge/game/zone/MagicStack.java
index ee8571e5eb5..f3e90a992f8 100644
--- a/forge-game/src/main/java/forge/game/zone/MagicStack.java
+++ b/forge-game/src/main/java/forge/game/zone/MagicStack.java
@@ -314,9 +314,9 @@ public class MagicStack /* extends MyObservable */ implements Iterable cycleParams = AbilityKey.mapFromCard(sp.getHostCard());
+ cycleParams.put(AbilityKey.Cause, sp);
+ game.getTriggerHandler().runTrigger(TriggerType.Cycled, cycleParams, false);
}
if (sp.hasParam("Crew")) {
diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java
index 2660337d82f..039085ee9d0 100644
--- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java
+++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java
@@ -134,6 +134,9 @@ public enum TrackableProperty {
HasUnlimitedLandPlay(TrackableTypes.BooleanType),
NumLandThisTurn(TrackableTypes.IntegerType),
NumDrawnThisTurn(TrackableTypes.IntegerType),
+ AdditionalVote(TrackableTypes.IntegerType),
+ OptionalAdditionalVote(TrackableTypes.IntegerType),
+ ControlVotes(TrackableTypes.BooleanType),
Keywords(TrackableTypes.KeywordCollectionViewType, FreezeMode.IgnoresFreeze),
Commander(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze),
CommanderCast(TrackableTypes.IntegerMapType),
diff --git a/forge-game/src/main/java/forge/trackable/TrackableTypes.java b/forge-game/src/main/java/forge/trackable/TrackableTypes.java
index 6214563f8f7..0dce88f71e5 100644
--- a/forge-game/src/main/java/forge/trackable/TrackableTypes.java
+++ b/forge-game/src/main/java/forge/trackable/TrackableTypes.java
@@ -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 ...");
}
}
}
diff --git a/forge-gui-android/AndroidManifest.xml b/forge-gui-android/AndroidManifest.xml
index 5edbcfad66e..1f2dc8ae144 100644
--- a/forge-gui-android/AndroidManifest.xml
+++ b/forge-gui-android/AndroidManifest.xml
@@ -6,7 +6,8 @@
+ android:targetSdkVersion="26" />
+
diff --git a/forge-gui-android/libs/android-support-v4.jar b/forge-gui-android/libs/android-support-v4.jar
index 187bdf48b1d..653cb767665 100644
Binary files a/forge-gui-android/libs/android-support-v4.jar and b/forge-gui-android/libs/android-support-v4.jar differ
diff --git a/forge-gui-android/libs/gdx-backend-android-sources.jar b/forge-gui-android/libs/gdx-backend-android-sources.jar
index 414fa961cfd..d328206fb3a 100644
Binary files a/forge-gui-android/libs/gdx-backend-android-sources.jar and b/forge-gui-android/libs/gdx-backend-android-sources.jar differ
diff --git a/forge-gui-android/libs/gdx-backend-android.jar b/forge-gui-android/libs/gdx-backend-android.jar
index d780673aa6b..40af9131f01 100644
Binary files a/forge-gui-android/libs/gdx-backend-android.jar and b/forge-gui-android/libs/gdx-backend-android.jar differ
diff --git a/forge-gui-android/libs/gdx-freetype.jar b/forge-gui-android/libs/gdx-freetype.jar
index eda8b39b168..3181586a666 100644
Binary files a/forge-gui-android/libs/gdx-freetype.jar and b/forge-gui-android/libs/gdx-freetype.jar differ
diff --git a/forge-gui-android/libs/gdx-sources.jar b/forge-gui-android/libs/gdx-sources.jar
index 798fee80a7b..74b5aadb926 100644
Binary files a/forge-gui-android/libs/gdx-sources.jar and b/forge-gui-android/libs/gdx-sources.jar differ
diff --git a/forge-gui-android/libs/gdx.jar b/forge-gui-android/libs/gdx.jar
index 0ba4a3f2bb4..2c603b032c5 100644
Binary files a/forge-gui-android/libs/gdx.jar and b/forge-gui-android/libs/gdx.jar differ
diff --git a/forge-gui-android/pom.xml b/forge-gui-android/pom.xml
index 76158aee896..a556f6c9698 100644
--- a/forge-gui-android/pom.xml
+++ b/forge-gui-android/pom.xml
@@ -6,7 +6,7 @@
jar-Xms1024m-Xmx1536m
- 1.6.32.001
+ 1.6.33.001keystorealiasstorepass
@@ -19,7 +19,7 @@
forgeforge
- 1.6.33-SNAPSHOT
+ 1.6.34-SNAPSHOTforge-gui-android
@@ -104,24 +104,6 @@
gdx-backend-android1.9.10
-
- org.cache2k
- cache2k-base-bom
- 1.2.4.Final
- pom
-
-
- org.cache2k
- cache2k-core
- 1.2.4.Final
- compile
-
-
- org.cache2k
- cache2k-api
- 1.2.4.Final
- compile
-
@@ -142,7 +124,7 @@
true
- 25
+ 26true${project.basedir}/AndroidManifest.xml
@@ -183,7 +165,7 @@
false
- 25
+ 26false
diff --git a/forge-gui-android/proguard.cfg b/forge-gui-android/proguard.cfg
index 57fa2061f4d..ab6fbdeecf0 100644
--- a/forge-gui-android/proguard.cfg
+++ b/forge-gui-android/proguard.cfg
@@ -29,20 +29,8 @@
-dontwarn javax.**
-dontwarn org.apache.logging.log4j.**
-dontwarn module-info
-
-# mandatory proguard rules for cache2k to keep the core implementation
--dontwarn org.cache2k.impl.xmlConfiguration.**
--dontwarn org.cache2k.impl.serverSide.**
--keep interface org.cache2k.spi.Cache2kCoreProvider
--keep public class * extends org.cache2k.spi.Cache2kCoreProvider
-# optional proguard rules for cache2k, to keep XML configuration code
-# if only programmatic configuration is used, these rules may be ommitted
--keep interface org.cache2k.core.spi.CacheConfigurationProvider
--keep public class * extends org.cache2k.core.spi.CacheConfigurationProvider
--keepclassmembers public class * extends org.cache2k.configuration.ConfigurationBean {
- public void set*(...);
- public ** get*();
-}
+## Support library
+-dontwarn android.support.**
-keep class forge.** { *; }
-keep class com.thoughtworks.xstream.** { *; }
@@ -51,6 +39,7 @@
-keep class com.google.common.** { *; }
-keep class io.sentry.event.Event { *; }
-keep class io.netty.util.internal.logging.** { *; }
+-keep class net.jpountz.** { *; }
-keepclassmembers class com.badlogic.gdx.backends.android.AndroidInput* {
(com.badlogic.gdx.Application, android.content.Context, java.lang.Object, com.badlogic.gdx.backends.android.AndroidApplicationConfiguration);
diff --git a/forge-gui-android/project.properties b/forge-gui-android/project.properties
index 735ea3db305..94206b9059a 100644
--- a/forge-gui-android/project.properties
+++ b/forge-gui-android/project.properties
@@ -9,4 +9,4 @@
# Project target.
project.type=0
-target=android-20
+target=android-26
diff --git a/forge-gui-android/res/mipmap-hdpi/ic_launcher.png b/forge-gui-android/res/mipmap-hdpi/ic_launcher.png
index 13ac6220632..4271e4c73a0 100644
Binary files a/forge-gui-android/res/mipmap-hdpi/ic_launcher.png and b/forge-gui-android/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/forge-gui-android/res/mipmap-hdpi/ic_launcher_foreground.png b/forge-gui-android/res/mipmap-hdpi/ic_launcher_foreground.png
index a29e46aa510..a749b470515 100644
Binary files a/forge-gui-android/res/mipmap-hdpi/ic_launcher_foreground.png and b/forge-gui-android/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/forge-gui-android/res/mipmap-hdpi/ic_launcher_round.png b/forge-gui-android/res/mipmap-hdpi/ic_launcher_round.png
index 07a75d76bfa..23880358c4f 100644
Binary files a/forge-gui-android/res/mipmap-hdpi/ic_launcher_round.png and b/forge-gui-android/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/forge-gui-android/res/mipmap-ldpi/ic_launcher.png b/forge-gui-android/res/mipmap-ldpi/ic_launcher.png
index ee25ac432d9..ae40d73a33c 100644
Binary files a/forge-gui-android/res/mipmap-ldpi/ic_launcher.png and b/forge-gui-android/res/mipmap-ldpi/ic_launcher.png differ
diff --git a/forge-gui-android/res/mipmap-mdpi/ic_launcher.png b/forge-gui-android/res/mipmap-mdpi/ic_launcher.png
index 5fdd9db84cf..75cac9e8a25 100644
Binary files a/forge-gui-android/res/mipmap-mdpi/ic_launcher.png and b/forge-gui-android/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/forge-gui-android/res/mipmap-mdpi/ic_launcher_foreground.png b/forge-gui-android/res/mipmap-mdpi/ic_launcher_foreground.png
index 524a6232021..bcb5f32aa66 100644
Binary files a/forge-gui-android/res/mipmap-mdpi/ic_launcher_foreground.png and b/forge-gui-android/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/forge-gui-android/res/mipmap-mdpi/ic_launcher_round.png b/forge-gui-android/res/mipmap-mdpi/ic_launcher_round.png
index bb574ba21ae..fa0400329c5 100644
Binary files a/forge-gui-android/res/mipmap-mdpi/ic_launcher_round.png and b/forge-gui-android/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/forge-gui-android/res/mipmap-xhdpi/ic_launcher.png b/forge-gui-android/res/mipmap-xhdpi/ic_launcher.png
index 5d5de8a444f..ad7c6ed96bb 100644
Binary files a/forge-gui-android/res/mipmap-xhdpi/ic_launcher.png and b/forge-gui-android/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/forge-gui-android/res/mipmap-xhdpi/ic_launcher_foreground.png b/forge-gui-android/res/mipmap-xhdpi/ic_launcher_foreground.png
index 0e93289f2d1..25275afddb0 100644
Binary files a/forge-gui-android/res/mipmap-xhdpi/ic_launcher_foreground.png and b/forge-gui-android/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/forge-gui-android/res/mipmap-xhdpi/ic_launcher_round.png b/forge-gui-android/res/mipmap-xhdpi/ic_launcher_round.png
index 96328be12a9..42f78d72996 100644
Binary files a/forge-gui-android/res/mipmap-xhdpi/ic_launcher_round.png and b/forge-gui-android/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/forge-gui-android/res/mipmap-xxhdpi/ic_launcher.png b/forge-gui-android/res/mipmap-xxhdpi/ic_launcher.png
index e6ea9ce1897..3f9d487ce92 100644
Binary files a/forge-gui-android/res/mipmap-xxhdpi/ic_launcher.png and b/forge-gui-android/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/forge-gui-android/res/mipmap-xxhdpi/ic_launcher_foreground.png b/forge-gui-android/res/mipmap-xxhdpi/ic_launcher_foreground.png
index a01a5036f4f..a01aaa6e9df 100644
Binary files a/forge-gui-android/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/forge-gui-android/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/forge-gui-android/res/mipmap-xxhdpi/ic_launcher_round.png b/forge-gui-android/res/mipmap-xxhdpi/ic_launcher_round.png
index e4d1b24e68e..c1f4153cef3 100644
Binary files a/forge-gui-android/res/mipmap-xxhdpi/ic_launcher_round.png and b/forge-gui-android/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher.png b/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher.png
index 3380a1063bb..6d186851cd6 100644
Binary files a/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher.png and b/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher_foreground.png
index e8ccb890b9e..aaa2395dcb9 100644
Binary files a/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher_round.png b/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher_round.png
index 68aca8009d4..8ad3506407f 100644
Binary files a/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher_round.png and b/forge-gui-android/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/forge-gui-android/res/values/ic_launcher_background.xml b/forge-gui-android/res/values/ic_launcher_background.xml
index 7f8bb682c03..ab983282473 100644
--- a/forge-gui-android/res/values/ic_launcher_background.xml
+++ b/forge-gui-android/res/values/ic_launcher_background.xml
@@ -1,4 +1,4 @@
- #f0f0f0
+ #ffffff
\ No newline at end of file
diff --git a/forge-gui-android/src/forge/app/Main.java b/forge-gui-android/src/forge/app/Main.java
index 70aa9389704..a54df3b32e3 100644
--- a/forge-gui-android/src/forge/app/Main.java
+++ b/forge-gui-android/src/forge/app/Main.java
@@ -1,16 +1,22 @@
package forge.app;
import android.app.AlarmManager;
+import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.StateListDrawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
@@ -18,8 +24,17 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.PowerManager;
+import android.provider.Settings;
+import android.text.SpannableString;
+import android.text.style.StyleSpan;
+import android.view.Gravity;
+import android.view.View;
import android.view.WindowManager;
import android.webkit.MimeTypeMap;
+import android.widget.Button;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.android.AndroidApplication;
import forge.Forge;
@@ -35,13 +50,143 @@ import java.io.OutputStream;
import java.util.concurrent.Callable;
public class Main extends AndroidApplication {
+ AndroidAdapter Gadapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- AndroidAdapter adapter = new AndroidAdapter(this.getContext());
+ boolean permissiongranted = checkPermission();
+ Gadapter = new AndroidAdapter(this.getContext());
+ initForge(Gadapter, permissiongranted);
+ //permission
+ if(!permissiongranted){
+ //requestPermission();
+ displayMessage(Gadapter);
+ }
+ }
+
+ private void displayMessage(AndroidAdapter adapter){
+ TableLayout TL = new TableLayout(this);
+ TableRow row = new TableRow(this);
+ TableRow row2 = new TableRow(this);
+ TextView text = new TextView(this);
+ text.setGravity(Gravity.LEFT);
+ text.setTypeface(Typeface.SERIF);
+
+ String title="Forge needs Storage Permission to run properly...\n" +
+ "Follow these simple steps:\n\n";
+ String steps = " 1) Tap \"Open App Details\" Button.\n" +
+ " 2) Tap Permissions\n"+
+ " 3) Turn on the Storage Permission.\n\n"+
+ "(You can tap anywhere to exit and restart the app)\n\n";
+
+ SpannableString ss1= new SpannableString(title);
+ ss1.setSpan(new StyleSpan(Typeface.BOLD), 0, ss1.length(), 0);
+ text.append(ss1);
+ text.append(steps);
+ row.addView(text);
+ row.setGravity(Gravity.CENTER);
+
+ int[] colors = {Color.TRANSPARENT,Color.TRANSPARENT};
+ int[] pressed = {Color.GREEN,Color.GREEN};
+ GradientDrawable gd = new GradientDrawable(
+ GradientDrawable.Orientation.TOP_BOTTOM, colors);
+ gd.setStroke(3, Color.DKGRAY);
+ gd.setCornerRadius(100);
+
+ GradientDrawable gd2 = new GradientDrawable(
+ GradientDrawable.Orientation.TOP_BOTTOM, pressed);
+ gd2.setStroke(3, Color.DKGRAY);
+ gd2.setCornerRadius(100);
+
+ Button button = new Button(this);
+ button.setText("Open App Details");
+
+ StateListDrawable states = new StateListDrawable();
+
+ states.addState(new int[] {android.R.attr.state_pressed}, gd2);
+ states.addState(new int[] { }, gd);
+
+ button.setBackground(states);
+
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Uri uri = Uri.fromParts("package", getPackageName(), null);
+ intent.setData(uri);
+ startActivity(intent);
+ }
+ });
+
+ row2.addView(button);
+ row2.setGravity(Gravity.CENTER);
+
+ TL.addView(row, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT));
+ TL.addView(row2, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT));
+ TL.setGravity(Gravity.CENTER);
+ TL.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ adapter.exit();
+ }
+ });
+ setContentView(TL);
+ }
+ @Override
+ public void onBackPressed() {
+ if (Gadapter!=null)
+ Gadapter.exit();
+
+ super.onBackPressed();
+ }
+ private boolean checkPermission() {
+ int pid = android.os.Process.myPid();
+ int uid = android.os.Process.myUid();
+ try {
+ int result = this.getBaseContext().checkPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, pid, uid);
+ if (result == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (NullPointerException e) {
+ return false;
+ }
+ }
+ private void requestPermission() {
+ //Show Information about why you need the permission
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Storage Permission Denied...");
+ builder.setMessage("This app needs storage permission to run properly.\n\n\n\n");
+ builder.setPositiveButton("Open App Details", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ //ActivityCompat crashes... maybe it needs the appcompat v7???
+ //ActivityCompat.requestPermissions(Main.this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
+ }
+ });
+ /*builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+ builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+
+ }
+ });*/
+ builder.show();
+ }
+
+ private void initForge(AndroidAdapter adapter, boolean permissiongranted){
+ boolean isPortrait;
//establish assets directory
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
Gdx.app.error("Forge", "Can't access external storage");
@@ -65,15 +210,25 @@ public class Main extends AndroidApplication {
//enforce orientation based on whether device is a tablet and user preference
adapter.switchOrientationFile = assetsDir + "switch_orientation.ini";
boolean landscapeMode = adapter.isTablet == !FileUtil.doesFileExist(adapter.switchOrientationFile);
- if (landscapeMode) {
- Main.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
- }
- else {
- Main.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ if (permissiongranted){
+ if (landscapeMode) {
+ isPortrait = false;
+ Main.this.setRequestedOrientation(Build.VERSION.SDK_INT >= 26 ?
+ ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : //Oreo and above has virtual back/menu buttons
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ } else {
+ isPortrait = true;
+ Main.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+ } else {
+ isPortrait = true;
+ //set current orientation
+ Main.this.setRequestedOrientation(Main.this.getResources().getConfiguration().orientation);
}
- boolean value = Build.VERSION.SDK_INT >= 26;
- initialize(Forge.getApp(new AndroidClipboard(), adapter, assetsDir, value));
+ ForgePreferences prefs = FModel.getPreferences();
+ boolean propertyConfig = prefs != null && prefs.getPrefBoolean(ForgePreferences.FPref.UI_NETPLAY_COMPAT);
+ initialize(Forge.getApp(new AndroidClipboard(), adapter, assetsDir, propertyConfig, isPortrait));
}
/*@Override
diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml
index c2bb8f2388f..e1fa8d95747 100644
--- a/forge-gui-desktop/pom.xml
+++ b/forge-gui-desktop/pom.xml
@@ -4,7 +4,7 @@
forgeforge
- 1.6.33-SNAPSHOT
+ 1.6.34-SNAPSHOTforge-gui-desktop
@@ -213,7 +213,7 @@
com.akathist.maven.plugins.launch4jlaunch4j-maven-plugin
- 1.5.2
+ 1.7.25l4j-gui
@@ -235,7 +235,10 @@
1.8.0
- 1024
+ 4096
+
+ -Dfile.encoding=UTF-8
+
@@ -367,7 +370,7 @@
com.akathist.maven.plugins.launch4jlaunch4j-maven-plugin
- 1.5.2
+ 1.7.25l4j-gui
@@ -389,7 +392,10 @@
1.8.0
- 1024
+ 4096
+
+ -Dfile.encoding=UTF-8
+
@@ -564,7 +570,7 @@
-
+
@@ -675,7 +681,7 @@
-
+
diff --git a/forge-gui-desktop/src/main/config/forge.command b/forge-gui-desktop/src/main/config/forge.command
index e6068616734..1e3165f2ed0 100644
--- a/forge-gui-desktop/src/main/config/forge.command
+++ b/forge-gui-desktop/src/main/config/forge.command
@@ -1,3 +1,3 @@
#!/bin/sh
-cd "`dirname \"$0\"`"
-java -Xmx1024m -jar $project.build.finalName$
\ No newline at end of file
+cd $(dirname "${0}")
+java -Xmx4096m -Dfile.encoding=UTF-8 -jar $project.build.finalName$
diff --git a/forge-gui-desktop/src/main/config/forge.sh b/forge-gui-desktop/src/main/config/forge.sh
index e6068616734..1e3165f2ed0 100644
--- a/forge-gui-desktop/src/main/config/forge.sh
+++ b/forge-gui-desktop/src/main/config/forge.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-cd "`dirname \"$0\"`"
-java -Xmx1024m -jar $project.build.finalName$
\ No newline at end of file
+cd $(dirname "${0}")
+java -Xmx4096m -Dfile.encoding=UTF-8 -jar $project.build.finalName$
diff --git a/forge-gui-desktop/src/main/java/forge/control/FControl.java b/forge-gui-desktop/src/main/java/forge/control/FControl.java
index 4f951641eb5..99b9316813d 100644
--- a/forge-gui-desktop/src/main/java/forge/control/FControl.java
+++ b/forge-gui-desktop/src/main/java/forge/control/FControl.java
@@ -40,6 +40,7 @@ import javax.swing.WindowConstants;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
+import forge.GuiBase;
import forge.ImageCache;
import forge.LobbyPlayer;
import forge.Singletons;
@@ -219,6 +220,10 @@ public enum FControl implements KeyEventDispatcher {
final ForgePreferences prefs = FModel.getPreferences();
+ //set ExperimentalNetworkOption from preference
+ boolean propertyConfig = prefs != null && prefs.getPrefBoolean(ForgePreferences.FPref.UI_NETPLAY_COMPAT);
+ GuiBase.enablePropertyConfig(propertyConfig);
+
closeAction = CloseAction.valueOf(prefs.getPref(FPref.UI_CLOSE_ACTION));
final Localizer localizer = Localizer.getInstance();
diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java
index eec8c20c98a..0d72be9a4e5 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java
@@ -103,7 +103,7 @@ public final class CEditorConstructed extends CDeckEditor {
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);
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java
index aebb0936bec..21928c2ce24 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java
@@ -917,7 +917,7 @@ public class VLobby implements ILobbyView {
final List usedNames = getPlayerNames();
do {
newName = NameGenerator.getRandomName(gender, type, usedNames);
- confirmMsg = localizer.getMessage("lblconfirmName").replace("%n","\"" +newName + "\"");
+ confirmMsg = localizer.getMessage("lblconfirmName").replace("%s","\"" +newName + "\"");
} while (!FOptionPane.showConfirmDialog(confirmMsg, title, localizer.getMessage("lblUseThisName"), localizer.getMessage("lblTryAgain"), true));
return newName;
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java
index 0b9c344906a..10f2b5ea446 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java
@@ -200,4 +200,8 @@ public enum VSubmenuOnlineLobby implements IVSubmenu, IOnli
}
return false;
}
+
+ @Override
+ public void closeConn(String msg) {
+ }
}
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuDownloaders.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuDownloaders.java
index 53a0814f8c3..b3fd57d9a7f 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuDownloaders.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuDownloaders.java
@@ -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);
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java
index 96080f2a500..2e9e306ec04 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java
@@ -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;
@@ -94,6 +95,19 @@ public enum CSubmenuPreferences implements ICDoc {
}
});
+ // This updates Experimental Network Option
+ view.getCbUseExperimentalNetworkStream().addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(final ItemEvent arg0) {
+ if (updating) { return; }
+
+ final boolean toggle = view.getCbUseExperimentalNetworkStream().isSelected();
+ GuiBase.enablePropertyConfig(toggle);
+ prefs.setPref(FPref.UI_NETPLAY_COMPAT, String.valueOf(toggle));
+ prefs.save();
+ }
+ });
+
lstControls.clear(); // just in case
lstControls.add(Pair.of(view.getCbAnte(), FPref.UI_ANTE));
lstControls.add(Pair.of(view.getCbAnteMatchRarity(), FPref.UI_ANTE_MATCH_RARITY));
@@ -113,6 +127,8 @@ public enum CSubmenuPreferences implements ICDoc {
lstControls.add(Pair.of(view.getCbRemoveArtifacts(), FPref.DECKGEN_ARTIFACTS));
lstControls.add(Pair.of(view.getCbSingletons(), FPref.DECKGEN_SINGLETONS));
lstControls.add(Pair.of(view.getCbEnableAICheats(), FPref.UI_ENABLE_AI_CHEATS));
+ lstControls.add(Pair.of(view.getCbEnableUnknownCards(), FPref.UI_LOAD_UNKNOWN_CARDS));
+ lstControls.add(Pair.of(view.getCbUseExperimentalNetworkStream(), FPref.UI_NETPLAY_COMPAT));
lstControls.add(Pair.of(view.getCbImageFetcher(), FPref.UI_ENABLE_ONLINE_IMAGE_FETCHER));
lstControls.add(Pair.of(view.getCbDisplayFoil(), FPref.UI_OVERLAY_FOIL_EFFECT));
lstControls.add(Pair.of(view.getCbRandomFoil(), FPref.UI_RANDOM_FOIL));
@@ -225,6 +241,7 @@ public enum CSubmenuPreferences implements ICDoc {
initializeGameLogVerbosityComboBox();
initializeCloseActionComboBox();
initializeDefaultFontSizeComboBox();
+ initializeAutoUpdaterComboBox();
initializeMulliganRuleComboBox();
initializeAiProfilesComboBox();
initializeStackAdditionsComboBox();
@@ -251,6 +268,7 @@ public enum CSubmenuPreferences implements ICDoc {
setPlayerNameButtonText();
view.getCbDevMode().setSelected(ForgePreferences.DEV_MODE);
view.getCbEnableMusic().setSelected(prefs.getPrefBoolean(FPref.UI_ENABLE_MUSIC));
+ view.getCbUseExperimentalNetworkStream().setSelected(prefs.getPrefBoolean(FPref.UI_NETPLAY_COMPAT));
for(final Pair kv: lstControls) {
kv.getKey().setSelected(prefs.getPrefBoolean(kv.getValue()));
@@ -378,6 +396,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 panel = this.view.getCbpAutoUpdater();
+ final FComboBox 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;
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuDownloaders.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuDownloaders.java
index 6e1354a6727..2f48c1841ef 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuDownloaders.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuDownloaders.java
@@ -55,6 +55,7 @@ public enum VSubmenuDownloaders implements IVSubmenu {
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 {
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 {
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); }
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java
index e5d6c8658cf..4f8245eb74f 100644
--- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java
+++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java
@@ -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.*;
/**
@@ -107,6 +107,8 @@ public enum VSubmenuPreferences implements IVSubmenu {
private final JCheckBox cbShowStormCount = new OptionsCheckBox(localizer.getMessage("cbShowStormCount"));
private final JCheckBox cbRemindOnPriority = new OptionsCheckBox(localizer.getMessage("cbRemindOnPriority"));
private final JCheckBox cbUseSentry = new OptionsCheckBox(localizer.getMessage("cbUseSentry"));
+ private final JCheckBox cbEnableUnknownCards = new OptionsCheckBox("Enable Unknown Cards");
+ private final JCheckBox cbUseExperimentalNetworkStream = new OptionsCheckBox("Experimental Network Compatibility");
private final Map shortcutFields = new HashMap<>();
@@ -123,6 +125,7 @@ public enum VSubmenuPreferences implements IVSubmenu {
private final FComboBoxPanel cbpCounterDisplayLocation =new FComboBoxPanel<>(localizer.getMessage("cbpCounterDisplayLocation")+":");
private final FComboBoxPanel cbpGraveyardOrdering = new FComboBoxPanel<>(localizer.getMessage("cbpGraveyardOrdering")+":");
private final FComboBoxPanel cbpDefaultLanguage = new FComboBoxPanel<>(localizer.getMessage("cbpSelectLanguage")+":");
+ private final FComboBoxPanel cbpAutoUpdater = new FComboBoxPanel<>(localizer.getMessage("cbpAutoUpdater")+":");
/**
* Constructor.
@@ -157,6 +160,10 @@ public enum VSubmenuPreferences implements IVSubmenu {
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);
@@ -282,6 +289,12 @@ public enum VSubmenuPreferences implements IVSubmenu {
pnlPrefs.add(cbLoadHistoricFormats, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlLoadHistoricFormats")), descriptionConstraints);
+ pnlPrefs.add(cbEnableUnknownCards, titleConstraints);
+ pnlPrefs.add(new NoteLabel("Enable Unknown Cards to be loaded to Unknown Set. (Requires restart)"), descriptionConstraints);
+
+ pnlPrefs.add(cbUseExperimentalNetworkStream, titleConstraints);
+ pnlPrefs.add(new NoteLabel("Forge switches to compatible network stream. (If unsure, turn OFF this option)"), descriptionConstraints);
+
// Graphic Options
pnlPrefs.add(new SectionLabel(localizer.getMessage("GraphicOptions")), sectionConstraints + ", gaptop 2%");
@@ -531,6 +544,10 @@ public enum VSubmenuPreferences implements IVSubmenu {
}
}
+ public final FComboBoxPanel getCbpAutoUpdater() {
+ return cbpAutoUpdater;
+ }
+
/** @return {@link javax.swing.JCheckBox} */
public final JCheckBox getCbCompactMainMenu() {
return cbCompactMainMenu;
@@ -571,6 +588,16 @@ public enum VSubmenuPreferences implements IVSubmenu {
return cbEnableAICheats;
}
+ /** @return {@link javax.swing.JCheckBox} */
+ public JCheckBox getCbEnableUnknownCards() {
+ return cbEnableUnknownCards;
+ }
+
+ /** @return {@link javax.swing.JCheckBox} */
+ public JCheckBox getCbUseExperimentalNetworkStream() {
+ return cbUseExperimentalNetworkStream;
+ }
+
/** @return {@link javax.swing.JCheckBox} */
public JCheckBox getCbImageFetcher() {
return cbImageFetcher;
diff --git a/forge-gui-desktop/src/main/java/forge/view/Main.java b/forge-gui-desktop/src/main/java/forge/view/Main.java
index 05675d2225d..9a7424bc7e7 100644
--- a/forge-gui-desktop/src/main/java/forge/view/Main.java
+++ b/forge-gui-desktop/src/main/java/forge/view/Main.java
@@ -49,9 +49,6 @@ public final class Main {
//setup GUI interface
GuiBase.setInterface(new GuiDesktop());
- //set PropertyConfig log4j to true
- GuiBase.enablePropertyConfig(true);
-
//install our error handler
ExceptionHandler.registerErrorHandling();
@@ -81,7 +78,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;
}
diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/CardDatabaseHelper.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/CardDatabaseHelper.java
index 972e579a28f..10e73f4d8af 100644
--- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/CardDatabaseHelper.java
+++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/CardDatabaseHelper.java
@@ -29,7 +29,7 @@ public class CardDatabaseHelper {
private static void initialize() {
final CardStorageReader reader = new CardStorageReader(ForgeConstants.CARD_DATA_DIR, null, FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
- staticData = new StaticData(reader, ForgeConstants.EDITIONS_DIR, ForgeConstants.BLOCK_DATA_DIR);
+ staticData = new StaticData(reader, ForgeConstants.EDITIONS_DIR, ForgeConstants.BLOCK_DATA_DIR, FModel.getPreferences().getPrefBoolean(FPref.UI_LOAD_UNKNOWN_CARDS));
}
private static boolean hasBeenInitialized() {
diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java
index c9ee0f75d17..921b860fc40 100644
--- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java
+++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java
@@ -486,7 +486,7 @@ public class PlayerControllerForTests extends PlayerController {
}
@Override
- public Object vote(SpellAbility sa, String prompt, List options, ListMultimap votes) {
+ public Object vote(SpellAbility sa, String prompt, List options, ListMultimap votes, Player forPlayer) {
return chooseItem(options);
}
diff --git a/forge-gui-ios/libs/gdx-backend-robovm-sources.jar b/forge-gui-ios/libs/gdx-backend-robovm-sources.jar
index 1ed5c6f5486..7169ae4579b 100644
Binary files a/forge-gui-ios/libs/gdx-backend-robovm-sources.jar and b/forge-gui-ios/libs/gdx-backend-robovm-sources.jar differ
diff --git a/forge-gui-ios/libs/gdx-backend-robovm.jar b/forge-gui-ios/libs/gdx-backend-robovm.jar
index cbf789dd7b1..10cfefb0768 100644
Binary files a/forge-gui-ios/libs/gdx-backend-robovm.jar and b/forge-gui-ios/libs/gdx-backend-robovm.jar differ
diff --git a/forge-gui-ios/libs/gdx-sources.jar b/forge-gui-ios/libs/gdx-sources.jar
index 798fee80a7b..74b5aadb926 100644
Binary files a/forge-gui-ios/libs/gdx-sources.jar and b/forge-gui-ios/libs/gdx-sources.jar differ
diff --git a/forge-gui-ios/libs/gdx.jar b/forge-gui-ios/libs/gdx.jar
index 0ba4a3f2bb4..2c603b032c5 100644
Binary files a/forge-gui-ios/libs/gdx.jar and b/forge-gui-ios/libs/gdx.jar differ
diff --git a/forge-gui-ios/libs/libObjectAL.a b/forge-gui-ios/libs/libObjectAL.a
index 6113d44ffc9..289fd6ff6fb 100644
Binary files a/forge-gui-ios/libs/libObjectAL.a and b/forge-gui-ios/libs/libObjectAL.a differ
diff --git a/forge-gui-ios/libs/libgdx-freetype.a b/forge-gui-ios/libs/libgdx-freetype.a
index 67ed332cb29..371a1daee46 100644
Binary files a/forge-gui-ios/libs/libgdx-freetype.a and b/forge-gui-ios/libs/libgdx-freetype.a differ
diff --git a/forge-gui-ios/libs/libgdx.a b/forge-gui-ios/libs/libgdx.a
index 038693b6c84..82be25afbf9 100644
Binary files a/forge-gui-ios/libs/libgdx.a and b/forge-gui-ios/libs/libgdx.a differ
diff --git a/forge-gui-ios/pom.xml b/forge-gui-ios/pom.xml
index a19f7f51625..a597b8484a0 100644
--- a/forge-gui-ios/pom.xml
+++ b/forge-gui-ios/pom.xml
@@ -6,13 +6,13 @@
jar-Xms128m-Xmx2048m
- 1.6.32.001
+ 1.6.33.001forgeforge
- 1.6.33-SNAPSHOT
+ 1.6.34-SNAPSHOTforge-gui-ios
@@ -75,23 +75,5 @@
gdx-backend-robovm1.9.10
-
- org.cache2k
- cache2k-base-bom
- 1.2.4.Final
- pom
-
-
- org.cache2k
- cache2k-core
- 1.2.4.Final
- compile
-
-
- org.cache2k
- cache2k-api
- 1.2.4.Final
- compile
-
diff --git a/forge-gui-ios/src/forge/ios/Main.java b/forge-gui-ios/src/forge/ios/Main.java
index 689e07d6fa7..57fe63bc77c 100644
--- a/forge-gui-ios/src/forge/ios/Main.java
+++ b/forge-gui-ios/src/forge/ios/Main.java
@@ -8,6 +8,8 @@ import com.badlogic.gdx.backends.iosrobovm.IOSFiles;
import forge.Forge;
import forge.assets.AssetsDownloader;
import forge.interfaces.IDeviceAdapter;
+import forge.model.FModel;
+import forge.properties.ForgePreferences;
import forge.util.FileUtil;
import org.robovm.apple.foundation.NSAutoreleasePool;
import org.robovm.apple.uikit.UIApplication;
@@ -29,7 +31,9 @@ public class Main extends IOSApplication.Delegate {
final IOSApplicationConfiguration config = new IOSApplicationConfiguration();
config.useAccelerometer = false;
config.useCompass = false;
- final ApplicationListener app = Forge.getApp(new IOSClipboard(), new IOSAdapter(), assetsDir, false);
+ ForgePreferences prefs = FModel.getPreferences();
+ boolean propertyConfig = prefs != null && prefs.getPrefBoolean(ForgePreferences.FPref.UI_NETPLAY_COMPAT);
+ final ApplicationListener app = Forge.getApp(new IOSClipboard(), new IOSAdapter(), assetsDir, propertyConfig, false);
final IOSApplication iosApp = new IOSApplication(app, config);
return iosApp;
}
diff --git a/forge-gui-mobile-dev/libs/gdx-backend-lwjgl-sources.jar b/forge-gui-mobile-dev/libs/gdx-backend-lwjgl-sources.jar
index f736a822dd8..45f4e0fdbd0 100644
Binary files a/forge-gui-mobile-dev/libs/gdx-backend-lwjgl-sources.jar and b/forge-gui-mobile-dev/libs/gdx-backend-lwjgl-sources.jar differ
diff --git a/forge-gui-mobile-dev/libs/gdx-backend-lwjgl.jar b/forge-gui-mobile-dev/libs/gdx-backend-lwjgl.jar
index d1699805b6d..96f39d65c17 100644
Binary files a/forge-gui-mobile-dev/libs/gdx-backend-lwjgl.jar and b/forge-gui-mobile-dev/libs/gdx-backend-lwjgl.jar differ
diff --git a/forge-gui-mobile-dev/libs/gdx-freetype-natives.jar b/forge-gui-mobile-dev/libs/gdx-freetype-natives.jar
index 025707e2973..74481284775 100644
Binary files a/forge-gui-mobile-dev/libs/gdx-freetype-natives.jar and b/forge-gui-mobile-dev/libs/gdx-freetype-natives.jar differ
diff --git a/forge-gui-mobile-dev/libs/gdx-natives.jar b/forge-gui-mobile-dev/libs/gdx-natives.jar
index 18f0fbc3dcf..cbd60119618 100644
Binary files a/forge-gui-mobile-dev/libs/gdx-natives.jar and b/forge-gui-mobile-dev/libs/gdx-natives.jar differ
diff --git a/forge-gui-mobile-dev/pom.xml b/forge-gui-mobile-dev/pom.xml
index bd8d76b98ca..0deaa787ba2 100644
--- a/forge-gui-mobile-dev/pom.xml
+++ b/forge-gui-mobile-dev/pom.xml
@@ -4,7 +4,7 @@
forgeforge
- 1.6.33-SNAPSHOT
+ 1.6.34-SNAPSHOTforge-gui-mobile-dev
@@ -80,23 +80,5 @@
commons-cli1.4
-
- org.cache2k
- cache2k-base-bom
- 1.2.4.Final
- pom
-
-
- org.cache2k
- cache2k-core
- 1.2.4.Final
- compile
-
-
- org.cache2k
- cache2k-api
- 1.2.4.Final
- compile
-
diff --git a/forge-gui-mobile-dev/src/forge/app/Main.java b/forge-gui-mobile-dev/src/forge/app/Main.java
index 8f2de339592..620209f7d74 100644
--- a/forge-gui-mobile-dev/src/forge/app/Main.java
+++ b/forge-gui-mobile-dev/src/forge/app/Main.java
@@ -7,6 +7,8 @@ import com.badlogic.gdx.backends.lwjgl.LwjglClipboard;
import forge.Forge;
import forge.assets.AssetsDownloader;
import forge.interfaces.IDeviceAdapter;
+import forge.model.FModel;
+import forge.properties.ForgePreferences;
import forge.util.FileUtil;
import forge.util.OperatingSystem;
import forge.util.RestartUtil;
@@ -92,8 +94,10 @@ public class Main {
config.title = "Forge";
config.useHDPI = desktopMode; // enable HiDPI on Mac OS
+ ForgePreferences prefs = FModel.getPreferences();
+ boolean propertyConfig = prefs != null && prefs.getPrefBoolean(ForgePreferences.FPref.UI_NETPLAY_COMPAT);
new LwjglApplication(Forge.getApp(new LwjglClipboard(), new DesktopAdapter(switchOrientationFile),
- desktopMode ? desktopModeAssetsDir : assetsDir, true), config);
+ desktopMode ? desktopModeAssetsDir : assetsDir, propertyConfig, false), config);
}
private static class DesktopAdapter implements IDeviceAdapter {
diff --git a/forge-gui-mobile/libs/gdx-freetype.jar b/forge-gui-mobile/libs/gdx-freetype.jar
index eda8b39b168..3181586a666 100644
Binary files a/forge-gui-mobile/libs/gdx-freetype.jar and b/forge-gui-mobile/libs/gdx-freetype.jar differ
diff --git a/forge-gui-mobile/libs/gdx-sources.jar b/forge-gui-mobile/libs/gdx-sources.jar
index 798fee80a7b..74b5aadb926 100644
Binary files a/forge-gui-mobile/libs/gdx-sources.jar and b/forge-gui-mobile/libs/gdx-sources.jar differ
diff --git a/forge-gui-mobile/libs/gdx.jar b/forge-gui-mobile/libs/gdx.jar
index 0ba4a3f2bb4..2c603b032c5 100644
Binary files a/forge-gui-mobile/libs/gdx.jar and b/forge-gui-mobile/libs/gdx.jar differ
diff --git a/forge-gui-mobile/pom.xml b/forge-gui-mobile/pom.xml
index 3a950b4f673..60029bb033b 100644
--- a/forge-gui-mobile/pom.xml
+++ b/forge-gui-mobile/pom.xml
@@ -4,7 +4,7 @@
forgeforge
- 1.6.33-SNAPSHOT
+ 1.6.34-SNAPSHOTforge-gui-mobile
@@ -70,24 +70,6 @@
gdx-freetype1.9.10
-
- org.cache2k
- cache2k-base-bom
- 1.2.4.Final
- pom
-
-
- org.cache2k
- cache2k-core
- 1.2.4.Final
- compile
-
-
- org.cache2k
- cache2k-api
- 1.2.4.Final
- compile
-
diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java
index 0b1121707a0..4cbb9b327db 100644
--- a/forge-gui-mobile/src/forge/Forge.java
+++ b/forge-gui-mobile/src/forge/Forge.java
@@ -1,5 +1,6 @@
package forge;
+import com.badlogic.gdx.Application;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
@@ -42,7 +43,7 @@ import java.util.List;
import java.util.Stack;
public class Forge implements ApplicationListener {
- public static final String CURRENT_VERSION = "1.6.32.001";
+ public static final String CURRENT_VERSION = "1.6.33.001";
private static final ApplicationListener app = new Forge();
private static Clipboard clipboard;
@@ -68,13 +69,15 @@ public class Forge implements ApplicationListener {
public static String locale = "en-US";
public static boolean hdbuttons = false;
public static boolean hdstart = false;
+ public static boolean isPortraitMode = false;
- public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value) {
+ public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value, boolean androidOrientation) {
if (GuiBase.getInterface() == null) {
clipboard = clipboard0;
deviceAdapter = deviceAdapter0;
GuiBase.setInterface(new GuiMobile(assetDir0));
GuiBase.enablePropertyConfig(value);
+ isPortraitMode = androidOrientation;
}
return app;
}
@@ -87,6 +90,7 @@ public class Forge implements ApplicationListener {
//install our error handler
ExceptionHandler.registerErrorHandling();
+ GuiBase.setIsAndroid(Gdx.app.getType() == Application.ApplicationType.Android);
graphics = new Graphics();
splashScreen = new SplashScreen();
frameRate = new FrameRate();
@@ -371,6 +375,8 @@ public class Forge implements ApplicationListener {
}
public static boolean isLandscapeMode() {
+ if(GuiBase.isAndroid())
+ return !isPortraitMode;
return screenWidth > screenHeight;
}
diff --git a/forge-gui-mobile/src/forge/assets/FSkin.java b/forge-gui-mobile/src/forge/assets/FSkin.java
index 3eea1009c25..520c5e6a05c 100644
--- a/forge-gui-mobile/src/forge/assets/FSkin.java
+++ b/forge-gui-mobile/src/forge/assets/FSkin.java
@@ -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);
}
diff --git a/forge-gui-mobile/src/forge/assets/FSkinImage.java b/forge-gui-mobile/src/forge/assets/FSkinImage.java
index 5ed5abe8b8f..38daeb2ced1 100644
--- a/forge-gui-mobile/src/forge/assets/FSkinImage.java
+++ b/forge-gui-mobile/src/forge/assets/FSkinImage.java
@@ -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),
diff --git a/forge-gui-mobile/src/forge/assets/ImageCache.java b/forge-gui-mobile/src/forge/assets/ImageCache.java
index 74f904e34ac..5d472e6e1aa 100644
--- a/forge-gui-mobile/src/forge/assets/ImageCache.java
+++ b/forge-gui-mobile/src/forge/assets/ImageCache.java
@@ -22,6 +22,8 @@ import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.LoadingCache;
import forge.ImageKeys;
import forge.card.CardEdition;
import forge.game.card.CardView;
@@ -32,11 +34,11 @@ import forge.model.FModel;
import forge.properties.ForgeConstants;
import forge.util.ImageUtil;
import org.apache.commons.lang3.StringUtils;
-import org.cache2k.Cache;
-import org.cache2k.Cache2kBuilder;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
/**
* This class stores ALL card images in a cache with soft values. this means
@@ -56,13 +58,10 @@ public class ImageCache {
// short prefixes to save memory
private static final Set missingIconKeys = new HashSet<>();
- private static final Cache cache = new Cache2kBuilder() {}
- .name("cache")
- .eternal(true)
- .permitNullValues(true)
- .disableStatistics(true)
- .loader(new ImageLoader())
- .build();
+ private static final LoadingCache cache = CacheBuilder.newBuilder()
+ .maximumSize(400)
+ .expireAfterAccess(15, TimeUnit.MINUTES)
+ .build(new ImageLoader());
public static final Texture defaultImage;
public static FImage BlackBorder = FSkinImage.IMG_BORDER_BLACK;
public static FImage WhiteBorder = FSkinImage.IMG_BORDER_WHITE;
@@ -85,7 +84,7 @@ public class ImageCache {
}
public static void clear() {
- cache.clear();
+ cache.invalidateAll();
missingIconKeys.clear();
}
@@ -134,7 +133,7 @@ public class ImageCache {
Texture image;
if (useDefaultIfNotFound) {
// Load from file and add to cache if not found in cache initially.
- image = cache.get(imageKey);
+ image = cache.getIfPresent(imageKey);
if (image != null) { return image; }
@@ -164,8 +163,12 @@ public class ImageCache {
}
return image;
}
- public static void preloadCache(Iterable keys) {
- cache.getAll(keys);
+ public static void preloadCache(Iterable keys) {
+ try {
+ cache.getAll(keys);
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ }
}
public static TextureRegion croppedBorderImage(Texture image, boolean fullborder) {
if (!fullborder)
@@ -173,10 +176,9 @@ public class ImageCache {
float rscale = 0.96f;
int rw = Math.round(image.getWidth()*rscale);
int rh = Math.round(image.getHeight()*rscale);
- int rx = Math.round((image.getWidth() - rw)/2);
- int ry = Math.round((image.getHeight() - rh)/2)-2;
- TextureRegion rimage = new TextureRegion(image, rx, ry, rw, rh);
- return rimage;
+ int rx = Math.round((image.getWidth() - rw)/2f);
+ int ry = Math.round((image.getHeight() - rh)/2f)-2;
+ return new TextureRegion(image, rx, ry, rw, rh);
}
public static boolean isWhiteBordered(IPaperCard c) {
if (c == null)
diff --git a/forge-gui-mobile/src/forge/assets/ImageLoader.java b/forge-gui-mobile/src/forge/assets/ImageLoader.java
index 5f33d4c44c9..0137daa9f5e 100644
--- a/forge-gui-mobile/src/forge/assets/ImageLoader.java
+++ b/forge-gui-mobile/src/forge/assets/ImageLoader.java
@@ -8,8 +8,8 @@ import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.TextureData;
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
+import com.google.common.cache.CacheLoader;
import forge.FThreads;
-import org.cache2k.integration.CacheLoader;
import forge.Forge;
import forge.ImageKeys;
diff --git a/forge-gui-mobile/src/forge/card/CardImageRenderer.java b/forge-gui-mobile/src/forge/card/CardImageRenderer.java
index b2094fbaa15..8bedd01ae42 100644
--- a/forge-gui-mobile/src/forge/card/CardImageRenderer.java
+++ b/forge-gui-mobile/src/forge/card/CardImageRenderer.java
@@ -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()))
diff --git a/forge-gui-mobile/src/forge/card/CardRenderer.java b/forge-gui-mobile/src/forge/card/CardRenderer.java
index 6dee11cb8cc..d4a04841adc 100644
--- a/forge-gui-mobile/src/forge/card/CardRenderer.java
+++ b/forge-gui-mobile/src/forge/card/CardRenderer.java
@@ -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 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)
@@ -481,6 +535,9 @@ public class CardRenderer {
}
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos) {
+ drawCardWithOverlays(g, card, x, y, w, h, pos, false);
+ }
+ public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean stackview) {
boolean canShow = MatchController.instance.mayView(card);
float oldAlpha = g.getfloatAlphaComposite();
boolean unselectable = !MatchController.instance.isSelectable(card) && MatchController.instance.isSelecting();
@@ -501,32 +558,10 @@ public class CardRenderer {
Color color = FSkinColor.fromRGB(borderColor.r, borderColor.g, borderColor.b);
color = FSkinColor.tintColor(Color.WHITE, color, CardRenderer.PT_BOX_TINT);
- //draw name and mana cost overlays if card is small or default card image being used
- if (h <= NAME_COST_THRESHOLD && canShow) {
- if (showCardNameOverlay(card)) {
- g.drawOutlinedText(CardTranslation.getTranslatedName(details.getName()), FSkinFont.forHeight(h * 0.15f), Color.WHITE, Color.BLACK, x + padding -1f, y + padding, w - 2 * padding, h * 0.4f, true, Align.left, false);
- }
- if (showCardManaCostOverlay(card)) {
- float manaSymbolSize = w / 4.5f;
- if (card.isSplitCard() && card.hasAlternateState()) {
- if (!card.isFaceDown()) { // no need to draw mana symbols on face down split cards (e.g. manifested)
- float dy = manaSymbolSize / 2 + Utils.scale(5);
-
- PaperCard pc = StaticData.instance().getCommonCards().getCard(card.getName());
- if (Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH)){
- dy *= -1; // flip card costs for Aftermath cards
- }
-
- drawManaCost(g, card.getAlternateState().getManaCost(), x - padding, y - dy, w + 2 * padding, h, manaSymbolSize);
- drawManaCost(g, card.getCurrentState().getManaCost(), x - padding, y + dy, w + 2 * padding, h, manaSymbolSize);
- }
- }
- else {
- drawManaCost(g, card.getCurrentState().getManaCost(), x - padding, y, w + 2 * padding, h, manaSymbolSize);
- }
- }
- }
+ //card name && manacost original position is here moved at the bottom...
+ if (stackview)
+ return; //override
if (pos == CardStackPosition.BehindVert) { return; } //remaining rendering not needed if card is behind another card in a vertical stack
boolean onTop = (pos == CardStackPosition.Top);
@@ -602,6 +637,7 @@ public class CardRenderer {
float abiSpace = cw / 5.7f;
float abiCount = 0;
+ if (unselectable){ g.setAlphaComposite(0.6f); }
if (onbattlefield && onTop && showAbilityIcons(card)) {
if (card.isToken()){
CardFaceSymbols.drawSymbol("token", g, abiX, abiY, abiScale, abiScale);
@@ -610,85 +646,63 @@ public class CardRenderer {
}
if (card.getCurrentState().hasFlying()) {
CardFaceSymbols.drawSymbol("flying", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasHaste()) {
CardFaceSymbols.drawSymbol("haste", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasDoubleStrike()) {
CardFaceSymbols.drawSymbol("doublestrike", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().hasFirstStrike()) {
CardFaceSymbols.drawSymbol("firststrike", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasDeathtouch()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("deathtouch", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasIndestructible()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("indestructible", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasMenace()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("menace", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasFear()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("fear", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasIntimidate()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("intimidate", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasShadow()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("shadow", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasHorsemanship()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("horsemanship", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
@@ -699,57 +713,41 @@ public class CardRenderer {
List listHK = Arrays.asList(splitK);
if (listHK.contains("generic")) {
CardFaceSymbols.drawSymbol("hexproof", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (listHK.contains("R")) {
CardFaceSymbols.drawSymbol("hexproofR", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (listHK.contains("B")) {
CardFaceSymbols.drawSymbol("hexproofB", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (listHK.contains("U")) {
CardFaceSymbols.drawSymbol("hexproofU", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (listHK.contains("G")) {
CardFaceSymbols.drawSymbol("hexproofG", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (listHK.contains("W")) {
CardFaceSymbols.drawSymbol("hexproofW", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (listHK.contains("monocolored")) {
CardFaceSymbols.drawSymbol("hexproofC", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
} else {
CardFaceSymbols.drawSymbol("hexproof", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
@@ -757,48 +755,36 @@ public class CardRenderer {
else if (card.getCurrentState().hasShroud()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("shroud", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasVigilance()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("vigilance", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasTrample()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("trample", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasReach()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("reach", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasLifelink()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("lifelink", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
if (card.getCurrentState().hasDefender()) {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
CardFaceSymbols.drawSymbol("defender", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
@@ -807,132 +793,138 @@ public class CardRenderer {
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
if (card.getCurrentState().getProtectionKey().contains("everything") || card.getCurrentState().getProtectionKey().contains("allcolors")) {
CardFaceSymbols.drawSymbol("protectAll", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().contains("coloredspells")) {
CardFaceSymbols.drawSymbol("protectColoredSpells", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("R")) {
CardFaceSymbols.drawSymbol("protectR", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("G")) {
CardFaceSymbols.drawSymbol("protectG", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("B")) {
CardFaceSymbols.drawSymbol("protectB", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("U")) {
CardFaceSymbols.drawSymbol("protectU", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("W")) {
CardFaceSymbols.drawSymbol("protectW", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("RG")||card.getCurrentState().getProtectionKey().equals("GR")) {
CardFaceSymbols.drawSymbol("protectRG", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("RB")||card.getCurrentState().getProtectionKey().equals("BR")) {
CardFaceSymbols.drawSymbol("protectRB", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("RU")||card.getCurrentState().getProtectionKey().equals("UR")) {
CardFaceSymbols.drawSymbol("protectRU", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("RW")||card.getCurrentState().getProtectionKey().equals("WR")) {
CardFaceSymbols.drawSymbol("protectRW", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("GB")||card.getCurrentState().getProtectionKey().equals("BG")) {
CardFaceSymbols.drawSymbol("protectGB", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("GU")||card.getCurrentState().getProtectionKey().equals("UG")) {
CardFaceSymbols.drawSymbol("protectGU", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("GW")||card.getCurrentState().getProtectionKey().equals("WG")) {
CardFaceSymbols.drawSymbol("protectGW", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("BU")||card.getCurrentState().getProtectionKey().equals("UB")) {
CardFaceSymbols.drawSymbol("protectBU", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("BW")||card.getCurrentState().getProtectionKey().equals("WB")) {
CardFaceSymbols.drawSymbol("protectBW", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().equals("UW")||card.getCurrentState().getProtectionKey().equals("WU")) {
CardFaceSymbols.drawSymbol("protectUW", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
else if (card.getCurrentState().getProtectionKey().contains("generic") || card.getCurrentState().getProtectionKey().length() > 2) {
CardFaceSymbols.drawSymbol("protectGeneric", g, abiX, abiY, abiScale, abiScale);
- if (unselectable){
- g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
abiY += abiSpace;
abiCount += 1;
}
}
}
+ //draw name and mana cost overlays if card is small or default card image being used
+ if (h <= NAME_COST_THRESHOLD && canShow) {
+ if (showCardNameOverlay(card)) {
+ float multiplier;
+ switch (Forge.extrawide) {
+ case "default":
+ multiplier = 0.145f; //good for tablets with 16:10 or similar
+ break;
+ case "wide":
+ multiplier = 0.150f;
+ break;
+ case "extrawide":
+ multiplier = 0.155f; //good for tall phones with 21:9 or similar
+ break;
+ default:
+ multiplier = 0.150f;
+ break;
+ }
+ g.drawOutlinedText(CardTranslation.getTranslatedName(details.getName()), FSkinFont.forHeight(h * multiplier), Color.WHITE, Color.BLACK, x + padding -1f, y + padding, w - 2 * padding, h * 0.4f, true, Align.left, false);
+ }
+ if (showCardManaCostOverlay(card)) {
+ float manaSymbolSize = w / 4.5f;
+ if (card.isSplitCard() && card.hasAlternateState()) {
+ if (!card.isFaceDown()) { // no need to draw mana symbols on face down split cards (e.g. manifested)
+ float dy = manaSymbolSize / 2 + Utils.scale(5);
+
+ PaperCard pc = StaticData.instance().getCommonCards().getCard(card.getName());
+ if (Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH)){
+ dy *= -1; // flip card costs for Aftermath cards
+ }
+
+ drawManaCost(g, card.getAlternateState().getManaCost(), x - padding, y - dy, w + 2 * padding, h, manaSymbolSize);
+ drawManaCost(g, card.getCurrentState().getManaCost(), x - padding, y + dy, w + 2 * padding, h, manaSymbolSize);
+ }
+ }
+ else {
+ drawManaCost(g, card.getCurrentState().getManaCost(), x - padding, y, w + 2 * padding, h, manaSymbolSize);
+ }
+ }
+ }
+ //reset alpha
+ g.setAlphaComposite(oldAlpha);
}
private static void drawCounterTabs(final CardView card, final Graphics g, final float x, final float y, final float w, final float h) {
@@ -1119,8 +1111,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;
diff --git a/forge-gui-mobile/src/forge/card/CardZoom.java b/forge-gui-mobile/src/forge/card/CardZoom.java
index 8608cbbc82f..2eea37d2ec6 100644
--- a/forge-gui-mobile/src/forge/card/CardZoom.java
+++ b/forge-gui-mobile/src/forge/card/CardZoom.java
@@ -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;
+ 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;
diff --git a/forge-gui-mobile/src/forge/deck/FDeckEditor.java b/forge-gui-mobile/src/forge/deck/FDeckEditor.java
index 2f74d266722..ac553fb40ab 100644
--- a/forge-gui-mobile/src/forge/deck/FDeckEditor.java
+++ b/forge-gui-mobile/src/forge/deck/FDeckEditor.java
@@ -856,6 +856,9 @@ public class FDeckEditor extends TabPageScreen {
case Brawl:
isLegalCommander = card.getRules().canBeBrawlCommander();
break;
+ case TinyLeaders:
+ isLegalCommander = card.getRules().canBeTinyLeadersCommander();
+ break;
case Oathbreaker:
isLegalCommander = card.getRules().canBeOathbreaker();
captionSuffix = localizer.getMessage("lblOathbreaker");
diff --git a/forge-gui-mobile/src/forge/screens/FScreen.java b/forge-gui-mobile/src/forge/screens/FScreen.java
index 163c5276e17..7ceece302c6 100644
--- a/forge-gui-mobile/src/forge/screens/FScreen.java
+++ b/forge-gui-mobile/src/forge/screens/FScreen.java
@@ -8,6 +8,7 @@ import com.badlogic.gdx.utils.Align;
import forge.Forge;
import forge.Graphics;
+import forge.GuiBase;
import forge.assets.FImage;
import forge.assets.FSkinColor;
import forge.assets.FSkinColor.Colors;
@@ -80,14 +81,12 @@ public abstract class FScreen extends FContainer {
@Override
protected final void doLayout(float width, float height) {
- if (width > height) { //handle landscape layout special
- doLandscapeLayout(width, height);
- }
- else if (header != null) {
+ if ((GuiBase.isAndroid() && Forge.isLandscapeMode())||(width > height)) {
+ doLandscapeLayout(width, height); //handle landscape layout special
+ } else if (header != null) {
header.setBounds(0, 0, width, header.getPreferredHeight());
doLayout(header.getHeight(), width, height);
- }
- else {
+ } else {
doLayout(0, width, height);
}
}
diff --git a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java
index 7ecde4cd371..5d6fd28f4d6 100644
--- a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java
+++ b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java
@@ -11,6 +11,7 @@ import forge.deck.DeckSection;
import forge.deck.DeckType;
import forge.deck.FDeckChooser;
+import forge.net.server.FServerManager;
import forge.util.Localizer;
import org.apache.commons.lang3.StringUtils;
@@ -530,6 +531,8 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
GuiBase.setNetworkplay(allowNetworking);
+ setStartButtonAvailability();
+
for (int i = 0; i < cbPlayerCount.getSelectedItem(); i++) {
final boolean hasPanel = i < playerPanels.size();
if (i < playerCount) {
@@ -731,4 +734,11 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
public FScrollPane getPlayersScroll() {
return playersScroll;
}
+
+ public void setStartButtonAvailability() {
+ if (lobby.isAllowNetworking() && FServerManager.getInstance() != null)
+ btnStart.setVisible(FServerManager.getInstance().isHosting());
+ else
+ btnStart.setVisible(true);
+ }
}
diff --git a/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java b/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java
index 47f4707f310..a875ea6c3ec 100644
--- a/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java
+++ b/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java
@@ -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;
diff --git a/forge-gui-mobile/src/forge/screens/home/NewGameMenu.java b/forge-gui-mobile/src/forge/screens/home/NewGameMenu.java
index 0bd6829888b..af3cb2e1b74 100644
--- a/forge-gui-mobile/src/forge/screens/home/NewGameMenu.java
+++ b/forge-gui-mobile/src/forge/screens/home/NewGameMenu.java
@@ -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;
diff --git a/forge-gui-mobile/src/forge/screens/home/puzzle/PuzzleScreen.java b/forge-gui-mobile/src/forge/screens/home/puzzle/PuzzleScreen.java
index a740859f49b..0011ed4a6dc 100644
--- a/forge-gui-mobile/src/forge/screens/home/puzzle/PuzzleScreen.java
+++ b/forge-gui-mobile/src/forge/screens/home/puzzle/PuzzleScreen.java
@@ -54,36 +54,45 @@ public class PuzzleScreen extends LaunchScreen {
final ArrayList puzzles = PuzzleIO.loadPuzzles();
Collections.sort(puzzles);
- GuiChoose.one(Localizer.getInstance().getMessage("lblChooseAPuzzle"), puzzles, new Callback() {
+ GuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblChooseAPuzzle"), puzzles, new Callback() {
@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 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 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());
+ }
+ });
+ }
}
});
diff --git a/forge-gui-mobile/src/forge/screens/match/MatchController.java b/forge-gui-mobile/src/forge/screens/match/MatchController.java
index 8cc153d9ac4..a438e2510ed 100644
--- a/forge-gui-mobile/src/forge/screens/match/MatchController.java
+++ b/forge-gui-mobile/src/forge/screens/match/MatchController.java
@@ -125,15 +125,12 @@ public class MatchController extends AbstractGuiGame {
@Override
public void refreshField() {
- if(!GuiBase.isNetworkplay()) //TODO alternate method for update Netplay...
- return;
- if(getGameView() == null)
+ if(!GuiBase.isNetworkplay())
return;
if(getGameView().getPhase() == null)
return;
if (getGameView().getPhase().phaseforUpdateField())
- for (final VPlayerPanel pnl : view.getPlayerPanels().values())
- pnl.getField().update(false);
+ refreshCardDetails(null);
}
public boolean hotSeatMode() {
diff --git a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java
index bd61f09d006..82479ebb60c 100644
--- a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java
+++ b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java
@@ -83,9 +83,13 @@ public class MatchScreen extends FScreen {
scroller = add(new FieldScroller());
+ int humanCount = 0;
+
for (VPlayerPanel playerPanel : playerPanels0) {
playerPanels.put(playerPanel.getPlayer(), scroller.add(playerPanel));
playerPanel.setFlipped(true);
+ if(!playerPanel.getPlayer().isAI())
+ humanCount++;
}
bottomPlayerPanel = playerPanels0.get(0);
bottomPlayerPanel.setFlipped(false);
@@ -110,10 +114,10 @@ public class MatchScreen extends FScreen {
}
}));
- if (MatchController.instance.getLocalPlayerCount() <= 1 || MatchController.instance.hotSeatMode()) {
+ if (humanCount < 2 || MatchController.instance.hotSeatMode() || GuiBase.isNetworkplay())
topPlayerPrompt = null;
- }
- else { //show top prompt if multiple human players and not playing in Hot Seat mode
+ else {
+ //show top prompt if multiple human players and not playing in Hot Seat mode and not in network play
topPlayerPrompt = add(new VPrompt("", "",
new FEventHandler() {
@Override
@@ -561,6 +565,7 @@ public class MatchScreen extends FScreen {
float x = 0;
float y;
float w = getWidth();
+ Color color = Color.CYAN;
//field separator lines
if (!Forge.isLandscapeMode()) {
@@ -587,17 +592,43 @@ public class MatchScreen extends FScreen {
//Draw Priority Human Multiplayer 2 player
float oldAlphaComposite = g.getfloatAlphaComposite();
+
+
if ((getPlayerPanels().keySet().size() == 2) && (countHuman() == 2)){
for (VPlayerPanel playerPanel: playerPanelsList){
midField = playerPanel.getTop();
y = midField - 0.5f;
float adjustY = Forge.isLandscapeMode() ? y + 1f : midField;
float adjustH = Forge.isLandscapeMode() ? playerPanel.getField().getBottom() - 1f : playerPanel.getBottom() - 1f;
- if(playerPanel.getPlayer().getHasPriority() && !playerPanel.getPlayer().isAI())
+
+ if(playerPanel.getPlayer().getHasPriority())
g.setAlphaComposite(0.8f);
else
g.setAlphaComposite(0f);
- g.drawRect(4f, Color.CYAN, playerPanel.getField().getLeft(), adjustY, playerPanel.getField().getWidth(), adjustH);
+
+ if(MatchController.instance.getGameView()!= null) {
+ if(MatchController.instance.getGameView().getPhase()!=null)
+ {
+ if(MatchController.instance.getGameView().getPhase().isCombatPhase()){
+ if(playerPanel.getPlayer() == MatchController.instance.getCurrentPlayer())
+ g.setAlphaComposite(0.8f);
+ else
+ g.setAlphaComposite(0f);
+ }
+ }
+
+
+ if(MatchController.instance.getGameView().getCombat() != null) {
+ if(playerPanel.getPlayer() == MatchController.instance.getGameView().getPlayerTurn())
+ color = Color.RED;
+ else
+ color = Color.LIME;
+ }
+ else
+ color = Color.CYAN;
+ }
+
+ g.drawRect(4f, color, playerPanel.getField().getLeft(), adjustY, playerPanel.getField().getWidth(), adjustH);
g.setAlphaComposite(oldAlphaComposite);
}
}
diff --git a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java
index 5caf394fdd6..6af3cc4c31f 100644
--- a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java
+++ b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java
@@ -10,6 +10,7 @@ import com.badlogic.gdx.math.Vector2;
import forge.FThreads;
import forge.Graphics;
+import forge.GuiBase;
import forge.card.CardZoom;
import forge.card.CardRenderer.CardStackPosition;
import forge.card.CardZoom.ActivateHandler;
@@ -184,7 +185,10 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
@Override
public String getActivateAction(int index) {
- return MatchController.instance.getGameController().getActivateDescription(orderedCards.get(index));
+ if(!GuiBase.isNetworkplay()) //causes lag on netplay client side
+ return MatchController.instance.getGameController().getActivateDescription(orderedCards.get(index));
+
+ return "Activate | Cast | Play (if allowed)"; //simple text on card zoom swipe up
}
@Override
diff --git a/forge-gui-mobile/src/forge/screens/match/views/VStack.java b/forge-gui-mobile/src/forge/screens/match/views/VStack.java
index 7d9c5639694..07a9d9468ab 100644
--- a/forge-gui-mobile/src/forge/screens/match/views/VStack.java
+++ b/forge-gui-mobile/src/forge/screens/match/views/VStack.java
@@ -40,6 +40,7 @@ import forge.toolbox.FEvent;
import forge.toolbox.FEvent.FEventHandler;
import forge.toolbox.FLabel;
import forge.util.Localizer;
+import forge.util.TextUtil;
import forge.util.collect.FCollectionView;
import forge.util.Utils;
@@ -372,12 +373,37 @@ public class VStack extends FDropDown {
x += PADDING;
y += PADDING;
- CardRenderer.drawCardWithOverlays(g, stackInstance.getSourceCard(), x, y, CARD_WIDTH, CARD_HEIGHT, CardStackPosition.Top);
+ CardRenderer.drawCardWithOverlays(g, stackInstance.getSourceCard(), x, y, CARD_WIDTH, CARD_HEIGHT, CardStackPosition.Top, true);
x += CARD_WIDTH + PADDING;
w -= x + PADDING - BORDER_THICKNESS;
h -= y + PADDING - BORDER_THICKNESS;
- textRenderer.drawText(g, text, FONT, foreColor, x, y, w, h, y, h, true, Align.left, true);
+
+ String name = stackInstance.getSourceCard().getName();
+ int index = text.indexOf(name);
+ String newtext = "";
+ String cId = "(" + stackInstance.getSourceCard().getId() + ")";
+
+ if (index == -1) {
+ newtext = TextUtil.fastReplace(TextUtil.fastReplace(text.trim(),"--","-"),"- -","-");
+ newtext = TextUtil.fastReplace(newtext, "- - ", "- ");
+ textRenderer.drawText(g, name + " " + (name.length() > 1 ? cId : "") + "\n" + (newtext.length() > 1 ? newtext : ""),
+ FONT, foreColor, x, y, w, h, y, h, true, Align.left, true);
+
+ } else {
+ String trimFirst = TextUtil.fastReplace("\n" + text.substring(0, index) + text.substring(index + name.length()), "- -", "-");
+ String trimSecond = TextUtil.fastReplace(trimFirst, name+" "+cId, name);
+ newtext = TextUtil.fastReplace(trimSecond, " "+cId, name);
+
+ if(newtext.equals("\n"+name))
+ textRenderer.drawText(g, name + " " + cId, FONT, foreColor, x, y, w, h, y, h, true, Align.left, true);
+ else {
+ newtext = TextUtil.fastReplace(TextUtil.fastReplace(newtext,name+" -","-"), "\n ", "\n");
+ newtext = "\n"+ TextUtil.fastReplace(newtext.trim(),"--","-");
+ newtext = TextUtil.fastReplace(newtext, "- - ", "- ");
+ textRenderer.drawText(g, name+" "+cId+newtext, FONT, foreColor, x, y, w, h, y, h, true, Align.left, true);
+ }
+ }
g.endClip();
diff --git a/forge-gui-mobile/src/forge/screens/match/views/VZoneDisplay.java b/forge-gui-mobile/src/forge/screens/match/views/VZoneDisplay.java
index b3ab177f1b0..1b814d2752f 100644
--- a/forge-gui-mobile/src/forge/screens/match/views/VZoneDisplay.java
+++ b/forge-gui-mobile/src/forge/screens/match/views/VZoneDisplay.java
@@ -66,7 +66,12 @@ public class VZoneDisplay extends VCardDisplayArea {
}
private void setRevealedPanel(int idx) {
- revealedPanel = cardPanels.get(idx);
+ try {
+ revealedPanel = cardPanels.get(idx); //??? on network match, triggered by card ability
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.printStackTrace();
+ return;
+ }
clearChildren();
if (Forge.isLandscapeMode()) {
//for landscape mode, just show revealed card on top
diff --git a/forge-gui-mobile/src/forge/screens/online/OnlineLobbyScreen.java b/forge-gui-mobile/src/forge/screens/online/OnlineLobbyScreen.java
index 2d4b650a647..43639400396 100644
--- a/forge-gui-mobile/src/forge/screens/online/OnlineLobbyScreen.java
+++ b/forge-gui-mobile/src/forge/screens/online/OnlineLobbyScreen.java
@@ -1,7 +1,10 @@
package forge.screens.online;
+import com.google.common.collect.ImmutableList;
import forge.FThreads;
import forge.Forge;
+import forge.GuiBase;
+import forge.assets.FSkinProp;
import forge.interfaces.ILobbyView;
import forge.match.GameLobby;
import forge.net.ChatMessage;
@@ -10,15 +13,65 @@ import forge.net.IOnlineLobby;
import forge.net.NetConnectUtil;
import forge.net.OfflineLobby;
import forge.net.client.FGameClient;
+import forge.net.server.FServerManager;
+import forge.properties.ForgeConstants;
import forge.screens.LoadingOverlay;
import forge.screens.constructed.LobbyScreen;
import forge.screens.online.OnlineMenu.OnlineScreen;
+import forge.util.gui.SOptionPane;
public class OnlineLobbyScreen extends LobbyScreen implements IOnlineLobby {
public OnlineLobbyScreen() {
super(null, OnlineMenu.getMenu(), new OfflineLobby());
}
+ private static GameLobby gameLobby;
+
+ public static GameLobby getGameLobby() {
+ return gameLobby;
+ }
+
+ public static void clearGameLobby() {
+ gameLobby = null;
+ }
+
+ public static void setGameLobby(GameLobby gameLobby) {
+ OnlineLobbyScreen.gameLobby = gameLobby;
+ }
+
+ private static FGameClient fGameClient;
+
+ public static FGameClient getfGameClient() {
+ return fGameClient;
+ }
+
+ public static void closeClient() {
+ getfGameClient().close();
+ fGameClient = null;
+ }
+
+ @Override
+ public void closeConn(String msg) {
+ clearGameLobby();
+ Forge.back();
+ if (msg.length() > 0) {
+ FThreads.invokeInBackgroundThread(new Runnable() {
+ @Override
+ public void run() {
+ final boolean callBackAlwaysTrue = SOptionPane.showOptionDialog(msg, "Error", FSkinProp.ICO_WARNING, ImmutableList.of("Ok"), 1) == 0;
+ if (callBackAlwaysTrue) { //to activate online menu popup when player press play online
+ GuiBase.setInterrupted(false);
+
+ if(FServerManager.getInstance() != null)
+ FServerManager.getInstance().stopServer();
+ if(getfGameClient() != null)
+ closeClient();
+ }
+ }
+ });
+ }
+ }
+
@Override
public ILobbyView setLobby(GameLobby lobby0) {
initLobby(lobby0);
@@ -27,13 +80,17 @@ public class OnlineLobbyScreen extends LobbyScreen implements IOnlineLobby {
@Override
public void setClient(FGameClient client) {
- // TODO Auto-generated method stub
-
+ fGameClient = client;
}
@Override
public void onActivate() {
- if (getLobby() instanceof OfflineLobby) {
+ if (GuiBase.isInterrupted()) {
+ GuiBase.setInterrupted(false);
+ return;
+ }
+ if (getGameLobby() == null) {
+ setGameLobby(getLobby());
//prompt to connect to server when offline lobby activated
FThreads.invokeInBackgroundThread(new Runnable() {
@Override
@@ -43,7 +100,7 @@ public class OnlineLobbyScreen extends LobbyScreen implements IOnlineLobby {
@Override
public void run() {
if (url == null) {
- Forge.back(); //go back to previous screen if user cancels connection
+ closeConn(""); //go back to previous screen if user cancels connection
return;
}
@@ -56,6 +113,10 @@ public class OnlineLobbyScreen extends LobbyScreen implements IOnlineLobby {
final IOnlineChatInterface chatInterface = (IOnlineChatInterface)OnlineScreen.Chat.getScreen();
if (joinServer) {
result = NetConnectUtil.join(url, OnlineLobbyScreen.this, chatInterface);
+ if (result.getMessage() == ForgeConstants.CLOSE_CONN_COMMAND) { //this message is returned via netconnectutil on exception
+ closeConn("Invalid host address (" + url + ") was detected.");
+ return;
+ }
}
else {
result = NetConnectUtil.host(OnlineLobbyScreen.this, chatInterface);
@@ -69,6 +130,8 @@ public class OnlineLobbyScreen extends LobbyScreen implements IOnlineLobby {
}
});
}
+ //update menu buttons
+ OnlineScreen.Lobby.update();
}
});
}
diff --git a/forge-gui-mobile/src/forge/screens/online/OnlineMenu.java b/forge-gui-mobile/src/forge/screens/online/OnlineMenu.java
index f796b3d55d3..614e671c479 100644
--- a/forge-gui-mobile/src/forge/screens/online/OnlineMenu.java
+++ b/forge-gui-mobile/src/forge/screens/online/OnlineMenu.java
@@ -6,16 +6,22 @@ import forge.assets.FSkinImage;
import forge.menu.FMenuItem;
import forge.menu.FPopupMenu;
import forge.model.FModel;
+import forge.net.server.FServerManager;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.screens.FScreen;
import forge.toolbox.FEvent;
import forge.toolbox.FEvent.FEventHandler;
+import forge.toolbox.FOptionPane;
+import forge.util.Callback;
+
+import static forge.screens.online.OnlineLobbyScreen.getGameLobby;
public class OnlineMenu extends FPopupMenu {
public enum OnlineScreen {
- Lobby("Lobby", FSkinImage.FAVICON, OnlineLobbyScreen.class),
- Chat("Chat", FSkinImage.QUEST_NOTES, OnlineChatScreen.class);
+ Lobby("Lobby", FSkinImage.QUEST_GEAR, OnlineLobbyScreen.class),
+ Chat("Chat", FSkinImage.QUEST_NOTES, OnlineChatScreen.class),
+ Disconnect("Disconnect", FSkinImage.DELETE, null);
private final FMenuItem item;
private final Class extends FScreen> screenClass;
@@ -26,6 +32,30 @@ public class OnlineMenu extends FPopupMenu {
item = new FMenuItem(caption0, icon0, new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
+ if(screenClass == null) {
+ FOptionPane.showConfirmDialog(
+ "Leave lobby? Doing so will shut down all connections and stop hosting.",
+ "Disconnect", new Callback() {
+ @Override
+ public void run(Boolean result) {
+ if (result) {
+ if (FServerManager.getInstance() != null)
+ if(FServerManager.getInstance().isHosting()) {
+ FServerManager.getInstance().unsetReady();
+ FServerManager.getInstance().stopServer();
+ }
+
+ if (OnlineLobbyScreen.getfGameClient() != null)
+ OnlineLobbyScreen.closeClient();
+
+ Forge.back();
+ screen = null;
+ OnlineLobbyScreen.clearGameLobby();
+ }
+ }
+ });
+ return;
+ }
Forge.back(); //remove current screen from chain
open();
setPreferredScreen(OnlineScreen.this);
@@ -44,6 +74,7 @@ public class OnlineMenu extends FPopupMenu {
return;
}
}
+ update();
}
public void open() {
@@ -60,6 +91,17 @@ public class OnlineMenu extends FPopupMenu {
initializeScreen();
return screen;
}
+
+ public void update(){
+ for (OnlineScreen ngs : OnlineScreen.values()) {
+ if (ngs.ordinal() == 2){ //disconect
+ if (getGameLobby() == null)
+ ngs.item.setEnabled(false);
+ else
+ ngs.item.setEnabled(true);
+ }
+ }
+ }
}
private static final ForgePreferences prefs = FModel.getPreferences();
diff --git a/forge-gui-mobile/src/forge/screens/planarconquest/ConquestMenu.java b/forge-gui-mobile/src/forge/screens/planarconquest/ConquestMenu.java
index cffbd132a52..80ee1a8ff28 100644
--- a/forge-gui-mobile/src/forge/screens/planarconquest/ConquestMenu.java
+++ b/forge-gui-mobile/src/forge/screens/planarconquest/ConquestMenu.java
@@ -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);
diff --git a/forge-gui-mobile/src/forge/screens/planarconquest/ConquestRewardDialog.java b/forge-gui-mobile/src/forge/screens/planarconquest/ConquestRewardDialog.java
index e5086c2138c..239baa355d3 100644
--- a/forge-gui-mobile/src/forge/screens/planarconquest/ConquestRewardDialog.java
+++ b/forge-gui-mobile/src/forge/screens/planarconquest/ConquestRewardDialog.java
@@ -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);
}
}
diff --git a/forge-gui-mobile/src/forge/screens/quest/QuestMenu.java b/forge-gui-mobile/src/forge/screens/quest/QuestMenu.java
index 401852ee2e6..3cff3b41e84 100644
--- a/forge-gui-mobile/src/forge/screens/quest/QuestMenu.java
+++ b/forge-gui-mobile/src/forge/screens/quest/QuestMenu.java
@@ -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);
diff --git a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java
index 975eec792e8..c823baa07e0 100644
--- a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java
+++ b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java
@@ -3,6 +3,7 @@ package forge.screens.settings;
import com.badlogic.gdx.utils.Align;
import forge.Forge;
import forge.Graphics;
+import forge.GuiBase;
import forge.MulliganDefs;
import forge.StaticData;
import forge.ai.AiProfileUtil;
@@ -232,6 +233,37 @@ public class SettingsPage extends TabPage {
localizer.getMessage("cbLoadHistoricFormats"),
localizer.getMessage("nlLoadHistoricFormats")),
3);
+ lstSettings.addItem(new BooleanSetting(FPref.UI_LOAD_UNKNOWN_CARDS,
+ "Enable Unknown Cards",
+ "Enable Unknown Cards to be loaded to Unknown Set. (Requires restart)") {
+ @Override
+ public void select() {
+ super.select();
+ FOptionPane.showConfirmDialog(
+ localizer.getMessage("lblRestartForgeDescription"),
+ localizer.getMessage("lblRestartForge"),
+ localizer.getMessage("lblRestart"),
+ localizer.getMessage("lblLater"), new Callback() {
+ @Override
+ public void run(Boolean result) {
+ if (result) {
+ Forge.restart(true);
+ }
+ }
+ });
+ }
+ },
+ 3);
+ lstSettings.addItem(new BooleanSetting(FPref.UI_NETPLAY_COMPAT,
+ "Experimental Network Compatibility",
+ "Forge switches to compatible network stream. (If unsure, turn OFF this option)") {
+ @Override
+ public void select() {
+ super.select();
+ GuiBase.enablePropertyConfig(FModel.getPreferences().getPrefBoolean(FPref.UI_NETPLAY_COMPAT));
+ }
+ },
+ 3);
//Graphic Options
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_ONLINE_IMAGE_FETCHER,
diff --git a/forge-gui/MANUAL.txt b/forge-gui/MANUAL.txt
index 58a48b12da7..783763bd3d3 100644
--- a/forge-gui/MANUAL.txt
+++ b/forge-gui/MANUAL.txt
@@ -431,6 +431,7 @@ Regen - regeneration.wav - triggered when a creature is regenerated.
RemoveCounter - remove_counter.wav - triggered when a counter is removed from a permanent.
Sacrifice - sacrifice.wav - triggered when a permanent is sacrificed.
Sorcery [*] - sorcery.wav - triggered when a sorcery is played.
+StartOfDuel - start_duel.wav - triggered when a duel starts
Shuffle [*] - shuffle.wav - triggered when a player shuffles his deck.
Tap [*] - tap.wav - triggered when a permanent is tapped.
Token [*] - token.wav - triggered when a token is created.
diff --git a/forge-gui/pom.xml b/forge-gui/pom.xml
index d94849de169..929a9e2019d 100644
--- a/forge-gui/pom.xml
+++ b/forge-gui/pom.xml
@@ -4,7 +4,7 @@
forgeforge
- 1.6.33-SNAPSHOT
+ 1.6.34-SNAPSHOTforge-gui
@@ -54,7 +54,7 @@
io.nettynetty-all
- 4.1.43.Final
+ 4.1.48.Finalcompile
@@ -78,9 +78,9 @@
1.7.22
- org.mapdb
- elsa
- 3.0.0-M7
+ org.lz4
+ lz4-java
+ 1.7.1
diff --git a/forge-gui/res/blockdata/blocks.txt b/forge-gui/res/blockdata/blocks.txt
index 3db02878fcb..67d158df0af 100644
--- a/forge-gui/res/blockdata/blocks.txt
+++ b/forge-gui/res/blockdata/blocks.txt
@@ -83,4 +83,5 @@ War of the Spark, 3/6/WAR, WAR
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
\ No newline at end of file
+Theros Beyond Death, 3/6/THB, THB
+Mystery Booster, 3/6/THB, MB1
\ No newline at end of file
diff --git a/forge-gui/res/blockdata/printsheets.txt b/forge-gui/res/blockdata/printsheets.txt
index fbb6d083c2b..7f197b47fc4 100644
--- a/forge-gui/res/blockdata/printsheets.txt
+++ b/forge-gui/res/blockdata/printsheets.txt
@@ -1020,11 +1020,11 @@ Wasteland|EXP
[CN2 Draft Matters]
Adriana's Valor
-#Assemble the Rank and Vile
+Assemble the Rank and Vile
Echoing Boon
#Emissary's Ploy
Hired Heist
-#Hold the Permiter
+Hold the Perimeter
Hymn of the Wilds
Incendiary Dissent
Natural Unity
@@ -1657,7 +1657,7 @@ Torbran, Thane of Red Fell|ELD|2
Feasting Troll King|ELD|2
Gilded Goose|ELD|2
The Great Henge|ELD|2
-Once Upon A Time|ELD|2
+Once Upon a Time|ELD|2
Questing Beast|ELD|2
Return of the Wildspeaker|ELD|2
Wicked Wolf|ELD|2
@@ -1778,4 +1778,1972 @@ Alseid of Life's Bounty|THB|2
Thirst for Meaning|THB|2
Gray Merchant of Asphodel|THB|2
Thrill of Possibility|THB|2
-Wolfwillow Haven|THB|2
\ No newline at end of file
+Wolfwillow Haven|THB|2
+
+[MB1 White CommonUncommon]
+Abzan Falconer
+Abzan Runemark
+Acrobatic Maneuver
+Adanto Vanguard
+Affa Protector
+Ainok Bond-Kin
+Ajani's Pridemate
+Alley Evasion
+Angelic Gift
+Angelic Purge
+Angel of Mercy
+Angel of Renewal
+Angelsong
+Apostle's Blessing
+Arrest
+Arrester's Zeal
+Artful Maneuver
+Aura of Silence
+Aven Battle Priest
+Aven Sentry
+Ballynock Cohort
+Bartered Cow
+Battle Mastery
+Benevolent Ancestor
+Blade Instructor
+Blessed Spirits
+Bonds of Faith
+Borrowed Grace
+Built to Last
+Bulwark Giant
+Candlelight Vigil
+Caravan Escort
+Cartouche of Solidarity
+Cast Out
+Cathar's Companion
+Caught in the Brights
+Celestial Crusader
+Celestial Flare
+Center Soul
+Champion of Arashin
+Charge
+Cliffside Lookout
+Cloudshift
+#Coalition Honor Guard
+Collar the Culprit
+Congregate
+Conviction
+Countless Gears Renegade
+Court Homunculus
+Court Street Denizen
+Crib Swap
+Danitha Capashen, Paragon
+Daring Skyjek
+Darksteel Mutation
+Dauntless Cathar
+Dawnglare Invoker
+Decommission
+Defiant Strike
+Desperate Sentry
+Devilthorn Fox
+Disenchant
+Dismantling Blow
+Disposal Mummy
+Divine Favor
+Djeru's Renunciation
+Djeru's Resolve
+Doomed Traveler
+Dragon Bell Monk
+Dragon's Eye Sentry
+Dragon's Presence
+Eddytrail Hawk
+Emerge Unscathed
+Encampment Keeper
+Encircling Fissure
+Enduring Victory
+Enlightened Ascetic
+Ephemeral Shields
+Ephemerate
+Excavation Elephant
+Excoriate
+Expedition Raptor
+Expose Evil
+Exultant Skymarcher
+Eyes in the Skies
+Faithbearer Paladin
+Faith's Fetters
+Feat of Resistance
+Felidar Guardian
+Felidar Umbra
+Fencing Ace
+Fiend Hunter
+Firehoof Cavalry
+Forsake the Worldly
+Fortify
+Fragmentize
+Geist of the Moors
+Ghostblade Eidolon
+Gideon's Lawkeeper
+Gift of Estates
+Glaring Aegis
+Gleam of Resistance
+Glint-Sleeve Artisan
+God-Pharaoh's Faithful
+Gods Willing
+Grasp of the Hieromancer
+Great-Horn Krushok
+Guided Strike
+Gustcloak Skirmisher
+Gust Walker
+Healer's Hawk
+Healing Grace
+Healing Hands
+Heavy Infantry
+Humble
+Hyena Umbra
+Infantry Veteran
+Inquisitor's Ox
+Inspired Charge
+Intrusive Packbeast
+Iona's Judgment
+Isolation Zone
+Jubilant Mascot
+Knight of Cliffhaven
+Knight of Old Benalia
+Knight of Sorrows
+Knight of the Skyward Eye
+Knight of the Tusk
+Kor Bladewhirl
+Kor Firewalker
+Kor Hookmaster
+Kor Sky Climber
+Kor Skyfisher
+Leonin Relic-Warder
+Lieutenants of the Guard
+Lightform
+Lightwalker
+Lingering Souls
+Lone Missionary
+Lonesome Unicorn
+Looming Altisaur
+Lotus-Eye Mystics
+Loxodon Partisan
+Loyal Sentry
+Lunarch Mantle
+Mardu Hordechief
+Marked by Honor
+Meditation Puzzle
+Midnight Guard
+Momentary Blink
+Moonlit Strider
+Mortal's Ardor
+Mother of Runes
+Ninth Bridge Patrol
+Nyx-Fleece Ram
+Ondu Greathorn
+Ondu War Cleric
+Oreskos Swiftclaw
+Oust
+Pacifism
+Palace Jailer
+Palace Sentinels
+Paladin of the Bloodstained
+Path of Peace
+Path to Exile
+Peace of Mind
+Pegasus Courser
+Pentarch Ward
+Pitfall Trap
+Pressure Point
+Promise of Bunrei
+Prowling Caracal
+Rally the Peasants
+Raptor Companion
+Refurbish
+Renewed Faith
+Resurrection
+Retreat to Emeria
+Reviving Dose
+Rhet-Crop Spearmaster
+Righteous Cause
+Rootborn Defenses
+Sacred Cat
+Sanctum Gargoyle
+Sandstorm Charger
+Savannah Lions
+Seal of Cleansing
+Searing Light
+Seeker of the Way
+Sensor Splicer
+Seraph of the Suns
+Serra Disciple
+Serra's Embrace
+Sheer Drop
+Shining Aerosaur
+Shining Armor
+Shoulder to Shoulder
+Siegecraft
+Silverchase Fox
+Skyhunter Skirmisher
+Skymarcher Aspirant
+Skyspear Cavalry
+Slash of Talons
+Snubhorn Sentry
+Soulmender
+Soul Parry
+Soul-Strike Technique
+Soul Summons
+Soul Warden
+Sparring Mummy
+Spectral Gateguards
+Stalwart Aven
+Star-Crowned Stag
+Stave Off
+Steadfast Sentinel
+Stone Haven Medic
+Sunlance
+Sunrise Seeker
+Suppression Bonds
+Survive the Night
+Swords to Plowshares
+Take Vengeance
+Tandem Tactics
+Terashi's Grasp
+Territorial Hammerskull
+Thraben Inspector
+Thraben Standard Bearer
+Topan Freeblade
+Unwavering Initiate
+Veteran Swordsmith
+Village Bell-Ringer
+Voice of the Provinces
+Wake the Reflections
+Wall of Omens
+Wall of One Thousand Cuts
+Wandering Champion
+War Behemoth
+Wild Griffin
+Windborne Charge
+Winged Shepherd
+Wing Shards
+Youthful Knight
+Zealous Strike
+
+[MB1 Blue CommonUncommon]
+Academy Journeymage
+Aethersnipe
+Aether Spellbomb
+Aether Tradewinds
+Amass the Components
+Amphin Pathmage
+Anticipate
+Arcane Denial
+Archaeomancer
+Archetype of Imagination
+Artificer's Assistant
+Augur of Bolas
+Augury Owl
+Bastion Inventor
+Befuddle
+Benthic Giant
+Bewilder
+Blue Elemental Blast
+Borrowing 100,000 Arrows
+Brainstorm
+Brilliant Spectrum
+Brine Elemental
+Calculated Dismissal
+Caller of Gales
+Call to Heel
+Cancel
+Capture Sphere
+Cartouche of Knowledge
+Castaway's Despair
+Catalog
+Chart a Course
+Chillbringer
+Choking Tethers
+Chronostutter
+Circular Logic
+Citywatch Sphinx
+Claustrophobia
+Clear the Mind
+Cloak of Mists
+Cloud Elemental
+Cloudkin Seer
+Cloudreader Sphinx
+Clutch of Currents
+Compelling Argument
+Concentrate
+Condescend
+Containment Membrane
+Contingency Plan
+Contradict
+Convolute
+Coralhelm Guide
+Coral Trickster
+Counterspell
+Court Hussar
+Crashing Tide
+Crush Dissent
+Curiosity
+Curio Vendor
+Daze
+Dazzling Lights
+Decision Paralysis
+Deep Analysis
+Deep Freeze
+Diminish
+Dirgur Nemesis
+Dispel
+Displace
+Distortion Strike
+Divination
+Doorkeeper
+Dragon's Eye Savants
+Drag Under
+Dreadwaters
+Dream Cache
+Dream Twist
+Eel Umbra
+Embodiment of Spring
+Enlightened Maniac
+Ensoul Artifact
+Errant Ephemeron
+Essence Scatter
+Everdream
+Exclude
+Fact or Fiction
+Faerie Invaders
+Faerie Mechanist
+Failed Inspection
+Fascination
+Fathom Seer
+Flashfreeze
+Fledgling Mawcor
+Fleeting Distraction
+Fog Bank
+Fogwalker
+Foil
+Forbidden Alchemy
+Frantic Search
+Frilled Sea Serpent
+Frost Lynx
+Gaseous Form
+Ghost Ship
+Glacial Crasher
+Glint
+Gone Missing
+Grasp of Phantoms
+Guard Gomazoa
+Gurmag Drowner
+Gush
+Hieroglyphic Illumination
+Hightide Hermit
+Hinterland Drake
+Horseshoe Crab
+Humongulus
+Impulse
+Inkfathom Divers
+Invisibility
+Ior Ruin Expedition
+Jace's Phantasm
+Jeering Homunculus
+Jeskai Sage
+Jwar Isle Avenger
+Kiora's Dambreaker
+Laboratory Brute
+Laboratory Maniac
+Labyrinth Guardian
+Lay Claim
+Leapfrog
+Mahamoti Djinn
+Mana Leak
+Man-o'-War
+Maximize Altitude
+Memory Lapse
+Merfolk Looter
+Messenger Jays
+Metallic Rebuke
+Mind Sculpt
+Mist Raven
+Mnemonic Wall
+Monastery Loremaster
+Mulldrifter
+Murder of Crows
+Mystical Teachings
+Mystic of the Hidden Way
+Nagging Thoughts
+Negate
+Niblis of Dusk
+Nine-Tail White Fox
+Ninja of the Deep Hours
+Ojutai Interceptor
+Ojutai's Breath
+Omenspeaker
+Opportunity
+Opt
+Peel from Reality
+Phantasmal Bear
+Phyrexian Ingester
+Pondering Mage
+Portent
+Predict
+Preordain
+Prodigal Sorcerer
+Propaganda
+Prosperous Pirates
+Purple-Crystal Crab
+Refocus
+Repulse
+Retraction Helix
+Riftwing Cloudskate
+Ringwarden Owl
+River Darter
+River Serpent
+Riverwheel Aerialists
+Sage of Lat-Nam
+Sailor of Means
+Scroll Thief
+Sea Gate Oracle
+Sealock Monster
+Secrets of the Golden City
+Send to Sleep
+Shaper Parasite
+Shimmerscale Drake
+Shipwreck Looter
+Sigiled Starfish
+Silent Observer
+Silvergill Adept
+Singing Bell Strike
+Skaab Goliath
+Skitter Eel
+Skittering Crustacean
+Sleep
+Slipstream Eel
+Slither Blade
+Snap
+Snapping Drake
+Somber Hoverguard
+Sphinx's Tutelage
+Spire Monitor
+Steady Progress
+Stitched Drake
+Storm Sculptor
+Strategic Planning
+Stream of Thought
+Surrakar Banisher
+Syncopate
+Syr Elenora, the Discerning
+Tandem Lookout
+Temporal Fissure
+Thornwind Faeries
+Thoughtcast
+Thought Collapse
+Thought Scour
+Thrummingbird
+Thunder Drake
+Tidal Warrior
+Tidal Wave
+Totally Lost
+Trail of Evidence
+Treasure Cruise
+Treasure Hunt
+Treasure Mage
+Trinket Mage
+Triton Tactics
+Turn Aside
+Uncomfortable Chill
+Vapor Snag
+Vigean Graftmage
+Wall of Frost
+Warden of Evos Isle
+Watercourser
+Wave-Wing Elemental
+Weldfast Wingsmith
+Welkin Tern
+Whiplash Trap
+Windcaller Aven
+Wind Drake
+Wind-Kin Raiders
+Windrider Eel
+Wind Strider
+Wishcoin Crab
+Wishful Merfolk
+Write into Being
+Youthful Scholar
+
+[MB1 Black CommonUncommon]
+Absorb Vis
+Accursed Spirit
+Aid the Fallen
+Alesha's Vanguard
+Alley Strangler
+Altar's Reap
+Ambitious Aetherborn
+Ancestral Vengeance
+Animate Dead
+Annihilate
+Bala Ged Scorpion
+Baleful Ammit
+Balustrade Spy
+Bartizan Bats
+Bitter Revelation
+Black Cat
+Bladebrand
+Blessing of Belzenlok
+Blighted Bat
+Blightsoil Druid
+Blistergrub
+Blood Artist
+Bloodrite Invoker
+Bone Splinters
+Boon of Emrakul
+Breeding Pit
+Butcher's Glee
+Cabal Therapy
+Cackling Imp
+Cadaver Imp
+Caligo Skin-Witch
+Carrion Feeder
+Carrion Imp
+Catacomb Crocodile
+Catacomb Slug
+Caustic Tar
+Certain Death
+Child of Night
+Coat with Venom
+Corpsehatch
+Costly Plunder
+Covenant of Blood
+Cower in Fear
+Crippling Blight
+Crow of Dark Tidings
+Cursed Minotaur
+Daring Demolition
+Darkblast
+Dark Dabbling
+Dark Ritual
+Dark Withering
+Deadbridge Shaman
+Deadeye Tormentor
+Dead Reveler
+Death Denied
+Defeat
+Demonic Tutor
+Demonic Vigor
+Demon's Grasp
+Desperate Castaways
+Diabolic Edict
+Die Young
+Dinosaur Hunter
+Dirge of Dread
+Dismember
+Disowned Ancestor
+Doomed Dissenter
+Douse in Gloom
+Dreadbringer Lampads
+Dread Drone
+Dread Return
+Dregscape Zombie
+Driver of the Dead
+Drudge Sentinel
+Dukhara Scavenger
+Dune Beetle
+Duress
+Dusk Charger
+Dusk Legion Zealot
+Epicure of Blood
+Erg Raiders
+Eternal Thirst
+Evincar's Justice
+Executioner's Capsule
+Eyeblight's Ending
+Fallen Angel
+Farbog Revenant
+Fatal Push
+Fen Hauler
+Feral Abomination
+Festercreep
+Festering Newt
+Fetid Imp
+Fill with Fright
+First-Sphere Gargantua
+Flesh to Dust
+Fretwork Colony
+Fungal Infection
+Genju of the Fens
+Ghostly Changeling
+Ghoulcaller's Accomplice
+Gifted Aetherborn
+Go for the Throat
+Grasping Scoundrel
+Gravedigger
+Gravepurge
+Gray Merchant of Asphodel
+Grim Affliction
+Grim Discovery
+Grixis Slavedriver
+Grotesque Mutation
+Gruesome Fate
+Gurmag Angler
+Hideous End
+Hired Blade
+Hound of the Farbogs
+Induce Despair
+Infernal Scarring
+Infest
+Innocent Blood
+Inquisition of Kozilek
+Instill Infection
+Kalastria Nightwatch
+Krumar Bond-Kin
+Lawless Broker
+Lazotep Behemoth
+Lethal Sting
+Lord of the Accursed
+Macabre Waltz
+Marauding Boneslasher
+March of the Drowned
+Mark of the Vampire
+Marsh Hulk
+Mephitic Vapors
+Merciless Resolve
+Miasmic Mummy
+Mind Rake
+Mind Rot
+Mire's Malice
+Moment of Craving
+Murder
+Murderous Compulsion
+Nameless Inversion
+Nantuko Husk
+Never Happened
+Night's Whisper
+Nirkana Assassin
+Noxious Dragon
+Okiba-Gang Shinobi
+Painful Lesson
+Phyrexian Rager
+Phyrexian Reclamation
+Pit Keeper
+Plaguecrafter
+Plagued Rusalka
+Plague Wight
+Prakhata Club Security
+Prowling Pangolin
+Queen's Agent
+Quest for the Gravelord
+Rabid Bloodsucker
+Rakdos Drake
+Rakshasa's Secret
+Ravenous Chupacabra
+Read the Bones
+Reaper of Night
+Reassembling Skeleton
+Reckless Imp
+Reckless Spite
+Recover
+Renegade Demon
+Renegade's Getaway
+Returned Centaur
+Revenant
+Rite of the Serpent
+Rotfeaster Maggot
+Ruin Rat
+Scarab Feast
+Scrounger of Souls
+Scuttling Death
+Seal of Doom
+Sengir Vampire
+Shadowcloak Vampire
+Shambling Attendants
+Shambling Goblin
+Shriekmaw
+Silumgar Butcher
+Skeletal Scrying
+Skeleton Archer
+Skulking Ghost
+Smiting Helix
+Spreading Rot
+Stab Wound
+Stallion of Ashmouth
+Stinkweed Imp
+Street Wraith
+Stromkirk Patrol
+Subtle Strike
+Sultai Runemark
+Tar Snare
+Tavern Swindler
+Tendrils of Corruption
+Thallid Omnivore
+The Eldest Reborn
+Thornbow Archer
+Thorn of the Black Rose
+Thraben Foulbloods
+Tidy Conclusion
+Torment of Venom
+Touch of Moonglove
+Tragic Slip
+Trespasser's Curse
+Trial of Ambition
+Twins of Maurer Estate
+Typhoid Rats
+Unburden
+Undercity's Embrace
+Untamed Hunger
+Unyielding Krumar
+Urborg Uprising
+Vampire Champion
+Vampire Envoy
+Vampire Hexmage
+Vampire Lacerator
+Vampire Nighthawk
+Vessel of Malignity
+Virulent Swipe
+Voracious Null
+Vraska's Finisher
+Wake of Vultures
+Walking Corpse
+Walk the Plank
+Wander in Death
+Warteye Witch
+Weight of the Underworld
+Weirded Vampire
+Wight of Precinct Six
+Will-o'-the-Wisp
+Windgrace Acolyte
+Wrench Mind
+Yargle, Glutton of Urborg
+Zulaport Chainmage
+
+[MB1 Red CommonUncommon]
+Act of Treason
+Act on Impulse
+Ahn-Crop Crasher
+Ainok Tracker
+Akroan Sergeant
+Alchemist's Greeting
+Ancient Grudge
+Anger
+Arc Trail
+Arrow Storm
+Atarka Efreet
+Avarax
+Azra Bladeseeker
+Balduvian Horde
+Barging Sergeant
+Barrage of Boulders
+Battle Rampart
+Battle-Rattle Shaman
+Beetleback Chief
+Bellows Lizard
+Blades of Velis Vel
+Blastfire Bolt
+Blazing Volley
+Blindblast
+Bloodfire Expert
+Bloodlust Inciter
+Bloodmad Vampire
+Blood Ogre
+Bloodstone Goblin
+Blow Your House Down
+Blur of Blades
+Boggart Brute
+Boiling Earth
+Bombard
+Bomber Corps
+Borrowed Hostility
+Boulder Salvo
+Brazen Buccaneers
+Brazen Wolves
+Bring Low
+Browbeat
+Brute Strength
+Built to Smash
+Burst Lightning
+Canyon Lurkers
+Cartouche of Zeal
+Cathartic Reunion
+Chandra's Pyrohelix
+Chandra's Revolution
+Charging Monstrosaur
+Chartooth Cougar
+Cinder Hellion
+Cleansing Screech
+Cobblebrute
+Cosmotronic Wave
+Crash Through
+Crowd's Favor
+Crown-Hunter Hireling
+Curse of Opulence
+Curse of the Nightly Hunt
+Death by Dragons
+Defiant Ogre
+Demolish
+Desert Cerodon
+Desperate Ravings
+Destructive Tampering
+Direct Current
+Distemper of the Blood
+Dragon Breath
+Dragon Egg
+Dragon Fodder
+Dragonsoul Knight
+Dragon Whelp
+Dual Shot
+Dynacharge
+Earth Elemental
+Emrakul's Hatcher
+Enthralling Victor
+Erratic Explosion
+Expedite
+Faithless Looting
+Falkenrath Reaver
+Fall of the Hammer
+Fervent Strike
+Fierce Invocation
+Fiery Hellhound
+Fiery Temper
+Fireball
+Firebolt
+Firebrand Archer
+Fire Elemental
+Flame Jab
+Flametongue Kavu
+Flamewave Invoker
+Fling
+Forge Devil
+Foundry Street Denizen
+Frenzied Raptor
+Frilled Deathspitter
+Frontline Devastator
+Frontline Rebel
+Furnace Whelp
+Fury Charm
+Galvanic Blast
+Generator Servant
+Genju of the Spires
+Geomancer's Gambit
+Ghitu Lavarunner
+Giant Spectacle
+Goblin Assault
+Goblin Balloon Brigade
+Goblin Bombardment
+Goblin Fireslinger
+Goblin Locksmith
+Goblin Matron
+Goblin Motivator
+Goblin Oriflamme
+Goblin Roughrider
+Goblin Warchief
+Goblin War Paint
+Gorehorn Minotaurs
+Gore Swine
+Granitic Titan
+Grapeshot
+Gravitic Punch
+Gut Shot
+Guttersnipe
+Hammerhand
+Hanweir Lancer
+Hardened Berserker
+Hijack
+Hulking Devil
+Hyena Pack
+Ill-Tempered Cyclops
+Impact Tremors
+Incorrigible Youths
+Inferno Fist
+Inferno Jet
+Ingot Chewer
+Insolent Neonate
+Jackal Pup
+Keldon Halberdier
+Keldon Overseer
+Khenra Scrapper
+Kiln Fiend
+Kird Ape
+Kolaghan Stormsinger
+Krenko's Command
+Krenko's Enforcer
+Leaping Master
+Leopard-Spotted Jiao
+Lightning Bolt
+Lightning Javelin
+Lightning Shrieker
+Lightning Talons
+Madcap Skills
+Magma Spray
+Makindi Sliderunner
+Mardu Warshrieker
+Mark of Mutiny
+Maximize Velocity
+Miner's Bane
+Mogg Fanatic
+Mogg Flunkies
+Mogg War Marshal
+Molten Rain
+Monastery Swiftspear
+Mutiny
+Nimble-Blade Khenra
+Ondu Champion
+Orcish Cannonade
+Orcish Oriflamme
+Outnumber
+Pillage
+Price of Progress
+Prickleboar
+Prophetic Ravings
+Pyrotechnics
+Quakefoot Cyclops
+Rampaging Cyclops
+Reckless Fireweaver
+Reckless Wurm
+Renegade Tactics
+Rivals' Duel
+Roast
+Rolling Thunder
+Rubblebelt Maaka
+Ruinous Gremlin
+Rummaging Goblin
+Run Amok
+Rush of Adrenaline
+Salivating Gremlins
+Samut's Sprint
+Sarkhan's Rage
+Screamreach Brawler
+Seismic Shift
+Seismic Stomp
+Shatter
+Shattering Spree
+Shenanigans
+Shock
+Skirk Commando
+Skirk Prospector
+Smash to Smithereens
+Smelt
+Sparkmage Apprentice
+Sparkspitter
+Sparktongue Dragon
+Spikeshot Goblin
+Staggershock
+Stormblood Berserker
+Sulfurous Blast
+Summit Prowler
+Sun-Crowned Hunters
+Swashbuckling
+Sweatworks Brawler
+Swift Kick
+Tarfire
+Tectonic Rift
+Temur Battle Rage
+Thresher Lizard
+Thrill of Possibility
+Tibalt's Rager
+Torch Courier
+Uncaged Fury
+Undying Rage
+Valakut Invoker
+Valakut Predator
+Valley Dasher
+Vandalize
+Vent Sentinel
+Vessel of Volatility
+Volcanic Dragon
+Volcanic Rush
+Voldaren Duelist
+Wall of Fire
+Wayward Giant
+Wildfire Emissary
+Wojek Bodyguard
+Young Pyromancer
+Zada's Commando
+Zealot of the God-Pharaoh
+
+[MB1 Green CommonUncommon]
+Abundant Growth
+Acidic Slime
+Adventurous Impulse
+Aerie Bowmasters
+Affectionate Indrik
+Aggressive Instinct
+Aggressive Urge
+Ainok Survivalist
+Alpine Grizzly
+Ambassador Oak
+Ancestral Mask
+Ancient Brontodon
+Ancient Stirrings
+Arachnus Web
+Arbor Armament
+Arbor Elf
+Aura Gnarlid
+Avacyn's Pilgrim
+Backwoods Survivalists
+Baloth Gorger
+Basking Rootwalla
+Beastbreaker of Bala Ged
+Beast Within
+Become Immense
+Beneath the Sands
+Bestial Menace
+Bitterblade Warrior
+Bitterbow Sharpshooters
+Blanchwood Armor
+Blastoderm
+Blossom Dryad
+Borderland Explorer
+Borderland Ranger
+Briarhorn
+Bristling Boar
+Broken Bond
+Broodhunter Wurm
+Byway Courier
+Canopy Spider
+Carnivorous Moss-Beast
+Caustic Caterpillar
+Centaur Courser
+Charging Rhino
+Citanul Woodreaders
+Clip Wings
+Colossal Dreadmaw
+Combo Attack
+Commune with Nature
+Commune with the Gods
+Conifer Strider
+Creeping Mold
+Crop Rotation
+Crossroads Consecrator
+Crowned Ceratok
+Crushing Canopy
+Cultivate
+Daggerback Basilisk
+Dawn's Reflection
+Death-Hood Cobra
+Desert Twister
+Destructor Dragon
+Dissenter's Deliverance
+Domesticated Hydra
+Dragonscale Boon
+Dragon-Scarred Bear
+Durkwood Baloth
+Earthen Arms
+Elemental Uprising
+Elephant Guide
+Elves of Deep Shadow
+Elvish Fury
+Elvish Visionary
+Elvish Warrior
+Ember Weaver
+Epic Confrontation
+Essence Warden
+Eternal Witness
+Experiment One
+Explore
+Explosive Vegetation
+Ezuri's Archers
+Fade into Antiquity
+Farseek
+Feed the Clan
+Feral Krushok
+Feral Prowler
+Ferocious Zheng
+Fertile Ground
+Fierce Empath
+Fog
+Formless Nurturing
+Frontier Mastodon
+Gaea's Blessing
+Gaea's Protector
+Giant Growth
+Giant Spider
+Gift of Growth
+Gift of Paradise
+Glade Watcher
+Gnarlid Pack
+Grapple with the Past
+Grazing Gladehart
+Greater Basilisk
+Greater Sandwurm
+Greenwood Sentinel
+Groundswell
+Guardian Shield-Bearer
+Hamlet Captain
+Hardy Veteran
+Harmonize
+Harrow
+Hooded Brawler
+Hooting Mandrills
+Hunter's Ambush
+Hunt the Weak
+Imperious Perfect
+Invigorate
+Ivy Lane Denizen
+Jungle Delver
+Jungle Wayfinder
+Kavu Climber
+Kavu Primarch
+Khalni Heart Expedition
+Kin-Tree Warden
+Kozilek's Predator
+Kraul Foragers
+Kraul Warrior
+Krosan Druid
+Krosan Tusker
+Larger Than Life
+Lay of the Land
+Lead by Example
+Lead the Stampede
+Lifespring Druid
+Lignify
+Llanowar Elves
+Llanowar Empath
+Longshot Squad
+Lure
+Manglehorn
+Mantle of Webs
+Map the Wastes
+Might of the Masses
+Mulch
+Natural Connection
+Naturalize
+Nature's Claim
+Nature's Lore
+Nest Invader
+Nettle Sentinel
+New Horizons
+Nimble Mongoose
+Oakgnarl Warrior
+Ondu Giant
+Oran-Rief Invoker
+Overgrown Armasaur
+Overgrown Battlement
+Overrun
+Pack's Favor
+Peema Outrider
+Pelakka Wurm
+Penumbra Spider
+Pierce the Sky
+Pinion Feast
+Plummet
+Pouncing Cheetah
+Prey's Vengeance
+Prey Upon
+Priest of Titania
+Pulse of Murasa
+Quiet Disrepair
+Rain of Thorns
+Rampant Growth
+Rancor
+Ranger's Guile
+Ravenous Leucrocota
+Reclaim
+Reclaiming Vines
+Regrowth
+Relic Crush
+Return to the Earth
+Revive
+Rhox Maulers
+Riparian Tiger
+Roar of the Wurm
+Root Out
+Roots
+Rosethorn Halberd
+Runeclaw Bear
+Sagu Archer
+Sakura-Tribe Elder
+Saproling Migration
+Savage Punch
+Scatter the Seeds
+Seal of Strength
+Search for Tomorrow
+Seek the Horizon
+Seek the Wilds
+Shape the Sands
+Siege Wurm
+Silhana Ledgewalker
+Silkweaver Elite
+Snake Umbra
+Snapping Sailback
+Spider Spawning
+Stalking Tiger
+Stoic Builder
+Strength in Numbers
+Sylvan Bounty
+Sylvan Scrying
+Tajuru Pathwarden
+Tajuru Warcaller
+Take Down
+Talons of Wildwood
+Terrain Elemental
+Territorial Baloth
+#The Crowd Goes Wild
+Thornhide Wolves
+Thornscape Battlemage
+Thornweald Archer
+Thrashing Brontodon
+Thrive
+Timberwatch Elf
+Time to Feed
+Titanic Growth
+Tukatongue Thallid
+Turntimber Basilisk
+Vastwood Gorger
+Venom Sliver
+Watcher in the Web
+Wellwisher
+Wild Growth
+Wild Mongrel
+Wildsize
+Wolfkin Bond
+Woodborn Behemoth
+Woolly Loxodon
+Wren's Run Vanquisher
+Yavimaya Elder
+Yavimaya Sapherd
+Yeva's Forcemage
+Zendikar's Roil
+
+[MB1 Multi CommonUncommon]
+Azorius Charm
+Cunning Breezedancer
+Ethercaste Knight
+Kiss of the Amesha
+Lawmage's Binding
+Migratory Route
+Mistmeadow Witch
+Raff Capashen, Ship's Mage
+Reflector Mage
+Riptide Crab
+Agony Warp
+Baleful Strix
+Call of the Nightwing
+Contraband Kingpin
+Deny Reality
+Extract from Darkness
+Kathari Remnant
+Shipwreck Singer
+Soul Manipulation
+Thought Erasure
+Azra Oddsmaker
+Bituminous Blast
+Bladewing the Risen
+Blightning
+Cauldron Dance
+Claim // Fame
+Goblin Deathraiders
+Shambling Remains
+Terminate
+Unlicensed Disintegration
+Bloodbraid Elf
+Draconic Disciple
+Fires of Yavimaya
+Ghor-Clan Rampager
+Giantbaiting
+Raging Swordtooth
+Rosheen Meanderer
+Savage Twister
+Treacherous Terrain
+Vengeful Rebirth
+Zhur-Taa Druid
+Armadillo Cloak
+Belligerent Brontodon
+Citadel Castellan
+Engineered Might
+Join Shields
+Pollenbright Wings
+Qasali Pridemage
+Rosemane Centaur
+Satyr Enchanter
+Selesnya Guildmage
+Unflinching Courage
+Wayfaring Temple
+Campaign of Vengeance
+Drana's Emissary
+Gift of Orzhova
+Gwyllion Hedge-Mage
+Hidden Stockpile
+Mortify
+Pillory of the Sleepless
+Tithe Drinker
+Underworld Coinsmith
+Zealous Persecution
+Fire // Ice
+Gelectrode
+Hypothesizzle
+Maverick Thopterist
+Mercurial Geists
+Nucklavee
+Reclusive Artificer
+Shrewd Hatchling
+Stormchaser Chimera
+Wee Dragonauts
+Baloth Null
+Corpsejack Menace
+Deathreap Ritual
+Grim Contest
+Kin-Tree Invocation
+Obelisk Spider
+Ochran Assassin
+Putrefy
+Winding Constrictor
+Akroan Hoplite
+Boros Challenger
+Flame-Kin Zealot
+Hammer Dropper
+Highspire Mantis
+Iroas's Champion
+Lightning Helix
+Martial Glory
+Weapons Trainer
+Bounding Krasis
+Coiling Oracle
+Ethereal Ambush
+Jungle Barrier
+Kiora's Follower
+Plaxcaster Frogling
+River Hoopoe
+Shardless Agent
+Tatyova, Benthic Druid
+Urban Evolution
+Esper Charm
+Thopter Foundry
+Tower Gargoyle
+Crosis's Charm
+Sedraxis Specter
+Slave of Bolas
+Sprouting Thrinax
+Naya Charm
+Woolly Thoctar
+Rhox War Monk
+Skyward Eye Prophets
+Mardu Roughrider
+Bear's Companion
+Abzan Charm
+Abzan Guide
+Armament Corps
+Warden of the Eye
+Sultai Charm
+Sultai Soothsayer
+Fusion Elemental
+
+[MB1 Artifact Land CommonUncommon]
+Blinding Souleater
+Benthic Infiltrator
+Wretched Gryff
+Implement of Malice
+Call the Scions
+Gruul Signet
+Simic Locket
+Alchemist's Vial
+Alloy Myr
+Armillary Sphere
+Artisan of Kozilek
+Ashnod's Altar
+Bomat Bazaar Barge
+Bone Saw
+Bottle Gnomes
+Breaker of Armies
+Burnished Hart
+Cathodion
+Coldsteel Heart
+Consulate Dreadnought
+Copper Carapace
+Crystal Ball
+Crystal Chimes
+Diamond Mare
+Eldrazi Devastator
+Emmessi Tome
+Etched Oracle
+Farmstead Gleaner
+Filigree Familiar
+Flayer Husk
+Foundry Inspector
+Fountain of Renewal
+Frogmite
+Guardians of Meletis
+Heavy Arbalest
+Herald's Horn
+Hexplate Golem
+Hot Soup
+Icy Manipulator
+Irontread Crusher
+Juggernaut
+Lightning Greaves
+Loxodon Warhammer
+Mask of Memory
+Meteorite
+Millikin
+Millstone
+Mind Stone
+Mishra's Bauble
+Moonglove Extract
+Mortarpod
+Myr Retriever
+Myr Sire
+Ornithopter
+Palladium Myr
+Peace Strider
+Perilous Myr
+Pilgrim's Eye
+Prophetic Prism
+Renegade Map
+Rhonas's Monument
+Sandstone Oracle
+Serrated Arrows
+Short Sword
+Sigil of Valor
+Skullclamp
+Skyscanner
+Sol Ring
+Sorcerer's Broom
+Spy Kit
+Sunset Pyramid
+Suspicious Bookcase
+Thought Vessel
+Thran Dynamo
+Thran Golem
+Tormod's Crypt
+Trepanation Blade
+Universal Automaton
+Universal Solvent
+Whispersilk Cloak
+New Benalia
+Faerie Conclave
+Blighted Fen
+Bojuka Bog
+Forgotten Cave
+Goblin Burrows
+Great Furnace
+Wirewood Lodge
+Sejiri Refuge
+Dismal Backwater
+Dreadship Reef
+Akoum Refuge
+Kazandu Refuge
+Skarrg, the Rage Pits
+Blossoming Sands
+Graypelt Refuge
+Orzhov Basilica
+Scoured Barrens
+Swiftwater Cliffs
+Jungle Hollow
+Thornwood Falls
+Arcane Sanctum
+Crumbling Necropolis
+Jungle Shrine
+Frontier Bivouac
+Sandsteppe Citadel
+Aether Hub
+Ash Barrens
+Blasted Landscape
+Darksteel Citadel
+Evolving Wilds
+Field of Ruin
+Gateway Plaza
+Ghost Quarter
+Krosan Verge
+Mishra's Factory
+Reliquary Tower
+Rogue's Passage
+Tectonic Edge
+Temple of the False God
+Unclaimed Territory
+
+[MB1 Pre M15]
+Ana Sanctuary
+Ancient Den
+Ancient Ziggurat
+Angelic Destiny
+Archangel
+Asceticism
+Assemble the Legion
+Athreos, God of Passage
+Aura Shards
+Avalanche Riders
+Bear Cub
+Belbe's Portal
+Black Knight
+Bloom Tender
+Bonesplitter
+Bow of Nylea
+Brimstone Dragon
+Brimstone Mage
+Cairn Wanderer
+Carpet of Flowers
+Centaur Glade
+Chancellor of the Annex
+Chatter of the Squirrel
+Chromatic Star
+Contagion Clasp
+Corrupted Conscience
+Cragganwick Cremator
+Crenellated Wall
+Crystal Shard
+Darksteel Garrison
+Dauthi Mindripper
+Defense of the Heart
+Dictate of Erebos
+Dolmen Gate
+Dominus of Fealty
+Doomgape
+Draco
+Dragon Broodmother
+Dragon Mask
+Dungrove Elder
+Eater of Days
+Elixir of Immortality
+Empyrial Armor
+Enchanted Evening
+Energy Field
+Exsanguinate
+Flameshot
+Floodgate
+Font of Mythos
+Ghitu War Cry
+Gilt-Leaf Palace
+Goblin Game
+Greater Gargadon
+Guided Passage
+Haakon, Stromgald Scourge
+Hedron Crab
+Helm of Awakening
+Hunter of Eyeblights
+Hurricane
+Hypnotic Specter
+Impending Disaster
+Jushi Apprentice
+Kaervek's Torch
+Kargan Dragonlord
+Knight of Dawn
+Knollspine Dragon
+Kor Chant
+Kruphix, God of Horizons
+Lashknife Barrier
+Lotus Petal
+Maelstrom Archangel
+Magus of the Moat
+Mana Tithe
+Manamorphose
+Martyr's Bond
+Martyr's Cause
+Master Transmuter
+Meddling Mage
+Mistform Shrieker
+Nemesis of Reason
+Oracle of Nectars
+Pathrazer of Ulamog
+Perish
+Pestilence
+Phantasmal Dragon
+Phantom Centaur
+Phyrexian Metamorph
+Phyrexian Soulgorger
+Purphoros, God of the Forge
+Questing Phelddagrif
+Rage Reflection
+Recoup
+Release the Ants
+Rhys the Redeemed
+Rhystic Study
+Rishadan Footpad
+Rith, the Awakener
+River Boa
+Sadistic Hypnotist
+Sakashima the Impostor
+Sapphire Charm
+Shrouded Lore
+Soothsaying
+Sorin Markov
+Squirrel Wrangler
+Thieving Magpie
+Thrun, the Last Troll
+Time Sieve
+Timely Reinforcements
+Tinker
+Tower of Eons
+Toxin Sliver
+Triumph of the Hordes
+Umbral Mantle
+Viashino Sandstalker
+Violent Ultimatum
+Volunteer Reserves
+Wargate
+Weathered Wayfarer
+Wild Nacatl
+Yavimaya's Embrace
+
+[MB1 Post M15 RareMythic]
+Adorned Pouncer
+Aetherflux Reservoir
+Akroan Horse
+Alesha, Who Smiles at Death
+Alhammarret's Archive
+All Is Dust
+Aminatou's Augury
+Angel of the Dire Hour
+Anger of the Gods
+Animar, Soul of Elements
+Approach of the Second Sun
+Arch of Orazca
+Basilisk Collar
+Beacon of Immortality
+Beastmaster Ascension
+Birds of Paradise
+Black Market
+Boompile
+Boros Reckoner
+Caged Sun
+Cauldron of Souls
+Champion of the Parish
+Chaos Warp
+Chasm Skulker
+Chromatic Lantern
+Coat of Arms
+Collective Brutality
+Commit // Memory
+Courser of Kruphix
+Coveted Jewel
+Daretti, Scrap Savant
+Deadly Tempest
+Debtors' Knell
+Decree of Justice
+Deepglow Skate
+Desolation Twin
+Dictate of Heliod
+Djinn of Wishes
+Dragonlord Ojutai
+Drana, Kalastria Bloodchief
+Eldrazi Monument
+Eldritch Evolution
+Elesh Norn, Grand Cenobite
+Evra, Halcyon Witness
+Expropriate
+Fblthp, the Lost
+Felidar Sovereign
+Gideon Jura
+Goblin Charbelcher
+Goblin Piledriver
+Gonti, Lord of Luxury
+Grasp of Fate
+Grave Titan
+Gravecrawler
+Greenbelt Rampager
+Hornet Nest
+Kiki-Jiki, Mirror Breaker
+Kolaghan's Command
+Krenko, Mob Boss
+Liliana, Death's Majesty
+Living Death
+Mana Crypt
+Meandering Towershell
+Memory Erosion
+Meren of Clan Nel Toth
+Mimic Vat
+Mind Shatter
+Mind Spring
+Mirran Crusader
+Mirror Entity
+Misdirection
+Mizzix's Mastery
+Mycoloth
+Mystic Confluence
+Nighthowler
+Nin, the Pain Artist
+Nissa, Voice of Zendikar
+Odric, Lunarch Marshal
+Phyrexian Arena
+Phyrexian Plaguelord
+Precursor Golem
+Preyseizer Dragon
+Queen Marchesa
+Reality Scramble
+Recruiter of the Guard
+Release the Gremlins
+Revel in Riches
+Rune-Scarred Demon
+Savage Knuckleblade
+Selvala, Heart of the Wilds
+Serendib Efreet
+Sewer Nemesis
+Shamanic Revelation
+Sliver Hivelord
+Solemn Simulacrum
+Spawning Grounds
+Star of Extinction
+Steamflogger Boss
+Stunt Double
+Sudden Demise
+Supreme Verdict
+Sword of the Animist
+Talrand, Sky Summoner
+Taurean Mauler
+Teferi's Protection
+Teferi, Temporal Archmage
+Temporal Mastery
+Tempt with Discovery
+Thalia's Lancers
+The Gitrog Monster
+The Mirari Conjecture
+Tireless Tracker
+Torment of Hailfire
+Trading Post
+Two-Headed Giant
+Urza's Rage
+Vigor
+Wheel of Fate
+Whelming Wave
+Whir of Invention
+Yuriko, the Tiger's Shadow
+
+[FMB1 Foils]
+Not of This World+|FMB1
+Celestial Dawn+|FMB1
+Celestial Kirin+|FMB1
+Changeling Hero+|FMB1
+Council Guardian+|FMB1
+Eidolon of Rhetoric+|FMB1
+Isamaru, Hound of Konda+|FMB1
+Lapse of Certainty+|FMB1
+Lumithread Field+|FMB1
+Norn's Annex+|FMB1
+Proclamation of Rebirth+|FMB1
+Pull from Eternity+|FMB1
+Rune-Tail, Kitsune Ascendant+|FMB1
+Sinew Sliver+|FMB1
+Soul's Attendant+|FMB1
+Spelltithe Enforcer+|FMB1
+Springjack Shepherd+|FMB1
+Wall of Shards+|FMB1
+White Knight+|FMB1
+Blighted Agent+|FMB1
+Delay+|FMB1
+Fatespinner+|FMB1
+Frozen Aether+|FMB1
+Grand Architect+|FMB1
+Intruder Alarm+|FMB1
+Misthollow Griffin+|FMB1
+Paradox Haze+|FMB1
+Patron of the Moon+|FMB1
+Puca's Mischief+|FMB1
+Spellweaver Volute+|FMB1
+Storm Crow+|FMB1
+Zur's Weirding+|FMB1
+Bringer of the Black Dawn+|FMB1
+Chimney Imp+|FMB1
+Conspiracy+|FMB1
+Echoing Decay+|FMB1
+Funeral Charm+|FMB1
+Herald of Leshrac+|FMB1
+Marrow-Gnawer+|FMB1
+Nezumi Shortfang+|FMB1
+One with Nothing+|FMB1
+Ravenous Trap+|FMB1
+Rescue from the Underworld+|FMB1
+Undead Warchief+|FMB1
+Viscera Seer+|FMB1
+Balduvian Rage+|FMB1
+Braid of Fire+|FMB1
+Burning Inquiry+|FMB1
+Fiery Gambit+|FMB1
+Flamekin Harbinger+|FMB1
+Form of the Dragon+|FMB1
+Goblin Bushwhacker+|FMB1
+Guerrilla Tactics+|FMB1
+Lightning Storm+|FMB1
+Norin the Wary+|FMB1
+Ogre Gatecrasher+|FMB1
+Pyretic Ritual+|FMB1
+Scourge of the Throne+|FMB1
+Stigma Lasher+|FMB1
+Treasonous Ogre+|FMB1
+Allosaurus Rider+|FMB1
+Archetype of Endurance+|FMB1
+Boreal Druid+|FMB1
+Boundless Realms+|FMB1
+Bramblewood Paragon+|FMB1
+Fungusaur+|FMB1
+Game-Trail Changeling+|FMB1
+Gleeful Sabotage+|FMB1
+Greater Mossdog+|FMB1
+Helix Pinnacle+|FMB1
+Hornet Sting+|FMB1
+Manaweft Sliver+|FMB1
+Maro+|FMB1
+Myojin of Life's Web+|FMB1
+Panglacial Wurm+|FMB1
+Reki, the History of Kamigawa+|FMB1
+Rhox+|FMB1
+Sakura-Tribe Scout+|FMB1
+Scryb Ranger+|FMB1
+Sheltering Ancient+|FMB1
+Sosuke, Son of Seshiro+|FMB1
+Spike Feeder+|FMB1
+Aurelia's Fury+|FMB1
+Drogskol Captain+|FMB1
+Glittering Wish+|FMB1
+Harmonic Sliver+|FMB1
+Karrthus, Tyrant of Jund+|FMB1
+Maelstrom Nexus+|FMB1
+Mind Funeral+|FMB1
+Sarkhan the Mad+|FMB1
+Sen Triplets+|FMB1
+Yore-Tiller Nephilim+|FMB1
+Balefire Liege+|FMB1
+Gilder Bairn+|FMB1
+Kulrath Knight+|FMB1
+Noggle Bandit+|FMB1
+Wear // Tear+|FMB1
+Amulet of Vigor+|FMB1
+Blasting Station+|FMB1
+Codex Shredder+|FMB1
+Geth's Grimoire+|FMB1
+Iron Myr+|FMB1
+Knowledge Pool+|FMB1
+Lantern of Insight+|FMB1
+Leveler+|FMB1
+Lich's Mirror+|FMB1
+Magewright's Stone+|FMB1
+Memnite+|FMB1
+Mindslaver+|FMB1
+Pili-Pala+|FMB1
+Reaper King+|FMB1
+Sundial of the Infinite+|FMB1
+Teferi's Puzzle Box+|FMB1
+Trailblazer's Boots+|FMB1
+Triskelion+|FMB1
+Witchbane Orb+|FMB1
+Alchemist's Refuge+|FMB1
+Minamo, School at Water's Edge+|FMB1
+Mirrodin's Core+|FMB1
+Shizo, Death's Storehouse+|FMB1
+Stalking Stones+|FMB1
+
+[CN2 Not In Normal Slots]
+Adriana's Valor
+Assemble the Rank and Vile
+Echoing Boon
+#Emissary's Ploy
+Hired Heist
+Hold the Perimeter
+Hymn of the Wilds
+Incendiary Dissent
+Natural Unity
+#Sovereign's Realm
+#Summoner's Bond
+Weight Advantage
+Kaya, Ghost Assassin|CN2|2
+
+[CN2 Foil Kaya]
+Kaya, Ghost Assassin|CN2|2
+
+[IKO Secret Cards]
+Zilortha, Strength Incarnate
+Lukka, Coppercoat Outcast|IKO|2
+Vivien, Monsters' Advocate|IKO|2
+Narset of the Ancient Way|IKO|2
+Cubwarden|IKO|2
+Huntmaster Liger|IKO|2
+Majestic Auricorn|IKO|2
+Vulpikeet|IKO|2
+Archipelagore|IKO|2
+Dreamtail Heron|IKO|2
+Pouncing Shoreshark|IKO|2
+Sea-Dasher Octopus|IKO|2
+Cavern Whisperer|IKO|2
+Chittering Harvester|IKO|2
+Dirge Bat|IKO|2
+Insatiable Hemophage|IKO|2
+Cloudpiercer|IKO|2
+Everquill Phoenix|IKO|2
+Porcuparrot|IKO|2
+Auspicious Starrix|IKO|2
+Gemrazer|IKO|2
+Glowstone Recluse|IKO|2
+Migratory Greathorn|IKO|2
+Boneyard Lurker|IKO|2
+Brokkos, Apex of Forever|IKO|2
+Illuna, Apex of Wishes|IKO|2
+Lore Drakkis|IKO|2
+Necropanther|IKO|2
+Nethroi, Apex of Death|IKO|2
+Parcelbeast|IKO|2
+Regal Leosaur|IKO|2
+Snapdax, Apex of the Hunt|IKO|2
+Trumpeting Gnarr|IKO|2
+Vadrok, Apex of Thunder|IKO|2
+Indatha Triome|IKO|2
+Ketria Triome|IKO|2
+Raugrin Triome|IKO|2
+Savai Triome|IKO|2
+Zagoth Triome|IKO|2
+Drannith Magistrate|IKO|2
+Lavabrink Venturer|IKO|2
+Luminous Broodmoth|IKO|2
+Mythos of Snapdax|IKO|2
+Mythos of Illuna|IKO|2
+Shark Typhoon|IKO|2
+Voracious Greatshark|IKO|2
+Extinction Event|IKO|2
+Hunted Nightmare|IKO|2
+Mythos of Nethroi|IKO|2
+Mythos of Vadrok|IKO|2
+Unpredictable Cyclone|IKO|2
+Yidaro, Wandering Monster|IKO|2
+Colossification|IKO|2
+Kogla, the Titan Ape|IKO|2
+Mythos of Brokkos|IKO|2
+Chevill, Bane of Monsters|IKO|2
+Death's Oasis|IKO|2
+Eerie Ultimatum|IKO|2
+Emergent Ultimatum|IKO|2
+Frondland Felidar|IKO|2
+General Kudro of Drannith|IKO|2
+Genesis Ultimatum|IKO|2
+Inspired Ultimatum|IKO|2
+Kinnan, Bonder Prodigy|IKO|2
+Labyrinth Raptor|IKO|2
+Offspring's Revenge|IKO|2
+Quartzwood Crasher|IKO|2
+Rielle, the Everwise|IKO|2
+Ruinous Ultimatum|IKO|2
+Skycat Sovereign|IKO|2
+Slitherwisp|IKO|2
+Song of Creation|IKO|2
+Titans' Nest|IKO|2
+Whirlwind of Thought|IKO|2
+Winota, Joiner of Forces|IKO|2
+Fiend Artisan|IKO|2
+Gyruda, Doom of Depths|IKO|2
+Jegantha, the Wellspring|IKO|2
+Kaheera, the Orphanguard|IKO|2
+Keruga, the Macrosage|IKO|2
+Lurrus of the Dream-Den|IKO|2
+Lutri, the Spellchaser|IKO|2
+Obosh, the Preypiercer|IKO|2
+Umori, the Collector|IKO|2
+Yorion, Sky Nomad|IKO|2
+Zirda, the Dawnwaker|IKO|2
+Crystalline Giant|IKO|2
+The Ozolith|IKO|2
+Bonders' Enclave|IKO|2
+Colossification|IKO|3
+Flourishing Fox|IKO|2
+Heartless Act|IKO|2
+Forbidden Friendship|IKO|2
+Migration Path|IKO|2
+Sprite Dragon|IKO|2
+Huntmaster Liger|IKO|2
+Luminous Broodmoth|IKO|3
+Pollywog Symbiote|IKO|2
+Void Beckoner|IKO|2
+Everquill Phoenix|IKO|3
+Yidaro, Wandering Monster|IKO|3
+Gemrazer|IKO|3
+Titanoth Rex|IKO|2
+Brokkos, Apex of Forever|IKO|3
+Illuna, Apex of Wishes|IKO|3
+Nethroi, Apex of Death|IKO|3
+Snapdax, Apex of the Hunt|IKO|3
+Sprite Dragon|IKO|3
+Vadrok, Apex of Thunder|IKO|3
+Gyruda, Doom of Depths|IKO|3
+Mysterious Egg|IKO|2
+Dirge Bat|IKO|3
+Crystalline Giant|IKO|3
diff --git a/forge-gui/res/cardsfolder/a/akoum_hellkite.txt b/forge-gui/res/cardsfolder/a/akoum_hellkite.txt
index d887365b3ba..e2fc4cd5a10 100644
--- a/forge-gui/res/cardsfolder/a/akoum_hellkite.txt
+++ b/forge-gui/res/cardsfolder/a/akoum_hellkite.txt
@@ -6,5 +6,5 @@ K:Flying
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigDamage | TriggerDescription$ Landfall — Whenever a land enters the battlefield under your control, CARDNAME deals 1 damage to any target. If that land was a mountain, CARDNAME deals 2 damage to that permanent or player instead.
SVar:TrigDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | TgtPrompt$ Select any target | NumDmg$ X | References$ X
SVar:X:TriggeredCard$Valid Mountain/Plus.1
-SVar:Picture:http://www.wizards.com/global/images/magic/general/akoum_hellkite.jpg
+SVar:BuffedBy:Land
Oracle:Flying\nLandfall — Whenever a land enters the battlefield under your control, Akoum Hellkite deals 1 damage to any target. If that land is a Mountain, Akoum Hellkite deals 2 damage to that permanent or player instead.
\ No newline at end of file
diff --git a/forge-gui/res/cardsfolder/a/aretopolis.txt b/forge-gui/res/cardsfolder/a/aretopolis.txt
index e526ad01e69..b16967af1df 100644
--- a/forge-gui/res/cardsfolder/a/aretopolis.txt
+++ b/forge-gui/res/cardsfolder/a/aretopolis.txt
@@ -1,7 +1,7 @@
Name:Aretopolis
ManaCost:no cost
Types:Plane Kephalai
-T:Mode$ PlaneswalkedTo | ValidCard$ Plane.Self | TriggerZones$ Command | Execute$ AcquireScrolls | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, put a scroll counter on CARDNAME, then you gain life equal to the number of scroll counters on it.
+T:Mode$ PlaneswalkedTo | ValidCard$ Plane.Self | Execute$ AcquireScrolls | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, put a scroll counter on CARDNAME, then you gain life equal to the number of scroll counters on it.
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ AcquireScrolls | TriggerZones$ Command | Secondary$ True | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, put a scroll counter on CARDNAME, then you gain life equal to the number of scroll counters on it.
SVar:AcquireScrolls:DB$ PutCounter | Defined$ Self | CounterType$ SCROLL | CounterNum$ 1 | SubAbility$ ScrollsOfLife
SVar:ScrollsOfLife:DB$ GainLife | Defined$ You | LifeAmount$ NumScrolls | References$ NumScrolls
diff --git a/forge-gui/res/cardsfolder/b/ballot_broker.txt b/forge-gui/res/cardsfolder/b/ballot_broker.txt
index 23086c0407f..ea6110e74ce 100644
--- a/forge-gui/res/cardsfolder/b/ballot_broker.txt
+++ b/forge-gui/res/cardsfolder/b/ballot_broker.txt
@@ -2,5 +2,5 @@ Name:Ballot Broker
ManaCost:2 W
Types:Creature Human Advisor
PT:2/3
-S:Mode$ Continuous | Affected$ You | AddKeyword$ You may vote an additional time. | Description$ While voting, you may vote an additional time. (The votes can be for different choices or for the same choice.)
-Oracle:While voting, you may vote an additional time. (The votes can be for different choices or for the same choice.)
\ No newline at end of file
+S:Mode$ Continuous | Affected$ You | AdditionalOptionalVote$ 1 | Description$ While voting, you may vote an additional time. (The votes can be for different choices or for the same choice.)
+Oracle:While voting, you may vote an additional time. (The votes can be for different choices or for the same choice.)
diff --git a/forge-gui/res/cardsfolder/b/battering_krasis.txt b/forge-gui/res/cardsfolder/b/battering_krasis.txt
index b4c785520c7..e55f117b7a4 100644
--- a/forge-gui/res/cardsfolder/b/battering_krasis.txt
+++ b/forge-gui/res/cardsfolder/b/battering_krasis.txt
@@ -1,6 +1,6 @@
Name:Battering Krasis
ManaCost:2 G
-Types:Creature Fish Beast
+Types:Creature Shark Beast
PT:2/1
K:Evolve
K:Trample
diff --git a/forge-gui/res/cardsfolder/b/biomancers_familiar.txt b/forge-gui/res/cardsfolder/b/biomancers_familiar.txt
index 416715d855c..29492aae077 100644
--- a/forge-gui/res/cardsfolder/b/biomancers_familiar.txt
+++ b/forge-gui/res/cardsfolder/b/biomancers_familiar.txt
@@ -2,7 +2,7 @@ Name:Biomancer's Familiar
ManaCost:G U
Types:Creature Mutant
PT:2/2
-S:Mode$ ReduceCost | ValidCard$ Creature.YouCtrl | Type$ Ability | Amount$ 2 | MinMana$ 1 | AffectedZone$ Battlefield | Description$ Activated abilities of creatures you control cost {2} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana.
+S:Mode$ ReduceCost | ValidCard$ Creature.YouCtrl | Type$ Ability | Amount$ 2 | MinMana$ 1 | AffectedZone$ Battlefield | Description$ Activated abilities of creatures you control cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.
A:AB$ Pump | Cost$ T | ValidTgts$ Creature | KW$ HIDDEN CARDNAME adapts as though it had no +1/+1 counters | TgtPrompt$ Select target creature. | StackDescription$ SpellDescription | SpellDescription$ The next time target creature adapts this turn, it adapts as though it had no +1/+1 counters.
DeckHints:Keyword$Adapt
-Oracle:Activated abilities of creatures you control cost 2 less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana.\n{T}: The next time target creature adapts this turn, it adapts as though it had no +1/+1 counters.
+Oracle:Activated abilities of creatures you control cost 2 less to activate. This effect can't reduce the mana in that cost to less than one mana.\n{T}: The next time target creature adapts this turn, it adapts as though it had no +1/+1 counters.
diff --git a/forge-gui/res/cardsfolder/b/bladeback_sliver.txt b/forge-gui/res/cardsfolder/b/bladeback_sliver.txt
index ee5d05eaf77..c6529e12cf5 100644
--- a/forge-gui/res/cardsfolder/b/bladeback_sliver.txt
+++ b/forge-gui/res/cardsfolder/b/bladeback_sliver.txt
@@ -2,7 +2,7 @@ Name:Bladeback Sliver
ManaCost:1 R
Types:Creature Sliver
PT:2/2
-S:Mode$ Continuous | Affected$ Creature.Sliver+YouCtrl | AddAbility$ DBDamage | Condition$ Hellbent | Description$ Hellbent As long as you have no cards in hand, Sliver creatures you control have "{T}: This creature deals 1 damage to target player or planeswalker.
+S:Mode$ Continuous | Affected$ Creature.Sliver+YouCtrl | AddAbility$ DBDamage | Condition$ Hellbent | Description$ Hellbent - As long as you have no cards in hand, Sliver creatures you control have "{T}: This creature deals 1 damage to target player or planeswalker.
SVar:DBDamage:AB$ DealDamage | Cost$ T | ValidTgts$ Player,Planeswalker | TgtPrompt$ Select target player or planeswalker | NumDmg$ 1 | SpellDescription$ This creature deals 1 damage to target player or planeswalker.
SVar:PlayMain1:TRUE
Oracle:Hellbent — As long as you have no cards in hand, Sliver creatures you control have "{T}: This creature deals 1 damage to target player or planeswalker."
diff --git a/forge-gui/res/cardsfolder/b/bragos_representative.txt b/forge-gui/res/cardsfolder/b/bragos_representative.txt
index 40fedfb2763..12ff6add8d1 100644
--- a/forge-gui/res/cardsfolder/b/bragos_representative.txt
+++ b/forge-gui/res/cardsfolder/b/bragos_representative.txt
@@ -2,6 +2,6 @@ Name:Brago's Representative
ManaCost:2 W
Types:Creature Human Advisor
PT:1/4
-S:Mode$ Continuous | Affected$ You | AddKeyword$ You get an additional vote. | Description$ While voting, you get an additional vote. (The votes can be for different choices or for the same choice.)
+S:Mode$ Continuous | Affected$ You | AdditionalVote$ 1 | Description$ While voting, you get an additional vote. (The votes can be for different choices or for the same choice.)
SVar:Picture:http://www.wizards.com/global/images/magic/general/bragos_representative.jpg
Oracle:While voting, you get an additional vote. (The votes can be for different choices or for the same choice.)
diff --git a/forge-gui/res/cardsfolder/c/cavalier_of_dawn.txt b/forge-gui/res/cardsfolder/c/cavalier_of_dawn.txt
index 513fd7f037c..601d7d96ee4 100644
--- a/forge-gui/res/cardsfolder/c/cavalier_of_dawn.txt
+++ b/forge-gui/res/cardsfolder/c/cavalier_of_dawn.txt
@@ -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.
diff --git a/forge-gui/res/cardsfolder/c/chaotic_aether.txt b/forge-gui/res/cardsfolder/c/chaotic_aether.txt
index da6ff6d34c7..8af06a34c6f 100644
--- a/forge-gui/res/cardsfolder/c/chaotic_aether.txt
+++ b/forge-gui/res/cardsfolder/c/chaotic_aether.txt
@@ -1,7 +1,7 @@
Name:Chaotic Aether
ManaCost:no cost
Types:Phenomenon
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ Aether | TriggerDescription$ When you encounter CARDNAME, each blank roll of the planar dice is a {CHAOS} roll until a player planeswalks away from a plane. (Then planeswalk away from this phenomenon)
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ Aether | TriggerDescription$ When you encounter CARDNAME, each blank roll of the planar dice is a {CHAOS} roll until a player planeswalks away from a plane. (Then planeswalk away from this phenomenon)
SVar:Aether:DB$ Effect | Name$ Chaotic Aether Effect | StaticAbilities$ STBlankIsChaos | Triggers$ TPWAway | SVars$ ExileSelf | SubAbility$ PWAway
SVar:PWAway:DB$ Planeswalk | Cost$ 0
SVar:STBlankIsChaos:Mode$ Continuous | EffectZone$ Command | GlobalRule$ Each blank roll of the planar dice is a {CHAOS} roll.
diff --git a/forge-gui/res/cardsfolder/d/dismantle.txt b/forge-gui/res/cardsfolder/d/dismantle.txt
index c39d32dfd26..69a7d4e4fb4 100644
--- a/forge-gui/res/cardsfolder/d/dismantle.txt
+++ b/forge-gui/res/cardsfolder/d/dismantle.txt
@@ -1,13 +1,9 @@
Name:Dismantle
ManaCost:2 R
Types:Sorcery
-A:SP$ Destroy | Cost$ 2 R | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | RememberTargets$ True | SubAbility$ DBChoice | SpellDescription$ Destroy target artifact. If that artifact had counters on it, put that many +1/+1 counters or charge counters on an artifact you control.
-SVar:DBChoice:DB$ GenericChoice | Choices$ DBP1P1,DBCharge | ConditionDefined$ Targeted | ConditionPresent$ Card.HasCounters | ConditionCompare$ GE1 | StackDescription$ put that many +1/+1 counters or charge counters on an artifact you control.
-SVar:DBP1P1:DB$ ChooseCard | Choices$ Artifact.YouCtrl | Amount$ 1 | SpellDescription$ +1/+1 | SubAbility$ DBPutP1P1
-SVar:DBPutP1P1:DB$ PutCounter | Defined$ ChosenCard | CounterType$ P1P1 | CounterNum$ X | References$ X | SubAbility$ DBCleanup
-SVar:DBCharge:DB$ ChooseCard | Choices$ Artifact.YouCtrl | Amount$ 1 | SpellDescription$ charge | SubAbility$ DBPutCharge
-SVar:DBPutCharge:DB$ PutCounter | Defined$ ChosenCard | CounterType$ CHARGE | CounterNum$ X | References$ X | SpellDescription$ Charge | SubAbility$ DBCleanup
-SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
-SVar:X:RememberedLKI$CardCounters.ALL
-SVar:Picture:http://www.wizards.com/global/images/magic/general/dismantle.jpg
+A:SP$ Destroy | Cost$ 2 R | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | SubAbility$ DBChoice | SpellDescription$ Destroy target artifact. If that artifact had counters on it, put that many +1/+1 counters or charge counters on an artifact you control.
+SVar:DBChoice:DB$ GenericChoice | Choices$ DBPutP1P1,DBPutCharge | ConditionDefined$ Targeted | ConditionPresent$ Card.HasCounters | ConditionCompare$ GE1 | StackDescription$ put that many +1/+1 counters or charge counters on an artifact you control.
+SVar:DBPutP1P1:DB$ PutCounter | Choices$ Artifact.YouCtrl | CounterType$ P1P1 | CounterNum$ X | References$ X | SpellDescription$ +1/+1
+SVar:DBPutCharge:DB$ PutCounter | Choices$ Artifact.YouCtrl | CounterType$ CHARGE | CounterNum$ X | References$ X | SpellDescription$ Charge
+SVar:X:TargetedLKI$CardCounters.ALL
Oracle:Destroy target artifact. If that artifact had counters on it, put that many +1/+1 counters or charge counters on an artifact you control.
diff --git a/forge-gui/res/cardsfolder/d/dissenters_delivery.txt b/forge-gui/res/cardsfolder/d/dissenters_deliverance.txt
similarity index 100%
rename from forge-gui/res/cardsfolder/d/dissenters_delivery.txt
rename to forge-gui/res/cardsfolder/d/dissenters_deliverance.txt
diff --git a/forge-gui/res/cardsfolder/d/dryad_of_the_ilysian_grove.txt b/forge-gui/res/cardsfolder/d/dryad_of_the_ilysian_grove.txt
index 05bbe8b8322..5d1455ea215 100755
--- a/forge-gui/res/cardsfolder/d/dryad_of_the_ilysian_grove.txt
+++ b/forge-gui/res/cardsfolder/d/dryad_of_the_ilysian_grove.txt
@@ -1,6 +1,6 @@
Name:Dryad of the Ilysian Grove
ManaCost:2 G
-Types:Enchantment Creature Nymph
+Types:Enchantment Creature Nymph Dryad
PT:2/4
S:Mode$ Continuous | Affected$ You | AdjustLandPlays$ 1 | Description$ You may play an additional land on each of your turns.
S:Mode$ Continuous | Affected$ Land.YouCtrl | AddType$ AllBasicLandType | Description$ Lands you control are every basic land type in addition to their other types.
diff --git a/forge-gui/res/cardsfolder/e/emeria_shepherd.txt b/forge-gui/res/cardsfolder/e/emeria_shepherd.txt
index 89129e43ec4..f32ecec2d36 100644
--- a/forge-gui/res/cardsfolder/e/emeria_shepherd.txt
+++ b/forge-gui/res/cardsfolder/e/emeria_shepherd.txt
@@ -3,11 +3,8 @@ ManaCost:5 W W
Types:Creature Angel
PT:4/4
K:Flying
-T:Mode$ ChangesZone | TriggerZones$ Battlefield | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl+HasSubtype Plains | Execute$ DBChoose | OptionalDecider$ You | TriggerDescription$ Landfall — Whenever a land enters the battlefield, you may return target nonland permanent card from your graveyard to your hand. If that land is a Plains, you may return that nonland permanent card to the battlefield instead.
-T:Mode$ ChangesZone | TriggerZones$ Battlefield | Secondary$ True | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl+HasNoSubtype Plains | Execute$ TrigChangeHand | OptionalDecider$ You | TriggerDescription$ Landfall — Whenever a land enters the battlefield, you may return target nonland permanent card from your graveyard to your hand.
-SVar:DBChoose:DB$ ChooseCard | Defined$ You | Choices$ Permanent.YouCtrl+nonLand | ChoiceZone$ Graveyard | SubAbility$ TrigChangeHand2
-SVar:TrigChangeHand:DB$ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Permanent.YouCtrl+nonLand | Defined$ ChosenCard
-SVar:TrigChangeHand2:DB$ChangeZone | Origin$ Graveyard | Destination$ Battlefield | DestinationAlternative$ Hand | AlternativeDestinationMessage$ Would you like to return this permanent to the battlefield (and not to the hand)? | Defined$ ChosenCard | ConditionCheckSVar$ NumTargets | ConditionSVarCompare$ GE1 | References$ NumTargets
-SVar:NumTargets:Count$ValidGraveyard Permanent.YouCtrl+nonLand
-SVar:Picture:http://www.wizards.com/global/images/magic/general/emeria_shepherd.jpg
+T:Mode$ ChangesZone | TriggerZones$ Battlefield | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | Execute$ TrigChangeHand | OptionalDecider$ You | TriggerDescription$ Landfall — Whenever a land enters the battlefield under your control, you may return target nonland permanent card from your graveyard to your hand. If that land is a Plains, you may return that nonland permanent card to the battlefield instead.
+SVar:TrigChangeHand:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TgtPrompt$ Select target nonland permanent | ValidTgts$ Permanent.YouCtrl+nonLand | ConditionDefined$ TriggeredCard | ConditionPresent$ Card.Plains | ConditionCompare$ EQ0 | SubAbility$ TrigChangeBattlefield
+SVar:TrigChangeBattlefield:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | DestinationAlternative$ Hand | AlternativeDestinationMessage$ Would you like to return this permanent to the battlefield (and not to your hand)? | Defined$ Targeted | ConditionDefined$ TriggeredCard | ConditionPresent$ Card.Plains | ConditionCompare$ EQ1
+SVar:BuffedBy:Land
Oracle:Flying\nLandfall — Whenever a land enters the battlefield under your control, you may return target nonland permanent card from your graveyard to your hand. If that land is a Plains, you may return that nonland permanent card to the battlefield instead.
diff --git a/forge-gui/res/cardsfolder/e/estrids_invocation.txt b/forge-gui/res/cardsfolder/e/estrids_invocation.txt
index 413f2310d90..bb327773c7a 100644
--- a/forge-gui/res/cardsfolder/e/estrids_invocation.txt
+++ b/forge-gui/res/cardsfolder/e/estrids_invocation.txt
@@ -2,8 +2,8 @@ Name:Estrid's Invocation
ManaCost:2 U
Types:Enchantment
K:ETBReplacement:Copy:DBCopy:Optional
-SVar:DBCopy:DB$ Clone | Choices$ Enchantment.Other+YouCtrl | AILogic$ AtLeast1 | AddTriggers$ InvocationExileUpkeep | AddSVars$ InvocationDBReturn,InvocationTrigExile,InvocationExileUpkeep | SpellDescription$ You may have CARDNAME enter the battlefield as a copy of any enchantment you control, except it gains "At the beginning of your upkeep, you may exile this enchantment. If you do, return it to the battlefield under its owner's control."
+SVar:DBCopy:DB$ Clone | Choices$ Enchantment.Other+YouCtrl | AILogic$ AtLeast1 | AddTriggers$ InvocationExileUpkeep | AddSVars$ InvocationDBReturn,InvocationTrigExile,InvocationExileUpkeep | SpellDescription$ You may have CARDNAME enter the battlefield as a copy of a enchantment you control, except it gains "At the beginning of your upkeep, you may exile this enchantment. If you do, return it to the battlefield under its owner's control."
SVar:InvocationExileUpkeep:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ InvocationTrigExile | OptionalDecider$ You | IsPresent$ Card.Self | TriggerDescription$ At the beginning of your upkeep you may exile this enchantment. If you do, return it to the battlefield under it's owner's control.
SVar:InvocationTrigExile:DB$ ChangeZone | Defined$ Self | Origin$ Battlefield | Destination$ Exile | SubAbility$ InvocationDBReturn
SVar:InvocationDBReturn:DB$ ChangeZone | Defined$ CorrectedSelf | Origin$ All | Destination$ Battlefield | GainControl$ True
-Oracle:You may have Estrid's Invocation enter the battlefield as a copy of any enchantment you control, except it gains "At the beginning of your upkeep, you may exile this enchantment. If you do, return it to the battlefield under its owner's control."
+Oracle:You may have Estrid's Invocation enter the battlefield as a copy of a enchantment you control, except it gains "At the beginning of your upkeep, you may exile this enchantment. If you do, return it to the battlefield under its owner's control."
diff --git a/forge-gui/res/cardsfolder/e/expropriate.txt b/forge-gui/res/cardsfolder/e/expropriate.txt
new file mode 100644
index 00000000000..3ced790ee5e
--- /dev/null
+++ b/forge-gui/res/cardsfolder/e/expropriate.txt
@@ -0,0 +1,9 @@
+Name:Expropriate
+ManaCost:7 U U
+Types:Sorcery
+A:SP$ Vote | Cost$ 7 U U | Defined$ Player | VoteType$ Time,Money | VoteTime$ DBTime | VoteMoney$ DBMoney | EachVote$ True | SubAbility$ DBChange | SpellDescription$ Council’s dilemma — Starting with you, each player votes for time or money. For each time vote, take an extra turn after this one. For each money vote, choose a permanent owned by the voter and gain control of it. Exile Expropriate.
+SVar:DBTime:DB$ AddTurn | Defined$ You | NumTurns$ 1
+SVar:DBMoney:DB$ ChooseCard | Defined$ You | Choices$ Permanent.RememberedPlayerOwn | SubAbility$ DBControl
+SVar:DBControl:DB$ GainControl | Defined$ ChosenCard | NewController$ You
+SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile | StackDescription$ None
+Oracle:Council’s dilemma — Starting with you, each player votes for time or money. For each time vote, take an extra turn after this one. For each money vote, choose a permanent owned by the voter and gain control of it. Exile Expropriate.
diff --git a/forge-gui/res/cardsfolder/e/eye_of_doom.txt b/forge-gui/res/cardsfolder/e/eye_of_doom.txt
index 9a002bde0c2..43abb2b5a3a 100644
--- a/forge-gui/res/cardsfolder/e/eye_of_doom.txt
+++ b/forge-gui/res/cardsfolder/e/eye_of_doom.txt
@@ -2,9 +2,8 @@ Name:Eye of Doom
ManaCost:4
Types:Artifact
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigChoose | TriggerDescription$ When CARDNAME enters the battlefield, each player chooses a nonland permanent and puts a doom counter on it.
-SVar:TrigChoose:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChoose
-SVar:DBChoose:DB$ ChooseCard | Defined$ Player.IsRemembered | Choices$ Permanent.nonLand | AILogic$ OppPreferred | Mandatory$ True | SubAbility$ DBPutCounter
-SVar:DBPutCounter:DB$ PutCounter | Defined$ ChosenCard | CounterType$ DOOM | CounterNum$ 1
+SVar:TrigChoose:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBPutCounter
+SVar:DBPutCounter:DB$ PutCounter | Chooser$ Remembered | Choices$ Permanent.nonLand | CounterType$ DOOM | CounterNum$ 1
A:AB$ DestroyAll | Cost$ 2 T Sac<1/CARDNAME> | ValidCards$ Permanent.counters_GE1_DOOM | SpellDescription$ Destroy each permanent with a doom counter.
AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/eye_of_doom.jpg
diff --git a/forge-gui/res/cardsfolder/f/faerie_artisans.txt b/forge-gui/res/cardsfolder/f/faerie_artisans.txt
index 79e22af4b84..99c4c246d6d 100644
--- a/forge-gui/res/cardsfolder/f/faerie_artisans.txt
+++ b/forge-gui/res/cardsfolder/f/faerie_artisans.txt
@@ -3,10 +3,9 @@ ManaCost:3 U
Types:Creature Faerie Artificer
PT:2/2
K:Flying
-T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.nonToken+OppCtrl | TriggerZones$ Battlefield | Execute$ TrigImprint | TriggerDescription$Whenever a nontoken creature enters the battlefield under an opponent's control, create a token that's a copy of that creature except that it's an artifact in addition to its other types. Then exile all other tokens created with Faerie Artisans.
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.nonToken+OppCtrl | TriggerZones$ Battlefield | Execute$ TrigImprint | TriggerDescription$ Whenever a nontoken creature enters the battlefield under an opponent's control, create a token that's a copy of that creature except that it's an artifact in addition to its other types. Then exile all other tokens created with CARDNAME.
SVar:TrigImprint:DB$ Pump | ImprintCards$ Remembered | SubAbility$ DBCopy
SVar:DBCopy:DB$ CopyPermanent | Defined$ TriggeredCard | Controller$ You | AddTypes$ Artifact | RememberCopied$ True | SubAbility$ DBChangeZoneAll
SVar:DBChangeZoneAll:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ Card.IsImprinted
DeckHas:Ability$Token
-SVar:Picture:http://www.wizards.com/global/images/magic/general/faerie_artisans.jpg
Oracle:Flying\nWhenever a nontoken creature enters the battlefield under an opponent's control, create a token that's a copy of that creature except that it's an artifact in addition to its other types. Then exile all other tokens created with Faerie Artisans.
diff --git a/forge-gui/res/cardsfolder/f/fervent_champion.txt b/forge-gui/res/cardsfolder/f/fervent_champion.txt
index 641b64e0b5d..72f720c033d 100644
--- a/forge-gui/res/cardsfolder/f/fervent_champion.txt
+++ b/forge-gui/res/cardsfolder/f/fervent_champion.txt
@@ -4,8 +4,8 @@ Types:Creature Human Knight
PT:1/1
K:First Strike
K:Haste
-T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks, another target creature you control gets +1/+0 until end of turn.
-SVar:TrigPump:DB$Pump | ValidTgts$ Knight.YouCtrl+Other | TgtPrompt$ Select another target attacking Knight you control | NumAtt$ +1
+T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks, another target attacking Knight you control gets +1/+0 until end of turn.
+SVar:TrigPump:DB$ Pump | ValidTgts$ Knight.YouCtrl+attacking+Other | TgtPrompt$ Select another target attacking Knight you control | NumAtt$ +1
SVar:HasAttackEffect:TRUE
S:Mode$ ReduceCost | ValidTarget$ Card.Self | ValidSpell$ Activated.Equip | Activator$ You | Amount$ 3 | Description$ Equip abilities you activate that target CARDNAME cost {3} less to activate.
Oracle:First strike, haste\nWhenever Fervent Champion attacks, another target attacking Knight you control gets +1/+0 until end of turn.\nEquip abilities you activate that target Fervent Champion cost {3} less to activate.
diff --git a/forge-gui/res/cardsfolder/f/find_finality.txt b/forge-gui/res/cardsfolder/f/find_finality.txt
index 2b85d2313fd..a4c41981640 100644
--- a/forge-gui/res/cardsfolder/f/find_finality.txt
+++ b/forge-gui/res/cardsfolder/f/find_finality.txt
@@ -10,9 +10,7 @@ ALTERNATE
Name:Finality
ManaCost:4 B G
Types:Sorcery
-A:SP$ ChooseCard | Cost$ 4 B G | Defined$ You | Amount$ 1 | MinAmount$ 0 | Choices$ Creature.YouCtrl | SubAbility$ DBPutCounter | SpellDescription$ You may put two +1/+1 counters on a creature you control. Then all creatures get -4/-4 until end of turn.
-SVar:DBPutCounter:DB$ PutCounter | Defined$ ChosenCard | CounterType$ P1P1 | CounterNum$ 2 | SubAbility$ DBPumpAll
-SVar:DBPumpAll:DB$ PumpAll | ValidCards$ Creature | NumAtt$ -4 | NumDef$ -4 | IsCurse$ True | SubAbility$ DBCleanup
-SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
+A:SP$ PutCounter | Cost$ 4 B G | Choices$ Creature.YouCtrl | ChoiceOptional$ True | CounterType$ P1P1 | CounterNum$ 2 | SubAbility$ DBPumpAll | StackDescription$ SpellDescription | SpellDescription$ You may put two +1/+1 counters on a creature you control. Then all creatures get -4/-4 until end of turn.
+SVar:DBPumpAll:DB$ PumpAll | ValidCards$ Creature | NumAtt$ -4 | NumDef$ -4 | IsCurse$ True
DeckHas:Ability$Counters
Oracle:You may put two +1/+1 counters on a creature you control. Then all creatures get -4/-4 until end of turn.
diff --git a/forge-gui/res/cardsfolder/f/furnace_layer.txt b/forge-gui/res/cardsfolder/f/furnace_layer.txt
index 382e81d55bf..4890adfa936 100644
--- a/forge-gui/res/cardsfolder/f/furnace_layer.txt
+++ b/forge-gui/res/cardsfolder/f/furnace_layer.txt
@@ -1,7 +1,7 @@
Name:Furnace Layer
ManaCost:no cost
Types:Plane New Phyrexia
-T:Mode$ PlaneswalkedTo | ValidCard$ Plane.Self | TriggerZones$ Command | Execute$ FurnaceDiscard | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, select target player at random. That player discards a card. If that player discards a land card this way, they lose 3 life.
+T:Mode$ PlaneswalkedTo | ValidCard$ Plane.Self | Execute$ FurnaceDiscard | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, select target player at random. That player discards a card. If that player discards a land card this way, they lose 3 life.
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ FurnaceDiscard | TriggerZones$ Command | Secondary$ True | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, select target player at random. That player discards a card. If that player discards a land card this way, they lose 3 life.
SVar:FurnaceDiscard:DB$ Discard | ValidTgts$ Player | TargetsAtRandom$ True | NumCards$ 1 | Mode$ TgtChoose | RememberDiscarded$ True | SubAbility$ DBLoseLife
SVar:DBLoseLife:DB$ LoseLife | Defined$ Targeted | LifeAmount$ 3 | ConditionDefined$ Remembered | ConditionPresent$ Land | ConditionCompare$ GE1 | SubAbility$ DBCleanup
diff --git a/forge-gui/res/cardsfolder/g/ghastly_demise.txt b/forge-gui/res/cardsfolder/g/ghastly_demise.txt
index 78ced7746b3..f04b0f2ac15 100644
--- a/forge-gui/res/cardsfolder/g/ghastly_demise.txt
+++ b/forge-gui/res/cardsfolder/g/ghastly_demise.txt
@@ -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.
diff --git a/forge-gui/res/cardsfolder/g/giant_shark.txt b/forge-gui/res/cardsfolder/g/giant_shark.txt
index 0046ca10f0c..d076b021629 100644
--- a/forge-gui/res/cardsfolder/g/giant_shark.txt
+++ b/forge-gui/res/cardsfolder/g/giant_shark.txt
@@ -1,6 +1,6 @@
Name:Giant Shark
ManaCost:5 U
-Types:Creature Fish
+Types:Creature Shark
PT:4/4
T:Mode$ AttackerBlockedByCreature | ValidCard$ Creature.wasDealtDamageThisTurn | ValidBlocker$ Card.Self | Execute$ TrigPumpShark | TriggerDescription$ Whenever CARDNAME blocks or becomes blocked by a creature that has been dealt damage this turn, CARDNAME gets +2/+0 and gains trample until end of turn.
T:Mode$ AttackerBlockedByCreature | ValidCard$ Card.Self | ValidBlocker$ Creature.wasDealtDamageThisTurn | Execute$ TrigPumpShark | Secondary$ True | TriggerDescription$ Whenever CARDNAME blocks or becomes blocked by a creature that has been dealt damage this turn, CARDNAME gets +2/+0 and gains trample until end of turn.
diff --git a/forge-gui/res/cardsfolder/g/glyph_of_delusion.txt b/forge-gui/res/cardsfolder/g/glyph_of_delusion.txt
new file mode 100644
index 00000000000..090f3b5a546
--- /dev/null
+++ b/forge-gui/res/cardsfolder/g/glyph_of_delusion.txt
@@ -0,0 +1,11 @@
+Name:Glyph of Delusion
+ManaCost:U
+Types:Instant
+A:SP$ Pump | Cost$ U | ValidTgts$ Wall.blockedThisTurn | TgtPrompt$ Select target Wall that blocked this turn | SubAbility$ DBPutCounter | StackDescription$ SpellDescription | SpellDescription$ Put X glyph counters on target creature that target Wall blocked this turn, where X is the power of that blocked creature. The creature gains “This creature doesn’t untap during your untap step if it has a glyph counter on it” and “At the beginning of your upkeep, remove a glyph counter from this creature.”
+SVar:DBPutCounter:DB$ PutCounter | CounterType$ GLYPH | CounterNum$ X | References$ X | ValidTgts$ Creature.blockedByValidThisTurn ParentTarget | TgtPrompt$ Select target creature blocked by target Wall this turn to put counters on | SubAbility$ Delude | IsCurse$ True
+SVar:X:Targeted$CardPower
+SVar:Delude:DB$ Animate | Defined$ ParentTarget | staticAbilities$ Delusional | Triggers$ TrigGlyphUpkeep | sVars$ LoseGlyph | Permanent$ True | StackDescription$ None
+SVar:Delusional:Mode$ Continuous | Affected$ Card.Self+counters_GE1_GLYPH | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ CARDNAME doesn't untap during your untap step if it has a glyph counter on it.
+SVar:TrigGlyphUpkeep:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ LoseGlyph | TriggerDescription$ At the beginning of your upkeep, remove a glyph counter from CARDNAME.
+SVar:LoseGlyph:DB$ RemoveCounter | CounterType$ GLYPH | CounterNum$ 1
+Oracle:Put X glyph counters on target creature that target Wall blocked this turn, where X is the power of that blocked creature. The creature gains “This creature doesn’t untap during your untap step if it has a glyph counter on it” and “At the beginning of your upkeep, remove a glyph counter from this creature.”
diff --git a/forge-gui/res/cardsfolder/g/glyph_of_reincarnation.txt b/forge-gui/res/cardsfolder/g/glyph_of_reincarnation.txt
new file mode 100644
index 00000000000..09fe0e77f23
--- /dev/null
+++ b/forge-gui/res/cardsfolder/g/glyph_of_reincarnation.txt
@@ -0,0 +1,10 @@
+Name:Glyph of Reincarnation
+ManaCost:G
+Types:Instant
+A:SP$ Pump | Cost$ G | ValidTgts$ Wall | TgtPrompt$ Select target Wall | AILogic$ Pump | ActivationPhases$ Main2->End of Turn | SubAbility$ DBDestroyAll | StackDescription$ SpellDescription | SpellDescription$ Cast this spell only after combat.
+SVar:DBDestroyAll:DB$ DestroyAll | ValidCards$ Creature.blockedByValidThisTurn ParentTarget | NoRegen$ True | RememberDestroyed$ True | SubAbility$ DBChoose | StackDescription$ SpellDescription | SpellDescription$ Destroy all creatures that were blocked by target Wall this turn. They can’t be regenerated.
+SVar:DBChoose:DB$ ChooseCard | Defined$ You | Choices$ Creature.OwnedBy Player.Active | ChoiceTitle$ Choose creatures to put on the battlefield | ChoiceZone$ Graveyard | Amount$ X | References$ X | Mandatory$ True | AILogic$ WorstCard | SubAbility$ DBChangeZone | StackDescription$ SpellDescription | SpellDescription$ For each creature that died this way, put a creature card from the graveyard of the player who controlled that creature the last time it became blocked by that Wall onto the battlefield under its owner’s control.
+SVar:DBChangeZone:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Graveyard | Destination$ Battlefield | SubAbility$ DBCleanup
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
+SVar:X:Count$ValidGraveyard Creature.IsRemembered
+Oracle:Cast this spell only after combat.\nDestroy all creatures that were blocked by target Wall this turn. They can’t be regenerated. For each creature that died this way, put a creature card from the graveyard of the player who controlled that creature the last time it became blocked by that Wall onto the battlefield under its owner’s control.
diff --git a/forge-gui/res/cardsfolder/g/grove_of_the_dreampods.txt b/forge-gui/res/cardsfolder/g/grove_of_the_dreampods.txt
index 45845e4e53b..e3d7670e6cc 100644
--- a/forge-gui/res/cardsfolder/g/grove_of_the_dreampods.txt
+++ b/forge-gui/res/cardsfolder/g/grove_of_the_dreampods.txt
@@ -1,7 +1,7 @@
Name:Grove of the Dreampods
ManaCost:no cost
Types:Plane Fabacin
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ DreampodsDig | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield and the rest on the bottom of your library in a random order.
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ DreampodsDig | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield and the rest on the bottom of your library in a random order.
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ DreampodsDig | TriggerZones$ Command | Secondary$ True | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield and the rest on the bottom of your library in a random order.
SVar:DreampodsDig:DB$ DigUntil | Valid$ Creature | ValidDescription$ creature | FoundDestination$ Battlefield | RevealedDestination$ Library | RevealedLibraryPosition$ -1 | RevealRandomOrder$ True
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, return target creature card from your graveyard to the battlefield.
diff --git a/forge-gui/res/cardsfolder/g/guul_draz_overseer.txt b/forge-gui/res/cardsfolder/g/guul_draz_overseer.txt
index 224a6c64e01..50e6d91c9eb 100644
--- a/forge-gui/res/cardsfolder/g/guul_draz_overseer.txt
+++ b/forge-gui/res/cardsfolder/g/guul_draz_overseer.txt
@@ -3,9 +3,8 @@ ManaCost:4 B B
Types:Creature Vampire
PT:3/4
K:Flying
-T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl+HasSubtype Swamp | TriggerZones$ Battlefield | Execute$ TrigPumpAll2 | TriggerDescription$ Landfall — Whenever a land enters the battlefield under your control, other creatures you control get +1/+0 until end of turn. If that land is a Swamp, those creatures get +2/+0 until end of turn instead.
-T:Mode$ ChangesZone | Secondary$ True | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl+HasNoSubtype Swamp | TriggerZones$ Battlefield | Execute$ TrigPumpAll | TriggerDescription$ Landfall — Whenever a land enters the battlefield, other creatures you control get +1/+0 until end of turn.
-SVar:TrigPumpAll:DB$PumpAll | ValidCards$ Creature.Other+YouCtrl | NumAtt$ 1
-SVar:TrigPumpAll2:DB$PumpAll | ValidCards$ Creature.Other+YouCtrl | NumAtt$ 2
-SVar:Picture:http://www.wizards.com/global/images/magic/general/guul_draz_overseer.jpg
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPumpAll | TriggerDescription$ Landfall — Whenever a land enters the battlefield under your control, other creatures you control get +1/+0 until end of turn. If that land is a Swamp, those creatures get +2/+0 until end of turn instead.
+SVar:TrigPumpAll:DB$PumpAll | ValidCards$ Creature.Other+YouCtrl | NumAtt$ X | References$ X
+SVar:X:TriggeredCard$Valid Swamp/Plus.1
+SVar:BuffedBy:Land
Oracle:Flying\nLandfall — Whenever a land enters the battlefield under your control, other creatures you control get +1/+0 until end of turn. If that land is a Swamp, those creatures get +2/+0 until end of turn instead.
diff --git a/forge-gui/res/cardsfolder/h/hammerhead_shark.txt b/forge-gui/res/cardsfolder/h/hammerhead_shark.txt
index d8b04d8ce8e..66b7b00e4ad 100644
--- a/forge-gui/res/cardsfolder/h/hammerhead_shark.txt
+++ b/forge-gui/res/cardsfolder/h/hammerhead_shark.txt
@@ -1,6 +1,6 @@
Name:Hammerhead Shark
ManaCost:1 U
-Types:Creature Fish
+Types:Creature Shark
PT:2/3
S:Mode$ CantAttack | ValidCard$ Card.Self | UnlessDefenderControls$ Island | Description$ CARDNAME can't attack unless defending player controls an Island.
SVar:Picture:http://www.wizards.com/global/images/magic/general/hammerhead_shark.jpg
diff --git a/forge-gui/res/cardsfolder/h/haphazard_bombardment.txt b/forge-gui/res/cardsfolder/h/haphazard_bombardment.txt
index d598dd69a28..c2f8d46dc9f 100644
--- a/forge-gui/res/cardsfolder/h/haphazard_bombardment.txt
+++ b/forge-gui/res/cardsfolder/h/haphazard_bombardment.txt
@@ -1,12 +1,10 @@
Name:Haphazard Bombardment
ManaCost:5 R
Types:Enchantment
-T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChoose | TriggerDescription$ When CARDNAME enters the battlefield, choose four nonenchantment permanents you don't control and put an aim counter on each of them.
-SVar:TrigChoose:DB$ ChooseCard | Defined$ You | Amount$ 4 | Choices$ Permanent.YouDontCtrl+nonEnchantment | SubAbility$ DBPutCounter | AILogic$ AtLeast1 | Mandatory$ True
-SVar:DBPutCounter:DB$ PutCounter | Defined$ ChosenCard | CounterType$ AIM | CounterNum$ 1 | SubAbility$ DBCleanup
-SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ DBPutCounter | TriggerDescription$ When CARDNAME enters the battlefield, choose four nonenchantment permanents you don't control and put an aim counter on each of them.
+SVar:DBPutCounter:DB$ PutCounter | Choices$ Permanent.YouDontCtrl+nonEnchantment | ChoiceAmount$ 4 | Defined$ ChosenCard | CounterType$ AIM | CounterNum$ 1
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | IsPresent$ Permanent.YouDontCtrl+counters_GE1_AIM | PresentCompare$ GE2 | Execute$ TrigDestroy | TriggerDescription$ At the beginning of your end step, if two or more permanents you don't control have an aim counter on them, destroy one of those permanents at random.
SVar:TrigDestroy:DB$ ChooseCard | Amount$ 1 | AtRandom$ True | Choices$ Permanent.YouDontCtrl+counters_GE1_AIM | SubAbility$ DBDestroy
SVar:DBDestroy:DB$ Destroy | Defined$ ChosenCard | SubAbility$ DBCleanup
-SVar:Picture:http://www.wizards.com/global/images/magic/general/haphazard_bombardment.jpg
+SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
Oracle:When Haphazard Bombardment enters the battlefield, choose four nonenchantment permanents you don't control and put an aim counter on each of them.\nAt the beginning of your end step, if two or more permanents you don't control have an aim counter on them, destroy one of those permanents at random.
diff --git a/forge-gui/res/cardsfolder/h/heartstone.txt b/forge-gui/res/cardsfolder/h/heartstone.txt
index 7b96b93b248..00a577031d4 100644
--- a/forge-gui/res/cardsfolder/h/heartstone.txt
+++ b/forge-gui/res/cardsfolder/h/heartstone.txt
@@ -1,6 +1,5 @@
Name:Heartstone
ManaCost:3
Types:Artifact
-S:Mode$ ReduceCost | ValidCard$ Creature | Type$ Ability | Amount$ 1 | MinMana$ 1 | AffectedZone$ Battlefield | Description$ Activated abilities of creatures cost {1} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana.
-SVar:Picture:http://www.wizards.com/global/images/magic/general/heartstone.jpg
-Oracle:Activated abilities of creatures cost {1} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana.
+S:Mode$ ReduceCost | ValidCard$ Creature | Type$ Ability | Amount$ 1 | MinMana$ 1 | AffectedZone$ Battlefield | Description$ Activated abilities of creatures cost {1} less to activate. This effect can't reduce the mana in that cost to less than one mana.
+Oracle:Activated abilities of creatures cost {1} less to activate. This effect can't reduce the mana in that cost to less than one mana.
diff --git a/forge-gui/res/cardsfolder/h/hold_the_perimeter.txt b/forge-gui/res/cardsfolder/h/hold_the_perimeter.txt
new file mode 100644
index 00000000000..edb0ff07a43
--- /dev/null
+++ b/forge-gui/res/cardsfolder/h/hold_the_perimeter.txt
@@ -0,0 +1,11 @@
+Name:Hold the Perimeter
+ManaCost:no cost
+Types:Conspiracy
+Text:(Start the game with this conspiracy face up in the command zone.)
+T:Mode$ Phase | Phase$ Upkeep | CheckSVar$ X | SVarCompare$ EQ1 | References$ X | ValidPlayer$ You | Execute$ TrigToken1 | EffectZone$ Command | TriggerDescription$ At the beginning of your first upkeep, create a 1/2 white Soldier creature token with defender.
+SVar:X:Count$YourTurns
+SVar:TrigToken1:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_2_soldier_defender | TokenOwner$ You | LegacyImage$ w 1 2 soldier defender cn2
+T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player.Other+IsNotRemembered | Execute$ TrigToken2 | EffectZone$ Command | TriggerDescription$ At the beginning of each other player’s first upkeep, that player creates a 1/1 red Goblin creature token with “This creature can’t block.”
+SVar:TrigToken2:DB$ Token | TokenAmount$ 1 | TokenScript$ r_1_1_goblin_noblock | TokenOwner$ TriggeredPlayer | LegacyImage$ r 1 1 goblin noblock cn2 | SubAbility$ RememberPlayer
+SVar:RememberPlayer:DB$ Pump | RememberObjects$ TriggeredPlayer
+Oracle:(Start the game with this conspiracy face up in the command zone.)\nAt the beginning of your first upkeep, create a 1/2 white Soldier creature token with defender.\nAt the beginning of each other player’s first upkeep, that player creates a 1/1 red Goblin creature token with “This creature can’t block.”
diff --git a/forge-gui/res/cardsfolder/i/illusion_of_choice.txt b/forge-gui/res/cardsfolder/i/illusion_of_choice.txt
index 851503cf9a0..805ccb7b2eb 100644
--- a/forge-gui/res/cardsfolder/i/illusion_of_choice.txt
+++ b/forge-gui/res/cardsfolder/i/illusion_of_choice.txt
@@ -3,6 +3,6 @@ ManaCost:U
Types:Instant
A:SP$ Effect | Cost$ U | Name$ Illusion of Choice Effect | StaticAbilities$ STVoter | SubAbility$ DBDraw | SpellDescription$ You choose how each player votes this turn. Draw a card.
SVar:DBDraw:DB$ Draw | NumCards$ 1
-SVar:STVoter:Mode$ Continuous | EffectZone$ Command | Affected$ You | AddKeyword$ You choose how each player votes this turn. | Description$ You choose how each player votes this turn.
+SVar:STVoter:Mode$ Continuous | EffectZone$ Command | Affected$ You | ControlVote$ True | Description$ You choose how each player votes this turn.
AI:RemoveDeck:All
-Oracle:You choose how each player votes this turn. Draw a card.
\ No newline at end of file
+Oracle:You choose how each player votes this turn. Draw a card.
diff --git a/forge-gui/res/cardsfolder/i/interplanar_tunnel.txt b/forge-gui/res/cardsfolder/i/interplanar_tunnel.txt
index 6481777c956..b51e88b5131 100644
--- a/forge-gui/res/cardsfolder/i/interplanar_tunnel.txt
+++ b/forge-gui/res/cardsfolder/i/interplanar_tunnel.txt
@@ -1,7 +1,7 @@
Name:Interplanar Tunnel
ManaCost:no cost
Types:Phenomenon
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ TrigDig | TriggerDescription$ When you encounter CARDNAME, reveal cards from the top of your planar deck until you reveal five plane cards. Put a plane card from among them on top of your planar deck, then put the rest of the revealed cards on the bottom in a random order. (Then planeswalk away from this phenomenon.)
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ When you encounter CARDNAME, reveal cards from the top of your planar deck until you reveal five plane cards. Put a plane card from among them on top of your planar deck, then put the rest of the revealed cards on the bottom in a random order. (Then planeswalk away from this phenomenon.)
SVar:TrigDig:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | SourceZone$ PlanarDeck | DestinationZone$ PlanarDeck | DestinationZone2$ PlanarDeck | LibraryPosition$ 0 | ChangeValid$ Plane | RestRandomOrder$ True | SubAbility$ Replaneswalk
SVar:Replaneswalk:DB$ Planeswalk | Cost$ 0
Oracle:When you encounter Interplanar Tunnel, reveal cards from the top of your planar deck until you reveal five plane cards. Put a plane card from among them on top of your planar deck, then put the rest of the revealed cards on the bottom in a random order. (Then planeswalk away from this phenomenon.)
diff --git a/forge-gui/res/cardsfolder/j/joust.txt b/forge-gui/res/cardsfolder/j/joust.txt
index 9d4c5ba84f4..d68d4eeb6f6 100644
--- a/forge-gui/res/cardsfolder/j/joust.txt
+++ b/forge-gui/res/cardsfolder/j/joust.txt
@@ -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.)
diff --git a/forge-gui/res/cardsfolder/k/kilnspire_district.txt b/forge-gui/res/cardsfolder/k/kilnspire_district.txt
index 10968a2c2ee..4bb0df42f1c 100644
--- a/forge-gui/res/cardsfolder/k/kilnspire_district.txt
+++ b/forge-gui/res/cardsfolder/k/kilnspire_district.txt
@@ -1,7 +1,7 @@
Name:Kilnspire District
ManaCost:no cost
Types:Plane Ravnica
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ PutCounter | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your precombat main phase, put a charge counter on CARDNAME, then add {R} for each charge counter on it.
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ PutCounter | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your precombat main phase, put a charge counter on CARDNAME, then add {R} for each charge counter on it.
T:Mode$ Phase | PreCombatMain$ True | ValidPlayer$ You | TriggerZones$ Command | Execute$ PutCounter | Secondary$ True | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your precombat main phase, put a charge counter on CARDNAME, then add {R} for each charge counter on it.
SVar:PutCounter:DB$PutCounter | Defined$ Self | CounterType$ CHARGE | CounterNum$ 1 | SubAbility$ DBMana
SVar:DBMana:DB$ Mana | Produced$ R | Amount$ Y | References$ Y
diff --git a/forge-gui/res/cardsfolder/k/krovikan_vampire.txt b/forge-gui/res/cardsfolder/k/krovikan_vampire.txt
new file mode 100644
index 00000000000..de9fd12c992
--- /dev/null
+++ b/forge-gui/res/cardsfolder/k/krovikan_vampire.txt
@@ -0,0 +1,17 @@
+Name:Krovikan Vampire
+ManaCost:3 B B
+Types:Creature Vampire
+PT:3/3
+T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.DamagedBy | TriggerZones$ Battlefield | Execute$ TrigPump | Static$ True
+SVar:TrigPump:DB$ Pump | RememberObjects$ TriggeredCard
+T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Any | ValidCard$ Card.IsRemembered | TriggerZones$ Battlefield | Execute$ LoseTrack | Static$ True
+SVar:LoseTrack:DB$ Pump | ForgetObjects$ TriggeredCard
+T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ DBCleanup | Static$ True
+T:Mode$ TurnBegin | Execute$ DBCleanup | Static$ True
+T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ Player | TriggerZones$ Battlefield | CheckSVar$ X | SVarCompare$ GE1 | IsPresent$ Card.Self | Execute$ TrigChange | TriggerDescription$ At the beginning of each end step, if a creature dealt damage by CARDNAME this turn died, put that card onto the battlefield under your control. Sacrifice it when you lose control of CARDNAME.
+SVar:TrigChange:DB$ ChangeZoneAll | ChangeType$ Creature.IsRemembered+ThisTurnEntered_Graveyard_from_Battlefield | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | RememberChanged$ True | ForgetOtherRemembered$ True | SubAbility$ TrigDelay
+SVar:TrigDelay:DB$ DelayedTrigger | Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | RememberObjects$ Remembered | Execute$ TrigSac | Secondary$ True | SubAbility$ DBCleanup | SpellDescription$ Sacrifice it when you lose control of CARDNAME.
+SVar:TrigSac:DB$ SacrificeAll | Defined$ DelayTriggerRemembered | SubAbility$ DBCleanup
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
+SVar:X:Remembered$Amount
+Oracle:At the beginning of each end step, if a creature dealt damage by Krovikan Vampire this turn died, put that card onto the battlefield under your control. Sacrifice it when you lose control of Krovikan Vampire.
diff --git a/forge-gui/res/cardsfolder/l/lead_the_stampede.txt b/forge-gui/res/cardsfolder/l/lead_the_stampede.txt
index 128abea43ca..eaeacd03d3e 100644
--- a/forge-gui/res/cardsfolder/l/lead_the_stampede.txt
+++ b/forge-gui/res/cardsfolder/l/lead_the_stampede.txt
@@ -1,6 +1,5 @@
Name:Lead the Stampede
ManaCost:2 G
Types:Sorcery
-A:SP$ Dig | Cost$ 2 G | DigNum$ 5 | ChangeValid$ Creature | AnyNumber$ True | SpellDescription$ Look at the top five cards of your library. You may reveal any number of creature cards from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in any order.
-SVar:Picture:http://www.wizards.com/global/images/magic/general/lead_the_stampede.jpg
+A:SP$ Dig | Cost$ 2 G | DigNum$ 5 | AnyNumber$ True | ChangeValid$ Creature | StackDescription$ SpellDescription | SpellDescription$ Look at the top five cards of your library. You may reveal any number of creature cards from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in any order.
Oracle:Look at the top five cards of your library. You may reveal any number of creature cards from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in any order.
diff --git a/forge-gui/res/cardsfolder/m/malfegor_avatar.txt b/forge-gui/res/cardsfolder/m/malfegor_avatar.txt
index a6e6d05fc80..e9106dd81d1 100644
--- a/forge-gui/res/cardsfolder/m/malfegor_avatar.txt
+++ b/forge-gui/res/cardsfolder/m/malfegor_avatar.txt
@@ -4,7 +4,7 @@ Types:Vanguard
HandLifeModifier:+2/-2
T:Mode$ ChangesZone | ValidCard$ Creature.YouCtrl+IsUnearthed | Origin$ Any | Destination$ Battlefield | TriggerZones$ Command | Execute$ TrigPump | TriggerDescription$ Whenever a creature enters the battlefield under your control, if it was unearthed, it gets +3/+0.
SVar:TrigPump:DB$ Pump | Defined$ TriggeredCard | NumAtt$ +3
-T:Mode$ ChangesZone | ValidCard$ Card.IsUnearthed | Origin$ Any | Destination$ Exile | TriggerZones$ Command | Execute$ TrigMove | TriggerDescription$ Whenever a creature you control is exiled, if it was uneathed, shuffle that card into its owner's library.
+T:Mode$ ChangesZone | ValidCard$ Card.IsUnearthed | Origin$ Any | Destination$ Exile | TriggerZones$ Command | Execute$ TrigMove | TriggerDescription$ Whenever a creature you control is exiled, if it was unearthed, shuffle that card into its owner's library.
SVar:TrigMove:DB$ ChangeZone | Origin$ Exile | Destination$ Library | Defined$ TriggeredCard | Shuffle$ True
SVar:Picture:https://downloads.cardforge.org/images/cards/VAN/Malfegor Avatar.full.jpg
-Oracle:Hand +2, life -2\nWhenever a creature enters the battlefield under your control, if it was unearthed, it gets +3/+0.\nWhenever a creature you control is exiled, if it was uneathed, shuffle that card into its owner's library.
+Oracle:Hand +2, life -2\nWhenever a creature enters the battlefield under your control, if it was unearthed, it gets +3/+0.\nWhenever a creature you control is exiled, if it was unearthed, shuffle that card into its owner's library.
diff --git a/forge-gui/res/cardsfolder/m/mana_cache.txt b/forge-gui/res/cardsfolder/m/mana_cache.txt
new file mode 100644
index 00000000000..d3c0d6aa5cf
--- /dev/null
+++ b/forge-gui/res/cardsfolder/m/mana_cache.txt
@@ -0,0 +1,10 @@
+Name:Mana Cache
+ManaCost:1 R R
+Types:Enchantment
+T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ Player | Execute$ TrigCounters | TriggerDescription$ At the beginning of each player’s end step, put a charge counter on CARDNAME for each untapped land that player controls.
+SVar:TrigCounters:DB$ PutCounter | Defined$ Self | CounterType$ CHARGE | CounterNum$ X | References$ X
+SVar:X:Count$Valid Land.ActivePlayerCtrl+untapped
+A:AB$ Mana | Cost$ SubCounter<1/CHARGE> | Produced$ C | AnyPlayer$ True | PlayerTurn$ True | ActivationPhases$ Upkeep->Main2 | SpellDescription$ Add {C}. Any player may activate this ability but only during their turn before the end step.
+AI:RemoveDeck:All
+#Until Agetian can sort the AI use/misuse of this card
+Oracle:At the beginning of each player’s end step, put a charge counter on Mana Cache for each untapped land that player controls.\nRemove a charge counter from Mana Cache: Add {C}. Any player may activate this ability but only during their turn before the end step.
diff --git a/forge-gui/res/cardsfolder/m/mercurial_pretender.txt b/forge-gui/res/cardsfolder/m/mercurial_pretender.txt
index 426f1da5e9e..2d77d5cff3a 100644
--- a/forge-gui/res/cardsfolder/m/mercurial_pretender.txt
+++ b/forge-gui/res/cardsfolder/m/mercurial_pretender.txt
@@ -3,7 +3,7 @@ ManaCost:4 U
Types:Creature Shapeshifter
PT:0/0
K:ETBReplacement:Copy:DBCopy:Optional
-SVar:DBCopy:DB$ Clone | Choices$ Creature.YouCtrl+Other | AddAbilities$ MercurialBounce | SpellDescription$ You may have CARDNAME enter the battlefield as a copy of any creature you control, except it has "{2}{U}{U}: Return this creature to its owner's hand."
+SVar:DBCopy:DB$ Clone | Choices$ Creature.YouCtrl+Other | AddAbilities$ MercurialBounce | SpellDescription$ You may have CARDNAME enter the battlefield as a copy of a creature you control, except it has "{2}{U}{U}: Return this creature to its owner's hand."
SVar:MercurialBounce:AB$ ChangeZone | Cost$ 2 U U | Defined$ Self | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return CARDNAME to its owner's hand.
SVar:Picture:http://www.wizards.com/global/images/magic/general/mercurial_pretender.jpg
-Oracle:You may have Mercurial Pretender enter the battlefield as a copy of any creature you control, except it has "{2}{U}{U}: Return this creature to its owner's hand."
+Oracle:You may have Mercurial Pretender enter the battlefield as a copy of a creature you control, except it has "{2}{U}{U}: Return this creature to its owner's hand."
diff --git a/forge-gui/res/cardsfolder/m/mirrodin_besieged.txt b/forge-gui/res/cardsfolder/m/mirrodin_besieged.txt
index 24465e1f219..a90731e972c 100644
--- a/forge-gui/res/cardsfolder/m/mirrodin_besieged.txt
+++ b/forge-gui/res/cardsfolder/m/mirrodin_besieged.txt
@@ -10,7 +10,7 @@ SVar:Phyrexian:DB$ Animate | Defined$ Self | Triggers$ TrigEnd | Permanent$ True
SVar:TrigEnd:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | ValidPlayer$ You | Execute$ Filter | TriggerDescription$ At the beginning of your end step, draw a card, then discard a card. Then if there are fifteen or more artifact cards in your graveyard, target opponent loses the game.
SVar:Filter:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBDiscard
SVar:DBDiscard:DB$Discard | Defined$ You | Mode$ TgtChoose | NumCards$ 1 | SubAbility$ DBLose
-SVar:DBLose:DB$ LosesGame | ValidTgts$ Player | ConditionCheckSVar$ CheckGraveyard | ConditionSVarCompare$ GE15 | References$ CheckGraveyard
+SVar:DBLose:DB$ LosesGame | ValidTgts$ Opponent | ConditionCheckSVar$ CheckGraveyard | ConditionSVarCompare$ GE15 | References$ CheckGraveyard
SVar:CheckGraveyard:Count$ValidGraveyard Card.Artifact+YouOwn
DeckHas:Ability$Token
-Oracle:As Mirrodin Besieged enters the battlefield, choose Mirran or Phyrexian. \n• Mirran - Whenever you cast an artifact spell, create a 1/1 colorless Myr artifact creature token.\n• At the beginning of your end step, draw a card, then discard a card. Then if there are fifteen or more artifact cards in your graveyard, target opponent loses the game.
+Oracle:As Mirrodin Besieged enters the battlefield, choose Mirran or Phyrexian. \n• Mirran - Whenever you cast an artifact spell, create a 1/1 colorless Myr artifact creature token.\n• Phyrexian - At the beginning of your end step, draw a card, then discard a card. Then if there are fifteen or more artifact cards in your graveyard, target opponent loses the game.
diff --git a/forge-gui/res/cardsfolder/m/mirror_image.txt b/forge-gui/res/cardsfolder/m/mirror_image.txt
index 15deb13db7f..59c05f7a34d 100644
--- a/forge-gui/res/cardsfolder/m/mirror_image.txt
+++ b/forge-gui/res/cardsfolder/m/mirror_image.txt
@@ -3,6 +3,6 @@ ManaCost:2 U
Types:Creature Shapeshifter
PT:0/0
K:ETBReplacement:Copy:DBCopy:Optional
-SVar:DBCopy:DB$ Clone | Choices$ Creature.YouCtrl | SpellDescription$ You may have CARDNAME enter the battlefield as a copy of any creature you control.
+SVar:DBCopy:DB$ Clone | Choices$ Creature.YouCtrl | SpellDescription$ You may have CARDNAME enter the battlefield as a copy of a creature you control.
SVar:NeedsToPlay:Creature.YouCtrl
-Oracle:You may have Mirror Image enter the battlefield as a copy of any creature you control.
+Oracle:You may have Mirror Image enter the battlefield as a copy of a creature you control.
diff --git a/forge-gui/res/cardsfolder/m/morphic_tide.txt b/forge-gui/res/cardsfolder/m/morphic_tide.txt
index 9f27463b7f9..19d2e5b5e56 100644
--- a/forge-gui/res/cardsfolder/m/morphic_tide.txt
+++ b/forge-gui/res/cardsfolder/m/morphic_tide.txt
@@ -1,7 +1,7 @@
Name:Morphic Tide
ManaCost:no cost
Types:Phenomenon
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ TrigPut | TriggerDescription$ When you encounter CARDNAME, starting with you, each player may put a permanent card from their hand onto the battlefield. (Then planeswalk away from this phenomenon.)
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ TrigPut | TriggerDescription$ When you encounter CARDNAME, starting with you, each player may put a permanent card from their hand onto the battlefield. (Then planeswalk away from this phenomenon.)
SVar:TrigPut:DB$ RepeatEach | StartingWithActivator$ True | RepeatPlayers$ Player | RepeatSubAbility$ DBShuffle | SubAbility$ ChangePermanent
SVar:DBShuffle:DB$ ChangeZoneAll | ChangeType$ Permanent.RememberedPlayerOwn | Imprint$ True | Origin$ Battlefield | Destination$ Library | Shuffle$ True | SubAbility$ DBDig
SVar:DBDig:DB$ Dig | Defined$ Remembered | NoMove$ True | DigNum$ WarpX | References$ WarpX | RememberRevealed$ True | Reveal$ True | SubAbility$ DBCleanImprint
diff --git a/forge-gui/res/cardsfolder/m/mutual_epiphany.txt b/forge-gui/res/cardsfolder/m/mutual_epiphany.txt
index 5c25471c2f7..a03226581ff 100644
--- a/forge-gui/res/cardsfolder/m/mutual_epiphany.txt
+++ b/forge-gui/res/cardsfolder/m/mutual_epiphany.txt
@@ -1,7 +1,7 @@
Name:Mutual Epiphany
ManaCost:no cost
Types:Phenomenon
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ Epiphany | TriggerDescription$ When you encounter CARDNAME, each player draws four cards. (Then planeswalk away from this phenomenon)
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ Epiphany | TriggerDescription$ When you encounter CARDNAME, each player draws four cards. (Then planeswalk away from this phenomenon)
SVar:Epiphany:DB$ Draw | Defined$ Player | NumCards$ 4 | SubAbility$ PWAway | SpellDescription$ Each player draws four cards.
SVar:PWAway:DB$ Planeswalk | Cost$ 0
SVar:Picture:http://www.wizards.com/global/images/magic/general/mutual_epiphany.jpg
diff --git a/forge-gui/res/cardsfolder/n/narcomoeba.txt b/forge-gui/res/cardsfolder/n/narcomoeba.txt
index bd92f97f581..57fd43a49cb 100644
--- a/forge-gui/res/cardsfolder/n/narcomoeba.txt
+++ b/forge-gui/res/cardsfolder/n/narcomoeba.txt
@@ -4,6 +4,5 @@ Types:Creature Illusion
PT:1/1
K:Flying
T:Mode$ ChangesZone | Origin$ Library | Destination$ Graveyard | ValidCard$ Card.Self | OptionalDecider$ You | Execute$ TrigChange | TriggerDescription$ When CARDNAME is put into your graveyard from your library, you may put it onto the battlefield.
-SVar:TrigChange:DB$ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ Self
-SVar:Picture:http://www.wizards.com/global/images/magic/general/narcomoeba.jpg
+SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard
Oracle:Flying\nWhen Narcomoeba is put into your graveyard from your library, you may put it onto the battlefield.
diff --git a/forge-gui/res/cardsfolder/n/natural_unity.txt b/forge-gui/res/cardsfolder/n/natural_unity.txt
index 8d2f338263b..ce8603b2c26 100644
--- a/forge-gui/res/cardsfolder/n/natural_unity.txt
+++ b/forge-gui/res/cardsfolder/n/natural_unity.txt
@@ -2,7 +2,7 @@ Name:Natural Unity
ManaCost:no cost
Types:Conspiracy
K:Hidden agenda
-S:Mode$ Continuous | EffectZone$ Command | Affected$ Creature.NamedCard+YouCtrl | EffectZone$ Battlefield | AddTrigger$ NUCombat | AddSVar$ NUCounter | Description$ Creatures you control with the chosen name have "At the beginning of combat on your turn, you may pay {G}. If you do, put a +1/+1 counter on this creature."
+S:Mode$ Continuous | EffectZone$ Command | Affected$ Creature.NamedCard+YouCtrl | AffectedZone$ Battlefield | AddTrigger$ NUCombat | AddSVar$ NUCounter | Description$ Creatures you control with the chosen name have "At the beginning of combat on your turn, you may pay {G}. If you do, put a +1/+1 counter on this creature."
SVar:NUCombat:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | Execute$ NUCounter | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of combat on your turn, you may pay {G}. If you do, put a +1/+1 counter on this creature.
SVar:NUCounter:AB$ PutCounter | Cost$ G | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
SVar:AgendaLogic:BestCreatureInComputerDeck
diff --git a/forge-gui/res/cardsfolder/n/nissas_pilgrimage.txt b/forge-gui/res/cardsfolder/n/nissas_pilgrimage.txt
index 3ad0c877919..3dfef99dc62 100644
--- a/forge-gui/res/cardsfolder/n/nissas_pilgrimage.txt
+++ b/forge-gui/res/cardsfolder/n/nissas_pilgrimage.txt
@@ -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 | ForgetChanged$ True | 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.
diff --git a/forge-gui/res/cardsfolder/o/oran_rief_hydra.txt b/forge-gui/res/cardsfolder/o/oran_rief_hydra.txt
index 22e4daedd22..169ce64cb19 100644
--- a/forge-gui/res/cardsfolder/o/oran_rief_hydra.txt
+++ b/forge-gui/res/cardsfolder/o/oran_rief_hydra.txt
@@ -6,5 +6,5 @@ K:Trample
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Landfall — Whenever a land enters the battlefield under your control, put a +1/+1 counter on CARDNAME. If that land is a Forest, put two +1/+1 counters on CARDNAME instead.
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ X | References$ X
SVar:X:TriggeredCard$Valid Forest/Plus.1
-SVar:Picture:http://www.wizards.com/global/images/magic/general/oran_rief_hydra.jpg
+SVar:BuffedBy:Land
Oracle:Trample\nLandfall — Whenever a land enters the battlefield under your control, put a +1/+1 counter on Oran-Rief Hydra. If that land is a Forest, put two +1/+1 counters on Oran-Rief Hydra instead.
\ No newline at end of file
diff --git a/forge-gui/res/cardsfolder/p/panopticon.txt b/forge-gui/res/cardsfolder/p/panopticon.txt
index 8ff00fe0356..0180640c61b 100644
--- a/forge-gui/res/cardsfolder/p/panopticon.txt
+++ b/forge-gui/res/cardsfolder/p/panopticon.txt
@@ -1,7 +1,7 @@
Name:Panopticon
ManaCost:no cost
Types:Plane Mirrodin
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ PanopticonDraw | TriggerDescription$ When you planeswalk to CARDNAME, draw a card.
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ PanopticonDraw | TriggerDescription$ When you planeswalk to CARDNAME, draw a card.
T:Mode$ Phase | Phase$ Draw | ValidPlayer$ You | Execute$ PanopticonDraw | TriggerZones$ Command | TriggerDescription$ At the beginning of your draw step, draw an additional card.
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ PanopticonDraw | TriggerDescription$ Whenever you roll {CHAOS}, draw a card.
SVar:PanopticonDraw:DB$ Draw | Defined$ You | NumCards$ 1
diff --git a/forge-gui/res/cardsfolder/p/pelt_collector.txt b/forge-gui/res/cardsfolder/p/pelt_collector.txt
index 56e8b90ad45..d05c265f3aa 100644
--- a/forge-gui/res/cardsfolder/p/pelt_collector.txt
+++ b/forge-gui/res/cardsfolder/p/pelt_collector.txt
@@ -6,6 +6,7 @@ S:Mode$ Continuous | Affected$ Card.Self+counters_GE3_P1P1 | AddKeyword$ Trample
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.YouCtrl+powerGTX+Other | References$ X | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever another creature you control enters the battlefield or dies, if that creature's power is greater than CARDNAME's, put a +1/+1 counter on CARDNAME.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouCtrl+powerGTX+Other | References$ X | TriggerZones$ Battlefield | Secondary$ True | Execute$ TrigPutCounter | TriggerDescription$ Whenever another creature you control enters the battlefield or dies, if that creature's power is greater than CARDNAME's, put a +1/+1 counter on CARDNAME.
SVar:X:Count$CardPower
-SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
+SVar:Y:TriggeredCard$CardPower
+SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ LTY | References$ X,Y
DeckHas:Ability$Counters
Oracle:Whenever another creature you control enters the battlefield or dies, if that creature's power is greater than Pelt Collector's, put a +1/+1 counter on Pelt Collector.\nAs long as Pelt Collector has three or more +1/+1 counters on it, it has trample.
\ No newline at end of file
diff --git a/forge-gui/res/cardsfolder/p/planewide_disaster.txt b/forge-gui/res/cardsfolder/p/planewide_disaster.txt
index 3264fab3bd3..f81fc737477 100644
--- a/forge-gui/res/cardsfolder/p/planewide_disaster.txt
+++ b/forge-gui/res/cardsfolder/p/planewide_disaster.txt
@@ -1,7 +1,7 @@
Name:Planewide Disaster
ManaCost:no cost
Types:Phenomenon
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ Disaster | TriggerDescription$ When you encounter CARDNAME, destroy all creatures. (Then planeswalk away from this phenomenon)
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ Disaster | TriggerDescription$ When you encounter CARDNAME, destroy all creatures. (Then planeswalk away from this phenomenon)
SVar:Disaster:DB$ DestroyAll | ValidCards$ Creature | SubAbility$ PWAway
SVar:PWAway:DB$ Planeswalk | Cost$ 0
SVar:Picture:http://www.wizards.com/global/images/magic/general/planewide_disaster.jpg
diff --git a/forge-gui/res/cardsfolder/p/power_artifact.txt b/forge-gui/res/cardsfolder/p/power_artifact.txt
index d95ffe6b2b7..09594c0d2be 100644
--- a/forge-gui/res/cardsfolder/p/power_artifact.txt
+++ b/forge-gui/res/cardsfolder/p/power_artifact.txt
@@ -2,8 +2,7 @@ Name:Power Artifact
ManaCost:U U
Types:Enchantment Aura
K:Enchant artifact
-S:Mode$ ReduceCost | ValidCard$ Artifact.EnchantedBy | Type$ Ability | Amount$ 2 | MinMana$ 1 | Description$ Enchanted artifact's activated abilities cost {2} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana.
+S:Mode$ ReduceCost | ValidCard$ Artifact.EnchantedBy | Type$ Ability | Amount$ 2 | MinMana$ 1 | Description$ Enchanted artifact's activated abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.
A:SP$ Attach | Cost$ U U | ValidTgts$ Artifact | AILogic$ Pump
AI:RemoveDeck:Random
-SVar:Picture:http://www.wizards.com/global/images/magic/general/power_artifact.jpg
-Oracle:Enchant artifact\nEnchanted artifact's activated abilities cost {2} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana.
+Oracle:Enchant artifact\nEnchanted artifact's activated abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.
diff --git a/forge-gui/res/cardsfolder/p/prismatic_vista.txt b/forge-gui/res/cardsfolder/p/prismatic_vista.txt
index f928ed83f5a..1ee34e1ac73 100644
--- a/forge-gui/res/cardsfolder/p/prismatic_vista.txt
+++ b/forge-gui/res/cardsfolder/p/prismatic_vista.txt
@@ -2,4 +2,4 @@ Name:Prismatic Vista
ManaCost:no cost
Types:Land
A:AB$ ChangeZone | Cost$ T PayLife<1> Sac<1/CARDNAME> | Origin$ Library | Destination$ Battlefield | ChangeType$ Land.Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic land card, put it onto the battlefield, then shuffle your library.
-Oracle:{T}, Pay 1 life, Sacrifice Prismatic View: Search your library for a basic land card, put it onto the battlefield, then shuffle your library.
+Oracle:{T}, Pay 1 life, Sacrifice Prismatic Vista: Search your library for a basic land card, put it onto the battlefield, then shuffle your library.
diff --git a/forge-gui/res/cardsfolder/p/pyramids.txt b/forge-gui/res/cardsfolder/p/pyramids.txt
new file mode 100644
index 00000000000..51a2c93d266
--- /dev/null
+++ b/forge-gui/res/cardsfolder/p/pyramids.txt
@@ -0,0 +1,10 @@
+Name:Pyramids
+ManaCost:6
+Types:Artifact
+A:AB$ Charm | Cost$ 2 | Choices$ DBDestroy,DBProtect | Defined$ You
+SVar:DBDestroy:DB$ Destroy | ValidTgts$ Aura.AttachedTo Land | TgtPrompt$ Select target Aura attached to a land | SpellDescription$ Destroy target Aura attached to a land.
+SVar:DBProtect:DB$ Effect | ValidTgts$ Land | TgtPrompt$ Select target land | ReplacementEffects$ DBRemove | SVars$ ExileEffect,RemoveDamage | ForgetOnMoved$ Battlefield | RememberObjects$ Targeted | SpellDescription$ The next time target land would be destroyed this turn, remove all damage marked on it instead.
+SVar:DBRemove:Event$ Destroy | ValidCard$ Land.IsRemembered | ReplaceWith$ RemoveDamage | Description$ The next time target land would be destroyed this turn, remove all damage marked on it instead.
+SVar:RemoveDamage:DB$ DealDamage | Defined$ ReplacedCard | Remove$ All | SubAbility$ ExileEffect
+SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
+Oracle:{2}: Choose one —\n• Destroy target Aura attached to a land.\n• The next time target land would be destroyed this turn, remove all damage marked on it instead.
diff --git a/forge-gui/res/cardsfolder/q/quicksilver_sea.txt b/forge-gui/res/cardsfolder/q/quicksilver_sea.txt
index d2d97f9c409..e654d641e31 100644
--- a/forge-gui/res/cardsfolder/q/quicksilver_sea.txt
+++ b/forge-gui/res/cardsfolder/q/quicksilver_sea.txt
@@ -1,7 +1,7 @@
Name:Quicksilver Sea
ManaCost:no cost
Types:Plane Mirrodin
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ QuicksilverScry | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, scry 4. (Look at the top four cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ QuicksilverScry | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, scry 4. (Look at the top four cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ QuicksilverScry | TriggerZones$ Command | Secondary$ True | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, scry 4. (Look at the top four cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)
SVar:QuicksilverScry:DB$ Scry | ScryNum$ 4
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, reveal the top card of your library. You may play it without paying its mana cost.
diff --git a/forge-gui/res/cardsfolder/r/reality_shaping.txt b/forge-gui/res/cardsfolder/r/reality_shaping.txt
index b7ebb20cda5..94ecfee2b93 100644
--- a/forge-gui/res/cardsfolder/r/reality_shaping.txt
+++ b/forge-gui/res/cardsfolder/r/reality_shaping.txt
@@ -1,7 +1,7 @@
Name:Reality Shaping
ManaCost:no cost
Types:Phenomenon
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ TrigPutFromHand | TriggerDescription$ When you encounter CARDNAME, starting with you, each player may put a permanent card from their hand onto the battlefield. (Then planeswalk away from this phenomenon.)
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ TrigPutFromHand | TriggerDescription$ When you encounter CARDNAME, starting with you, each player may put a permanent card from their hand onto the battlefield. (Then planeswalk away from this phenomenon.)
SVar:TrigPutFromHand:DB$ RepeatEach | StartingWithActivator$ True | RepeatPlayers$ Player | RepeatSubAbility$ DBChangeZone | SubAbility$ PWAway
SVar:DBChangeZone:DB$ ChangeZone | DefinedPlayer$ Player.IsRemembered | Choser$ Player.IsRemembered | ChangeType$ Permanent | ChangeNum$ 1 | Origin$ Hand | Destination$ Battlefield
SVar:PWAway:DB$ Planeswalk | Cost$ 0
diff --git a/forge-gui/res/cardsfolder/s/settle_the_score.txt b/forge-gui/res/cardsfolder/s/settle_the_score.txt
index 08d18bef014..4820ed006f0 100644
--- a/forge-gui/res/cardsfolder/s/settle_the_score.txt
+++ b/forge-gui/res/cardsfolder/s/settle_the_score.txt
@@ -1,7 +1,6 @@
Name:Settle the Score
ManaCost:2 B B
Types:Sorcery
-A:SP$ ChangeZone | Cost$ 2 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBChoice | SpellDescription$ Exile target creature. Put two loyalty counters on a planeswalker you control.
-SVar:DBChoice:DB$ ChooseCard | Choices$ Planeswalker.YouCtrl | Amount$ 1 | Mandatory$ True | SubAbility$ DBPutLoyalty
-SVar:DBPutLoyalty:DB$ PutCounter | Defined$ ChosenCard | CounterType$ LOYALTY | CounterNum$ 2
+A:SP$ ChangeZone | Cost$ 2 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBPutLoyalty | SpellDescription$ Exile target creature. Put two loyalty counters on a planeswalker you control.
+SVar:DBPutLoyalty:DB$ PutCounter | Choices$ Planeswalker.YouCtrl | CounterType$ LOYALTY | CounterNum$ 2
Oracle:Exile target creature. Put two loyalty counters on a planeswalker you control.
diff --git a/forge-gui/res/cardsfolder/s/settle_the_wreckage.txt b/forge-gui/res/cardsfolder/s/settle_the_wreckage.txt
index 521f24cb11c..c136b03296f 100644
--- a/forge-gui/res/cardsfolder/s/settle_the_wreckage.txt
+++ b/forge-gui/res/cardsfolder/s/settle_the_wreckage.txt
@@ -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
diff --git a/forge-gui/res/cardsfolder/s/shambleshark.txt b/forge-gui/res/cardsfolder/s/shambleshark.txt
index b970d046e2e..8c78ead85dc 100644
--- a/forge-gui/res/cardsfolder/s/shambleshark.txt
+++ b/forge-gui/res/cardsfolder/s/shambleshark.txt
@@ -1,6 +1,6 @@
Name:Shambleshark
ManaCost:G U
-Types:Creature Fish Crab
+Types:Creature Shark Crab
PT:2/1
K:Flash
K:Evolve
diff --git a/forge-gui/res/cardsfolder/s/sharktocrab.txt b/forge-gui/res/cardsfolder/s/sharktocrab.txt
index f67c19e1ec9..22647ca4571 100644
--- a/forge-gui/res/cardsfolder/s/sharktocrab.txt
+++ b/forge-gui/res/cardsfolder/s/sharktocrab.txt
@@ -1,11 +1,11 @@
Name:Sharktocrab
ManaCost:2 G U
-Types:Creature Fish Octopus Crab
+Types:Creature Shark Octopus Crab
PT:4/4
K:Adapt:1:2 G U
DeckHas:Ability$Counters
-T:Mode$ CounterAddedOnce | ValidCard$ Card.Self | TriggerZones$ Battlefield | CounterType$ P1P1 | Execute$ TrigTap | TriggerDescription$ Whenever one or more +1/+1 counter are put on CARDNAME, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.
+T:Mode$ CounterAddedOnce | ValidCard$ Card.Self | TriggerZones$ Battlefield | CounterType$ P1P1 | Execute$ TrigTap | TriggerDescription$ Whenever one or more +1/+1 counters are put on CARDNAME, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.
SVar:TrigTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Choose target creature an opponent controls. | SubAbility$ DBPump
SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ HIDDEN This card doesn't untap during your next untap step. | Permanent$ True
SVar:HasAttackEffect:TRUE
-Oracle:{2}{G}{U}: Adapt 1. (If this creature has no +1/+1 counters on it, put a +1/+1 counter on it.)\nWhenever one or more +1/+1 counter are put on Sharktocrab, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.
+Oracle:{2}{G}{U}: Adapt 1. (If this creature has no +1/+1 counters on it, put a +1/+1 counter on it.)\nWhenever one or more +1/+1 counters are put on Sharktocrab, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.
diff --git a/forge-gui/res/cardsfolder/s/silverblade_paladin.txt b/forge-gui/res/cardsfolder/s/silverblade_paladin.txt
index 050b834d63a..5eac0e468bb 100644
--- a/forge-gui/res/cardsfolder/s/silverblade_paladin.txt
+++ b/forge-gui/res/cardsfolder/s/silverblade_paladin.txt
@@ -3,6 +3,6 @@ ManaCost:1 W W
Types:Creature Human Knight
PT:2/2
K:Soulbond
-S:Mode$ Continuous | Affected$ Creature.PairedWith,Creature.Self+Paired | AddKeyword$ Double Strike | Description$ As long as CARDNAME is paired with another creature, both creature have double strike.
+S:Mode$ Continuous | Affected$ Creature.PairedWith,Creature.Self+Paired | AddKeyword$ Double Strike | Description$ As long as CARDNAME is paired with another creature, both creatures have double strike.
SVar:Picture:http://www.wizards.com/global/images/magic/general/silverblade_paladin.jpg
Oracle:Soulbond (You may pair this creature with another unpaired creature when either enters the battlefield. They remain paired for as long as you control both of them.)\nAs long as Silverblade Paladin is paired with another creature, both creatures have double strike.
diff --git a/forge-gui/res/cardsfolder/s/skybreen.txt b/forge-gui/res/cardsfolder/s/skybreen.txt
index 3206bb61e0c..c267a214e0b 100644
--- a/forge-gui/res/cardsfolder/s/skybreen.txt
+++ b/forge-gui/res/cardsfolder/s/skybreen.txt
@@ -1,7 +1,7 @@
Name:Skybreen
ManaCost:no cost
Types:Plane Kaldheim
-S:Mode$ Continuous | Affected$ Card.TopLibrary | AffectedZone$ Library | MayLookAt$ Player | Description$ Players play with the top card of their libraries revealed.
+S:Mode$ Continuous | EffectZone$ Command | Affected$ Card.TopLibrary | AffectedZone$ Library | MayLookAt$ Player | Description$ Players play with the top card of their libraries revealed.
S:Mode$ CantBeCast | EffectZone$ Command | ValidCard$ Card.sharesCardTypeWith EachTopLibrary | Description$ Spells that share a card type with the top card of a library can't be cast.
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, target player loses life equal to the number of cards in their hand.
SVar:RolledChaos:DB$ LoseLife | ValidTgts$ Player | LifeAmount$ Y | References$ Y
diff --git a/forge-gui/res/cardsfolder/s/spatial_merging.txt b/forge-gui/res/cardsfolder/s/spatial_merging.txt
index 114b6bed220..2227d169428 100644
--- a/forge-gui/res/cardsfolder/s/spatial_merging.txt
+++ b/forge-gui/res/cardsfolder/s/spatial_merging.txt
@@ -1,7 +1,7 @@
Name:Spatial Merging
ManaCost:no cost
Types:Phenomenon
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ TrigDig | TriggerDescription$ When you encounter CARDNAME, reveal cards from the top of your planar deck until you reveal two plane cards. Simultaneously planeswalk to both of them. Put all other cards revealed this way on the bottom of your planar deck in any order.
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ When you encounter CARDNAME, reveal cards from the top of your planar deck until you reveal two plane cards. Simultaneously planeswalk to both of them. Put all other cards revealed this way on the bottom of your planar deck in any order.
SVar:TrigDig:DB$ DigUntil | Amount$ 2 | Valid$ Plane | DigZone$ PlanarDeck | RememberFound$ True | FoundDestination$ PlanarDeck | RevealedDestination$ PlanarDeck | RevealedLibraryPosition$ -1 | SubAbility$ DBPWTo
SVar:DBPWTo:DB$ Planeswalk | Defined$ Remembered | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
diff --git a/forge-gui/res/cardsfolder/s/stand_or_fall.txt b/forge-gui/res/cardsfolder/s/stand_or_fall.txt
index 84202bdd218..346c76854d7 100644
--- a/forge-gui/res/cardsfolder/s/stand_or_fall.txt
+++ b/forge-gui/res/cardsfolder/s/stand_or_fall.txt
@@ -1,11 +1,11 @@
Name:Stand or Fall
ManaCost:3 R
Types:Enchantment
-T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | Execute$ ChoosePlayer | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of combat on your turn, separate all creatures defending player controls into two piles. Only creatures in the pile of that player's choice can block this turn.
-SVar:ChoosePlayer:DB$ ChoosePlayer | Choices$ Opponent | Defined$ You | AILogic$ LeastCreatures | SubAbility$ DBTwoPiles
-SVar:DBTwoPiles:DB$ TwoPiles | Defined$ ChosenPlayer | Chooser$ ChosenPlayer | Zone$ Battlefield | ValidCards$ Creature.ChosenCtrl | Separator$ You | ChosenPile$ DBOnlyBlock | AILogic$ Best
-SVar:DBOnlyBlock:DB$ PumpAll | KW$ HIDDEN CARDNAME can't block. | IsCurse$ True | ValidCards$ Card.Creature+IsNotRemembered | SubAbility$ DBCleanup
-SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenPlayer$ True
+T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | Execute$ RepeatPlayer | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of combat on your turn, for each defending player, separate all creatures that player controls into two piles and that player chooses one. Only creatures in the chosen piles can block this turn.
+SVar:RepeatPlayer:DB$ RepeatEach | RepeatPlayers$ Opponent | RepeatSubAbility$ DBTwoPiles | SubAbility$ DBOnlyBlock
+SVar:DBTwoPiles:DB$ TwoPiles | Defined$ Player.IsRemembered | Chooser$ Player.IsRemembered | Zone$ Battlefield | ValidCards$ Creature.RememberedPlayerCtrl | Separator$ You | ChosenPile$ DBRemember | AILogic$ Best
+SVar:DBRemember:DB$ Pump | ImprintCards$ Remembered
+SVar:DBOnlyBlock:DB$ PumpAll | KW$ HIDDEN CARDNAME can't block. | ValidCards$ Creature.OppCtrl+IsNotImprinted | SubAbility$ DBCleanup
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
SVar:PlayMain1:TRUE
-SVar:Picture:http://www.wizards.com/global/images/magic/general/stand_or_fall.jpg
-Oracle:At the beginning of combat on your turn, separate all creatures defending player controls into two piles. Only creatures in the pile of that player's choice can block this turn.
+Oracle:At the beginning of combat on your turn, for each defending player, separate all creatures that player controls into two piles and that player chooses one. Only creatures in the chosen piles can block this turn.
diff --git a/forge-gui/res/cardsfolder/t/tawnoss_coffin.txt b/forge-gui/res/cardsfolder/t/tawnoss_coffin.txt
new file mode 100644
index 00000000000..20d74e2ceda
--- /dev/null
+++ b/forge-gui/res/cardsfolder/t/tawnoss_coffin.txt
@@ -0,0 +1,18 @@
+Name:Tawnos's Coffin
+ManaCost:4
+Types:Artifact
+K:You may choose not to untap CARDNAME during your untap step.
+A:AB$ Pump | Cost$ 3 T | ValidTgts$ Creature | ImprintCards$ Targeted | SubAbility$ RecordCounters | StackDescription$ SpellDescription | SpellDescription$ Exile target creature and all Auras attached to it. Note the number and kind of counters that were on that creature. When CARDNAME leaves the battlefield or becomes untapped, return that exiled card to the battlefield under its owner’s control tapped with the noted number and kind of counters on it. If you do, return the other exiled cards to the battlefield under their owner’s control attached to that permanent.
+SVar:RecordCounters:DB$ NoteCounters | Mode$ Store | Defined$ Imprinted | SubAbility$ DBRememberAura
+SVar:DBRememberAura:DB$ PumpAll | ValidCards$ Aura.AttachedTo Creature.IsImprinted | RememberAllPumped$ True | StackDescription$ None | SubAbility$ DBEffect
+SVar:DBEffect:DB$ Effect | Triggers$ LeavesPlay,Untap | SVars$ RestoreCounters,TrigReturn,TrigAuraReturn,ExileSelf | References$ LeavesPlay,Untap | ImprintCards$ ParentTarget | RememberObjects$ Remembered | SubAbility$ DBExile
+SVar:DBExile:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ Card.IsRemembered,Card.IsImprinted | SubAbility$ DBCleanup
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
+SVar:LeavesPlay:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.EffectSource | Execute$ RestoreCounters | TriggerController$ TriggeredCardController | TriggerDescription$ When EFFECTSOURCE leaves the battlefield or becomes untapped, return that exiled card to the battlefield under its owner’s control tapped with the noted number and kind of counters on it. If you do, return the other exiled cards to the battlefield under their owner’s control attached to that permanent.
+SVar:Untap:Mode$ Untaps | ValidCard$ Card.EffectSource | Execute$ RestoreCounters | TriggerController$ TriggeredCardController | Secondary$ True | TriggerDescription$ When EFFECTSOURCE leaves the battlefield or becomes untapped, return that exiled card to the battlefield under its owner’s control tapped with the noted number and kind of counters on it. If you do, return the other exiled cards to the battlefield under their owner’s control attached to that permanent.
+SVar:RestoreCounters:DB$ NoteCounters | Mode$ Load | Defined$ Imprinted | SubAbility$ TrigReturn
+SVar:TrigReturn:DB$ ChangeZone | Defined$ Imprinted | Origin$ Exile | Destination$ Battlefield | Tapped$ True | SubAbility$ TrigAuraReturn
+SVar:TrigAuraReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | AttachedTo$ Valid Creature.IsImprinted | SubAbility$ ExileSelf
+SVar:ExileSelf:DB$ ChangeZone | Origin$ Command | Destination$ Exile | Defined$ Self
+AI:RemoveDeck:All
+Oracle:You may choose not to untap Tawnos’s Coffin during your untap step.\n{3},{T}: Exile target creature and all Auras attached to it. Note the number and kind of counters that were on that creature. When Tawnos’s Coffin leaves the battlefield or becomes untapped, return that exiled card to the battlefield under its owner’s control tapped with the noted number and kind of counters on it. If you do, return the other exiled cards to the battlefield under their owner’s control attached to that permanent.
diff --git a/forge-gui/res/cardsfolder/t/the_aether_flues.txt b/forge-gui/res/cardsfolder/t/the_aether_flues.txt
index 81e2ce51bab..6a09e777551 100644
--- a/forge-gui/res/cardsfolder/t/the_aether_flues.txt
+++ b/forge-gui/res/cardsfolder/t/the_aether_flues.txt
@@ -1,7 +1,7 @@
Name:The Aether Flues
ManaCost:no cost
Types:Plane Iquatana
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ FluesSacrifice | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, you may sacrifice a creature. If you do, reveal cards from the top of your library until you reveal a creature card, put that card onto the battlefield, then shuffle all other cards revealed this way into your library.
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ FluesSacrifice | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, you may sacrifice a creature. If you do, reveal cards from the top of your library until you reveal a creature card, put that card onto the battlefield, then shuffle all other cards revealed this way into your library.
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ FluesSacrifice | TriggerZones$ Command | Secondary$ True | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, you may sacrifice a creature. If you do, reveal cards from the top of your library until you reveal a creature card, put that card onto the battlefield, then shuffle all other cards revealed this way into your library.
SVar:FluesSacrifice:DB$ Sacrifice | Optional$ True | SacValid$ Creature | Amount$ 1 | RememberSacrificed$ True | SubAbility$ FluesDig
SVar:FluesDig:DB$ DigUntil | Valid$ Creature | ValidDescription$ creature | FoundDestination$ Battlefield | RevealedDestination$ Library | Shuffle$ True | ConditionDefined$ Remembered | ConditionPresent$ Creature | ConditionCompare$ EQ1 | SubAbility$ DBCleanup
diff --git a/forge-gui/res/cardsfolder/t/the_birth_of_meletis.txt b/forge-gui/res/cardsfolder/t/the_birth_of_meletis.txt
index 349f1e790d9..27021aac864 100644
--- a/forge-gui/res/cardsfolder/t/the_birth_of_meletis.txt
+++ b/forge-gui/res/cardsfolder/t/the_birth_of_meletis.txt
@@ -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.
diff --git a/forge-gui/res/cardsfolder/t/the_elderspell.txt b/forge-gui/res/cardsfolder/t/the_elderspell.txt
index ec0e5f85773..2255b8d308f 100644
--- a/forge-gui/res/cardsfolder/t/the_elderspell.txt
+++ b/forge-gui/res/cardsfolder/t/the_elderspell.txt
@@ -1,9 +1,8 @@
Name:The Elderspell
ManaCost:B B
Types:Sorcery
-A:SP$ Destroy | Cost$ B B | ValidTgts$ Planeswalker | TgtPrompt$ Select target planeswalker | TargetMin$ 0 | TargetMax$ MaxTargets | References$ MaxTargets | SubAbility$ DBChooseCard | RememberDestroyed$ True | SpellDescription$ Destroy any number of target planeswalkers. Choose a planeswalker you control. Put two loyalty counters on it for each planeswalker destroyed this way.
-SVar:DBChooseCard:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Planeswalker.YouCtrl | Mandatory$ True | SubAbility$ DBPutLoyalty
-SVar:DBPutLoyalty:DB$ PutCounter | Defined$ ChosenCard | CounterType$ LOYALTY | CounterNum$ X | References$ X | SubAbility$ DBCleanup
+A:SP$ Destroy | Cost$ B B | ValidTgts$ Planeswalker | TgtPrompt$ Select target planeswalker | TargetMin$ 0 | TargetMax$ MaxTargets | References$ MaxTargets | SubAbility$ DBPutLoyalty | RememberDestroyed$ True | SpellDescription$ Destroy any number of target planeswalkers. Choose a planeswalker you control. Put two loyalty counters on it for each planeswalker destroyed this way.
+SVar:DBPutLoyalty:DB$ PutCounter | Choices$ Planeswalker.YouCtrl | CounterType$ LOYALTY | CounterNum$ X | References$ X | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:MaxTargets:Count$Valid Planeswalker
SVar:X:Count$RememberedSize/Twice
diff --git a/forge-gui/res/cardsfolder/t/the_triumph_of_anax.txt b/forge-gui/res/cardsfolder/t/the_triumph_of_anax.txt
index a7aacb3fd3c..6a6b9d25af5 100755
--- a/forge-gui/res/cardsfolder/t/the_triumph_of_anax.txt
+++ b/forge-gui/res/cardsfolder/t/the_triumph_of_anax.txt
@@ -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
diff --git a/forge-gui/res/cardsfolder/t/time_distortion.txt b/forge-gui/res/cardsfolder/t/time_distortion.txt
index 5d80ee981ce..1e520040612 100644
--- a/forge-gui/res/cardsfolder/t/time_distortion.txt
+++ b/forge-gui/res/cardsfolder/t/time_distortion.txt
@@ -1,7 +1,7 @@
Name:Time Distortion
ManaCost:no cost
Types:Phenomenon
-T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | TriggerZones$ Command | Execute$ TrigReverse | TriggerDescription$ When you encounter CARDNAME, reverse the game's turn order. (For example, if play had proceeded clockwise around the table, it now goes counterclockwise. Then planeswalk away from this phenomenon.)
+T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ TrigReverse | TriggerDescription$ When you encounter CARDNAME, reverse the game's turn order. (For example, if play had proceeded clockwise around the table, it now goes counterclockwise. Then planeswalk away from this phenomenon.)
SVar:TrigReverse:DB$ ReverseTurnOrder | SubAbility$ PWAway
SVar:PWAway:DB$ Planeswalk | Cost$ 0
SVar:Picture:http://www.wizards.com/global/images/magic/general/time_distortion.jpg
diff --git a/forge-gui/res/cardsfolder/t/timmerian_fiends.txt b/forge-gui/res/cardsfolder/t/timmerian_fiends.txt
new file mode 100644
index 00000000000..028e195e54b
--- /dev/null
+++ b/forge-gui/res/cardsfolder/t/timmerian_fiends.txt
@@ -0,0 +1,13 @@
+Name:Timmerian Fiends
+ManaCost:1 B B
+Types:Creature Horror
+PT:1/1
+K:Remove CARDNAME from your deck before playing if you're not playing for ante.
+A:AB$ Pump | Cost$ B B B Sac<1/CARDNAME> | ValidTgts$ Artifact | SubAbility$ DBAnte | ImprintCards$ Targeted | RememberObjects$ TargetedController | StackDescription$ SpellDescription | SpellDescription$ The owner of target artifact may ante the top card of their library. If that player doesn’t, exchange ownership of that artifact and CARDNAME. Put the artifact card into your graveyard and CARDNAME from anywhere into that player’s graveyard. This change in ownership is permanent.
+SVar:DBAnte:DB$ Mill | Defined$ Player.IsRemembered | Destination$ Ante | NumCards$ 1 | RememberMilled$ True | Optional$ True | SubAbility$ ExchangeOwn1 | AILogic$ TimmerianFiends | StackDescription$ None
+SVar:ExchangeOwn1:DB$ GainOwnership | Defined$ Imprinted | DefinedPlayer$ You | SubAbility$ ToGrave1 | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0
+SVar:ToGrave1:DB$ ChangeZone | Defined$ Imprinted | Origin$ Battlefield | Destination$ Graveyard | SubAbility$ ExchangeOwn2 | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0 | StackDescription$ None
+SVar:ExchangeOwn2:DB$ GainOwnership | Defined$ Self | DefinedPlayer$ Remembered | SubAbility$ ToGrave | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0
+SVar:ToGrave:DB$ ChangeZone | Defined$ Self | Origin$ All | Destination$ Graveyard | SubAbility$ DBCleanup | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
+Oracle:Remove Timmerian Fiends from your deck before playing if you’re not playing for ante.\n{B}{B}{B}, Sacrifice Timmerian Fiends: The owner of target artifact may ante the top card of their library. If that player doesn’t, exchange ownership of that artifact and Timmerian Fiends. Put the artifact card into your graveyard and Timmerian Fiends from anywhere into that player’s graveyard. This change in ownership is permanent.
diff --git a/forge-gui/res/cardsfolder/t/tormods_crypt.txt b/forge-gui/res/cardsfolder/t/tormods_crypt.txt
index 9e97ad829b6..3989e27aafe 100644
--- a/forge-gui/res/cardsfolder/t/tormods_crypt.txt
+++ b/forge-gui/res/cardsfolder/t/tormods_crypt.txt
@@ -1,7 +1,6 @@
Name:Tormod's Crypt
ManaCost:0
Types:Artifact
-A:AB$ ChangeZoneAll | Cost$ T Sac<1/CARDNAME> | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Player | TgtPrompt$ Select target player | ChangeType$ Card | Shuffle$ True | SpellDescription$ Exile all cards from target player's graveyard.
+A:AB$ ChangeZoneAll | Cost$ T Sac<1/CARDNAME> | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Player | TgtPrompt$ Select target player | ChangeType$ Card | SpellDescription$ Exile all cards from target player's graveyard.
AI:RemoveDeck:Random
-SVar:Picture:http://www.wizards.com/global/images/magic/general/tormods_crypt.jpg
Oracle:{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.
diff --git a/forge-gui/res/cardsfolder/t/training_grounds.txt b/forge-gui/res/cardsfolder/t/training_grounds.txt
index cb68f30c87a..82dc7497592 100644
--- a/forge-gui/res/cardsfolder/t/training_grounds.txt
+++ b/forge-gui/res/cardsfolder/t/training_grounds.txt
@@ -1,6 +1,5 @@
Name:Training Grounds
ManaCost:U
Types:Enchantment
-S:Mode$ ReduceCost | ValidCard$ Creature.YouCtrl | Type$ Ability | Amount$ 2 | MinMana$ 1 | AffectedZone$ Battlefield | Description$ Activated abilities of creatures you control cost {2} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana.
-SVar:Picture:http://www.wizards.com/global/images/magic/general/training_grounds.jpg
-Oracle:Activated abilities of creatures you control cost up to {2} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana.
+S:Mode$ ReduceCost | ValidCard$ Creature.YouCtrl | Type$ Ability | Amount$ 2 | MinMana$ 1 | AffectedZone$ Battlefield | Description$ Activated abilities of creatures you control cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.
+Oracle:Activated abilities of creatures you control cost up to {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/adaptive_shimmerer.txt b/forge-gui/res/cardsfolder/upcoming/C2020/adaptive_shimmerer.txt
new file mode 100755
index 00000000000..7bbe7d005fe
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/adaptive_shimmerer.txt
@@ -0,0 +1,7 @@
+Name:Adaptive Shimmerer
+ManaCost:5
+Types:Creature Insect
+PT:0/0
+K:Flash
+K:etbCounter:P1P1:3
+Oracle:Flash\nAdaptive Shimmerer enters the battlefield with three +1/+1 counters on it.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/agitator_ant.txt b/forge-gui/res/cardsfolder/upcoming/C2020/agitator_ant.txt
new file mode 100755
index 00000000000..be28f69d4c7
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/agitator_ant.txt
@@ -0,0 +1,10 @@
+Name:Agitator Ant
+ManaCost:2 R
+Types:Creature Insect
+PT:2/2
+T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigRepeat | TriggerDescription$ At the beginning of your end step, each player may put two +1/+1 counters on a creature they control. Goad each creature that had counters put on it this way. (Until your next turn, those creatures attack each combat if able and attack a player other than you if able.)
+SVar:TrigRepeat:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBPutCounter | SubAbility$ DBGoad
+SVar:DBPutCounter:DB$ PutCounter | Chooser$ Remembered | Choices$ Creature.RememberedPlayerCtrl | ChoiceOptional$ True | CounterType$ P1P1 | CounterNum$ 2 | RememberCards$ True
+SVar:DBGoad:DB$ Goad | Defined$ Remembered | SubAbility$ DBCleanup
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
+Oracle:At the beginning of your end step, each player may put two +1/+1 counters on a creature they control. Goad each creature that had counters put on it this way. (Until your next turn, those creatures attack each combat if able and attack a player other than you if able.)
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/avenging_huntbonder.txt b/forge-gui/res/cardsfolder/upcoming/C2020/avenging_huntbonder.txt
new file mode 100755
index 00000000000..143e3bbd3a2
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/avenging_huntbonder.txt
@@ -0,0 +1,9 @@
+Name:Avenging Huntbonder
+ManaCost:3 W W
+Types:Creature Human Warrior
+PT:3/3
+K:Double Strike
+T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ Whenever CARDNAME attacks, put a double strike counter on another target attacking creature.
+SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.attacking+Other | TgtPrompt$ Select another target attacking creature | CounterType$ DOUBLE_STRIKE | CounterNum$ 1
+SVar:HasAttackEffect:TRUE
+Oracle:Double strike\nWhenever Avenging Huntbonder attacks, put a double strike counter on another target attacking creature.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/bonders_ornament.txt b/forge-gui/res/cardsfolder/upcoming/C2020/bonders_ornament.txt
new file mode 100755
index 00000000000..83d7ea6cc03
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/bonders_ornament.txt
@@ -0,0 +1,8 @@
+Name:Bonder's Ornament
+ManaCost:3
+Types:Artifact
+A:AB$ Mana | Cost$ T | Produced$ Any | SpellDescription$ Add one mana of any color.
+A:AB$ RepeatEach | Cost$ 4 T | RepeatPlayers$ Player | RepeatSubAbility$ DBDraw | SpellDescription$ Each player who controls a permanent named Bonder's Ornament draws a card.
+SVar:DBDraw:DB$ Draw | NumCards$ 1 | Defined$ Player.IsRemembered | ConditionCheckSVar$ OrnCheck | ConditionSVarCompare$ GE1 | References$ OrnCheck
+SVar:OrnCheck:PlayerCountRemembered$Valid Permanent.namedBonder's Ornament+RememberedPlayerCtrl
+Oracle:{T}: Add one mana of any color.\n{4}, {T}: Each player who controls a permanent named Bonder's Ornament draws a card.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/boneyard_mycodrax.txt b/forge-gui/res/cardsfolder/upcoming/C2020/boneyard_mycodrax.txt
new file mode 100755
index 00000000000..11e8e11f844
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/boneyard_mycodrax.txt
@@ -0,0 +1,8 @@
+Name:Boneyard Mycodrax
+ManaCost:2 B
+Types:Creature Fungus
+PT:*/*
+S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | References$ X | Description$ CARDNAME's power and toughness are each equal to the number of other creature cards in your graveyard.
+SVar:X:Count$ValidGraveyard Creature.Other+YouOwn
+K:Scavenge:4 B
+Oracle:Boneyard Mycodrax's power and toughness are each equal to the number of other creature cards in your graveyard.\nScavenge {4}{B} ({4}{B}, Exile this card from your graveyard: Put a number of +1/+1 counters equal to this card's power on target creature. Scavenge only as a sorcery.)
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/brallin_skyshark_rider.txt b/forge-gui/res/cardsfolder/upcoming/C2020/brallin_skyshark_rider.txt
new file mode 100755
index 00000000000..2177850ee31
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/brallin_skyshark_rider.txt
@@ -0,0 +1,11 @@
+Name:Brallin, Skyshark Rider
+ManaCost:3 R
+Types:Legendary Creature Human Shaman
+PT:3/3
+K:Partner:Shabraz, the Skyshark:Shabraz
+T:Mode$ Discarded | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you discard a card, put a +1/+1 counter on CARDNAME and it deals 1 damage to each opponent.
+SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBDealDamage
+SVar:DBDealDamage:DB$ DealDamage | Defined$ Player.Opponent | NumDmg$ 1
+A:AB$ Pump | Cost$ R | ValidTgts$ Shark | TgtPrompt$ Select target Shark | KW$ Trample | SpellDescription$ Target Shark gains trample until end of turn.
+DeckHas:Ability$Counters
+Oracle:Partner with Shabraz, the Skyshark (When this creature enters the battlefield, target player may put Shabraz into their hand from their library, then shuffle.)\nWhenever you discard a card, put a +1/+1 counter on Brallin, Skyshark Rider and it deals 1 damage to each opponent.\n{R}: Target Shark gains trample until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/cazur_ruthless_stalker.txt b/forge-gui/res/cardsfolder/upcoming/C2020/cazur_ruthless_stalker.txt
new file mode 100755
index 00000000000..a4c8729979b
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/cazur_ruthless_stalker.txt
@@ -0,0 +1,9 @@
+Name:Cazur, Ruthless Stalker
+ManaCost:3 G
+Types:Legendary Creature Human Warrior
+PT:3/3
+K:Flying
+K:Partner:Ukkima, Stalking Shadow:Ukkima
+T:Mode$ DamageDone | ValidSource$ Creature.YouCtrl | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever a creature you control deals combat damage to a player, put a +1/+1 counter on that creature.
+SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredSourceLKICopy | CounterType$ P1P1 | CounterNum$ 1
+Oracle:Partner with Ukkima, Stalking Shadow (When this creature enters the battlefield, target player may put Ukkima into their hand from their library, then shuffle.)\nWhenever a creature you control deals combat damage to a player, put a +1/+1 counter on that creature.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/curious_herd.txt b/forge-gui/res/cardsfolder/upcoming/C2020/curious_herd.txt
new file mode 100755
index 00000000000..cd4e820631d
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/curious_herd.txt
@@ -0,0 +1,8 @@
+Name:Curious Herd
+ManaCost:3 G
+Types:Instant
+A:SP$ Pump | Cost$ 3 G | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | Choices$ Player | SubAbility$ DBToken | StackDescription$ SpellDescription | SpellDescription$ Choose target opponent. You create X 3/3 green Beast creature tokens, where X is the number of artifacts that player controls.
+SVar:DBToken:DB$ Token | TokenAmount$ X | References$ X | TokenScript$ g_3_3_beast | TokenOwner$ You
+SVar:X:TargetedPlayer$Valid Artifact.YouCtrl
+DeckHas:Ability$Token
+Oracle:Choose target opponent. You create X 3/3 green Beast creature tokens, where X is the number of artifacts that player controls.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/decoy_gambit.txt b/forge-gui/res/cardsfolder/upcoming/C2020/decoy_gambit.txt
new file mode 100755
index 00000000000..e2312d5669d
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/decoy_gambit.txt
@@ -0,0 +1,11 @@
+Name:Decoy Gambit
+ManaCost:2 U
+Types:Instant
+A:SP$ Pump | Cost$ 2 U | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Choose up to one target creature each opponent controls | TargetMin$ 0 | TargetMax$ OneEach | References$ OneEach | TargetsWithDifferentControllers$ True | RememberObjects$ Targeted | SubAbility$ DBRepeat | SpellDescription$ For each opponent, choose up to one target creature that player controls, then return that creature to its owner's hand unless its controller has you draw a card.
+SVar:DBRepeat:DB$ RepeatEach | RepeatCards$ Card.IsRemembered | UseImprinted$ True | RepeatSubAbility$ DBReturn | SubAbility$ DBCleanup
+SVar:DBReturn:DB$ ChangeZone | Defined$ Imprinted | Origin$ Battlefield | Destination$ Hand | UnlessCost$ Draw<1/Player.IsRemembered> | UnlessPayer$ ImprintedController
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
+T:Mode$ SpellCast | ValidCard$ Card.Self | Execute$ TrigRemember | Static$ True
+SVar:TrigRemember:DB$ Pump | RememberObjects$ TriggeredActivator
+SVar:OneEach:PlayerCountOpponents$Amount
+Oracle:For each opponent, choose up to one target creature that player controls, then return that creature to its owner's hand unless its controller has you draw a card.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/jirina_kudro.txt b/forge-gui/res/cardsfolder/upcoming/C2020/jirina_kudro.txt
new file mode 100755
index 00000000000..5f91feb2e00
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/jirina_kudro.txt
@@ -0,0 +1,11 @@
+Name:Jirina Kudro
+ManaCost:1 R W B
+Types:Legendary Creature Human Soldier
+PT:3/3
+T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a 1/1 white Human Soldier creature token for each time you've cast a commander from the command zone this game.
+SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ w_1_1_human | TokenOwner$ You | LegacyImage$ w 1 1 human c20 | References$ X
+SVar:X:Count$TotalCommanderCastFromCommandZone
+S:Mode$ Continuous | Affected$ Human.Other+YouCtrl | AddPower$ 2 | Description$ Other Humans you control get +2/+0.
+SVar:PlayMain1:TRUE
+DeckHints:Type$Human
+Oracle:When Jirina Kudro enters the battlefield, create a 1/1 white Human Soldier creature token for each time you've cast a commander from the command zone this game.\nOther Humans you control get +2/+0.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/kalamax_the_stormsire.txt b/forge-gui/res/cardsfolder/upcoming/C2020/kalamax_the_stormsire.txt
new file mode 100755
index 00000000000..ac646c3d73a
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/kalamax_the_stormsire.txt
@@ -0,0 +1,10 @@
+Name:Kalamax, the Stormsire
+ManaCost:1 G U R
+Types:Legendary Creature Elemental Dinosaur
+PT:4/4
+T:Mode$ SpellCast | ValidCard$ Instant | ValidActivatingPlayer$ You | ActivatorThisTurnCast$ EQ1 | NoResolvingCheck$ True | Execute$ TrigCopy | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast your first instant spell each turn, if CARDNAME is tapped, copy that spell. You may choose new targets for the copy.
+SVar:TrigCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | IsPresent$ Card.Self+tapped | AILogic$ Always
+SVar:BuffedBy:Instant
+T:Mode$ SpellCopy | ValidCard$ Instant | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you copy an instant spell, put a +1/+1 counter on CARDNAME.
+SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
+Oracle:Whenever you cast your first instant spell each turn, if Kalamax, the Stormsire is tapped, copy that spell. You may choose new targets for the copy.\nWhenever you copy an instant spell, put a +1/+1 counter on Kalamax.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/shabraz_the_skyshark.txt b/forge-gui/res/cardsfolder/upcoming/C2020/shabraz_the_skyshark.txt
new file mode 100755
index 00000000000..6530b1de08f
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/shabraz_the_skyshark.txt
@@ -0,0 +1,13 @@
+Name:Shabraz, the Skyshark
+ManaCost:3 W U
+Types:Legendary Creature Shark Bird
+PT:3/3
+K:Partner:Brallin, Skyshark Rider:Brallin
+K:Flying
+T:Mode$ Drawn | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you draw a card, put a +1/+1 counter on CARDNAME and you gain 1 life.
+SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBGainLife
+SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1
+A:AB$ Pump | Cost$ WU | ValidTgts$ Human | TgtPrompt$ Select target Human | KW$ Flying | SpellDescription$ Target Human gains flying until end of turn.
+DeckHints:Type$Human
+DeckHas:Ability$Counters
+Oracle:Partner with Brallin, Skyshark Rider\nFlying\nWhenever you draw a card, put a +1/+1 counter on Shabraz, the Skyshark and you gain 1 life.\n{W/U}: Target Human gains flying until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/C2020/ukkima_stalking_shadow.txt b/forge-gui/res/cardsfolder/upcoming/C2020/ukkima_stalking_shadow.txt
new file mode 100755
index 00000000000..efc61a2fd3b
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/C2020/ukkima_stalking_shadow.txt
@@ -0,0 +1,11 @@
+Name:Ukkima, Stalking Shadow
+ManaCost:1 U B
+Types:Legendary Creature Whale Wolf
+PT:2/2
+K:Partner:Cazur, Ruthless Stalker:Cazur
+K:Unblockable
+T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigDealDamage | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, it deals X damage to target player and you gain X life, where X is its power.
+SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Player | TgtPrompt$ Select target player | NumDmg$ X | References$ X | SubAbility$ DBGainLife
+SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X | References$ X
+SVar:X:Count$CardPower
+Oracle:Partner with Cazur, Ruthless Stalker (When this creature enters the battlefield, target player may put Cazur into their hand from their library, then shuffle.)\nUkkima, Stalking Shadow can't be blocked.\nWhen Ukkima leaves the battlefield, it deals X damage to target player and you gain X life, where X is its power.
diff --git a/forge-gui/res/cardsfolder/upcoming/aegis_turtle.txt b/forge-gui/res/cardsfolder/upcoming/aegis_turtle.txt
new file mode 100755
index 00000000000..d56b7bf35e3
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/aegis_turtle.txt
@@ -0,0 +1,5 @@
+Name:Aegis Turtle
+ManaCost:U
+Types:Creature Turtle
+PT:0/5
+Oracle:
diff --git a/forge-gui/res/cardsfolder/upcoming/alert_heedbonder.txt b/forge-gui/res/cardsfolder/upcoming/alert_heedbonder.txt
new file mode 100755
index 00000000000..7ab589a7a6b
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/alert_heedbonder.txt
@@ -0,0 +1,9 @@
+Name:Alert Heedbonder
+ManaCost:1 G/W G/W
+Types:Creature Human Scout
+PT:2/4
+K:Vigilance
+T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ At the beginning of your end step, you gain 1 life for each creature you control with vigilance.
+SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X | References$ X
+SVar:X:Count$Valid Creature.YouCtrl+withVigilance
+Oracle:Vigilance\nAt the beginning of your end step, you gain 1 life for each creature you control with vigilance.
diff --git a/forge-gui/res/cardsfolder/upcoming/almighty_brushwagg.txt b/forge-gui/res/cardsfolder/upcoming/almighty_brushwagg.txt
new file mode 100755
index 00000000000..b570a53e9cf
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/almighty_brushwagg.txt
@@ -0,0 +1,7 @@
+Name:Almighty Brushwagg
+ManaCost:G
+Types:Creature Brushwagg
+PT:1/1
+K:Trample
+A:AB$ Pump | Cost$ 3 G | NumAtt$ +3 | NumDef$ +3 | SpellDescription$ CARDNAME gets +3/+3 until end of turn.
+Oracle:Trample\n{3}{G}: Almighty Brushwagg gets +3/+3 until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/avian_oddity.txt b/forge-gui/res/cardsfolder/upcoming/avian_oddity.txt
new file mode 100755
index 00000000000..b57e59187df
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/avian_oddity.txt
@@ -0,0 +1,9 @@
+Name:Avian Oddity
+ManaCost:3 U
+Types:Creature Bird
+PT:2/4
+K:Flying
+K:Cycling:2 U
+T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When you cycle CARDNAME, put a flying counter on target creature you control.
+SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ FLYING | CounterNum$ 1
+Oracle:Flying\nCycling {2}{U} ({2}{U}, Discard this card: Draw a card.)\nWhen you cycle Avian Oddity, put a flying counter on target creature you control.
diff --git a/forge-gui/res/cardsfolder/upcoming/back_for_more.txt b/forge-gui/res/cardsfolder/upcoming/back_for_more.txt
new file mode 100755
index 00000000000..368297d8182
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/back_for_more.txt
@@ -0,0 +1,6 @@
+Name:Back for More
+ManaCost:4 B G
+Types:Instant
+A:SP$ ChangeZone | Cost$ 4 B G | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouOwn | SubAbility$ DBFight | StackDescription$ SpellDescription | SpellDescription$ Return target creature card from your graveyard to the battlefield. When you do, it fights up to one target creature you don't control.
+SVar:DBFight:DB$ Fight | Defined$ ParentTarget | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Choose target creature you don't control | StackDescription$ None
+Oracle:Return target creature card from your graveyard to the battlefield. When you do, it fights up to one target creature you don't control. (Each deals damage equal to its power to the other.)
diff --git a/forge-gui/res/cardsfolder/upcoming/barrier_breach.txt b/forge-gui/res/cardsfolder/upcoming/barrier_breach.txt
new file mode 100755
index 00000000000..03669901607
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/barrier_breach.txt
@@ -0,0 +1,6 @@
+Name:Barrier Breach
+ManaCost:2 G
+Types:Instant
+A:SP$ ChangeZone | Cost$ 2 G | ValidTgts$ Enchantment | TargetMin$ 0 | TargetMax$ 3 | TgtPrompt$ Select target enchantment | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile up to three target enchantments.
+K:Cycling:2
+Oracle:Exile up to three target enchantments.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/bastion_of_remembrance.txt b/forge-gui/res/cardsfolder/upcoming/bastion_of_remembrance.txt
new file mode 100755
index 00000000000..8d6bbae56fe
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/bastion_of_remembrance.txt
@@ -0,0 +1,9 @@
+Name:Bastion of Remembrance
+ManaCost:2 B
+Types:Enchantment
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a 1/1 white Human Soldier creature token.
+SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_human_soldier | TokenOwner$ You | LegacyImage$ w 1 1 human soldier iko
+T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever a creature you control dies, each opponent loses 1 life and you gain 1 life.
+SVar:TrigLoseLife:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 1 | SubAbility$ DBGainLife
+SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1
+Oracle:When Bastion of Remembrance enters the battlefield, create a 1/1 white Human Soldier creature token.\nWhenever a creature you control dies, each opponent loses 1 life and you gain 1 life.
diff --git a/forge-gui/res/cardsfolder/upcoming/blade_banish.txt b/forge-gui/res/cardsfolder/upcoming/blade_banish.txt
new file mode 100755
index 00000000000..34c3bfa4551
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/blade_banish.txt
@@ -0,0 +1,5 @@
+Name:Blade Banish
+ManaCost:3 W
+Types:Instant
+A:SP$ ChangeZone | Cost$ 3 W | ValidTgts$ Creature.powerGE4 | TgtPrompt$ Select target creature with power 4 or greater | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target creature with power 4 or greater.
+Oracle:Exile target creature with power 4 or greater.
diff --git a/forge-gui/res/cardsfolder/upcoming/blisterspit_gremlin.txt b/forge-gui/res/cardsfolder/upcoming/blisterspit_gremlin.txt
new file mode 100755
index 00000000000..0250d0797b0
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/blisterspit_gremlin.txt
@@ -0,0 +1,9 @@
+Name:Blisterspit Gremlin
+ManaCost:R
+Types:Creature Gremlin
+PT:1/1
+A:AB$ DealDamage | Cost$ 1 T | NumDmg$ 1 | Defined$ Player.Opponent | SpellDescription$ CARDNAME deals 1 damage to each opponent.
+T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigUntap | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, untap CARDNAME.
+SVar:TrigUntap:DB$ Untap | Defined$ Self
+SVar:BuffedBy:Card.nonLand+nonCreature
+Oracle:{1}, {T}: Blisterspit Gremlin deals 1 damage to each opponent.\nWhenever you cast a noncreature spell, untap Blisterspit Gremlin.
diff --git a/forge-gui/res/cardsfolder/upcoming/blitz_leech.txt b/forge-gui/res/cardsfolder/upcoming/blitz_leech.txt
new file mode 100755
index 00000000000..de9e02ce0c9
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/blitz_leech.txt
@@ -0,0 +1,11 @@
+Name:Blitz Leech
+ManaCost:5 B
+Types:Creature Leech
+PT:5/2
+K:Flash
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ When CARDNAME enters the battlefield, target creature an opponent controls gets -2/-2 until end of turn. Remove all counters from that creature.
+SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | NumAtt$ -2 | NumDef$ -2 | IsCurse$ True | SubAbility$ DBRemoveCounter
+SVar:DBRemoveCounter:DB$ RemoveCounter | Defined$ Targeted | CounterType$ All | CounterNum$ All | StackDescription$ None
+SVar:NeedsToPlayVar:Z GE1
+SVar:Z:Count$Valid Creature.OppCtrl+inZoneBattlefield
+Oracle:Flash\nWhen Blitz Leech enters the battlefield, target creature an opponent controls gets -2/-2 until end of turn. Remove all counters from that creature.
diff --git a/forge-gui/res/cardsfolder/upcoming/blitz_of_the_thunder_raptor.txt b/forge-gui/res/cardsfolder/upcoming/blitz_of_the_thunder_raptor.txt
new file mode 100755
index 00000000000..c4cc0e963b2
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/blitz_of_the_thunder_raptor.txt
@@ -0,0 +1,6 @@
+Name:Blitz of the Thunder-Raptor
+ManaCost:1 R
+Types:Instant
+A:SP$ DealDamage | Cost$ 1 R | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker | NumDmg$ X | References$ X | ReplaceDyingDefined$ Targeted | SpellDescription$ CARDNAME deals damage to target creature equal to the number of instant or sorcery cards in your graveyard. If that creature or planeswalker would die this turn, exile it instead.
+SVar:X:Count$ValidGraveyard Instant.YouOwn,Sorcery.YouOwn
+Oracle:Blitz of the Thunder-Raptor deals damage to target creature or planeswalker equal to the number of instant or sorcery cards in your graveyard. If that creature or planeswalker would die this turn, exile it instead.
diff --git a/forge-gui/res/cardsfolder/upcoming/blood_curdle.txt b/forge-gui/res/cardsfolder/upcoming/blood_curdle.txt
new file mode 100755
index 00000000000..386a96f9a2a
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/blood_curdle.txt
@@ -0,0 +1,6 @@
+Name:Blood Curdle
+ManaCost:3 B
+Types:Instant
+A:SP$ Destroy | Cost$ 3 B | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBPutCounter | StackDescription$ SpellDescription | SpellDescription$ Destroy target creature. Put a menace counter on a creature you control.
+SVar:DBPutCounter:DB$ PutCounter | Choices$ Creature.YouCtrl | CounterType$ MENACE | CounterNum$ 1
+Oracle:Destroy target creature. Put a menace counter on a creature you control. (It can't be blocked except by two or more creatures.)
diff --git a/forge-gui/res/cardsfolder/upcoming/bonders_enclave.txt b/forge-gui/res/cardsfolder/upcoming/bonders_enclave.txt
new file mode 100755
index 00000000000..c6600c070e3
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/bonders_enclave.txt
@@ -0,0 +1,6 @@
+Name:Bonders' Enclave
+ManaCost:no cost
+Types:Land
+A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
+A:AB$ Draw | Cost$ 3 T | NumCards$ 1 | IsPresent$ Creature.YouCtrl+powerGE4 | SpellDescription$ Draw a card. Activate this ability only if you control a creature with power 4 or greater.
+Oracle:{T}: Add {C}.\n{3}, {T}: Draw a card. Activate this ability only if you control a creature with power 4 or greater.
diff --git a/forge-gui/res/cardsfolder/upcoming/boon_of_the_wish_giver.txt b/forge-gui/res/cardsfolder/upcoming/boon_of_the_wish_giver.txt
new file mode 100755
index 00000000000..34f8037b47a
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/boon_of_the_wish_giver.txt
@@ -0,0 +1,6 @@
+Name:Boon of the Wish-Giver
+ManaCost:4 U U
+Types:Sorcery
+A:SP$ Draw | Cost$ 4 U U | NumCards$ 4 | SpellDescription$ Draw four cards.
+K:Cycling:1
+Oracle:Draw four cards.\nCycling {1} ({1}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/boot_nipper.txt b/forge-gui/res/cardsfolder/upcoming/boot_nipper.txt
new file mode 100755
index 00000000000..7fc399ea046
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/boot_nipper.txt
@@ -0,0 +1,10 @@
+Name:Boot Nipper
+ManaCost:1 B
+Types:Creature Beast
+PT:2/1
+K:ETBReplacement:Other:CounterChoice
+SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Deathtouch,Lifelink | SpellDescription$ CARDNAME enters the battlefield with your choice of a flying counter or a hexproof counter on it.
+SVar:Deathtouch:DB$ PutCounter | CounterType$ DEATHTOUCH | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a deathtouch counter on it
+SVar:Lifelink:DB$ PutCounter | CounterType$ LIFELINK | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a lifelink counter on it
+DeckHints:Ability$Counters
+Oracle:Boot Nipper enters the battlefield with your choice of a deathtouch counter or a lifelink counter on it.
diff --git a/forge-gui/res/cardsfolder/upcoming/bushmeat_poacher.txt b/forge-gui/res/cardsfolder/upcoming/bushmeat_poacher.txt
new file mode 100755
index 00000000000..397800d2468
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/bushmeat_poacher.txt
@@ -0,0 +1,8 @@
+Name:Bushmeat Poacher
+ManaCost:3 B
+Types:Creature Human Soldier
+PT:2/4
+A:AB$ GainLife | Cost$ 1 T Sac<1/Creature.Other/another creature> | Defined$ You | LifeAmount$ X | References$ X | SubAbility$ DBDraw | SpellDescription$ You gain life equal to that creature's toughness. Draw a card.
+SVar:DBDraw:DB$ Draw | NumCards$ 1
+SVar:X:Sacrificed$CardToughness
+Oracle:{1}, {T}, Sacrifice another creature: You gain life equal to that creature's toughness. Draw a card.
diff --git a/forge-gui/res/cardsfolder/upcoming/call_of_the_death_dweller.txt b/forge-gui/res/cardsfolder/upcoming/call_of_the_death_dweller.txt
new file mode 100755
index 00000000000..a4635e3117c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/call_of_the_death_dweller.txt
@@ -0,0 +1,8 @@
+Name:Call of the Death-Dweller
+ManaCost:2 B
+Types:Sorcery
+A:SP$ ChangeZone | Cost$ 2 B | Origin$ Graveyard | Destination$ Battlefield | TargetMin$ 0 | TargetMax$ 2 | MaxTotalTargetCMC$ 3 | ValidTgts$ Creature.YouOwn | TgtPrompt$ Select up to two target creature cards with total converted mana cost 3 or less | SubAbility$ DBPutCounter | RememberChanged$ True | StackDescription$ SpellDescription | SpellDescription$ Return up to two target creature cards with total converted mana cost 3 or less from your graveyard to the battlefield. Put a deathtouch counter on either of them. Then put a menace counter on either of them.
+SVar:DBPutCounter:DB$ PutCounter | Choices$ Creature.IsRemembered | ChoiceTitle$ Choose a creature to put a deathtouch counter on | CounterType$ DEATHTOUCH | CounterNum$ 1 | SubAbility$ DBPutCounter2 | StackDescription$ None
+SVar:DBPutCounter2:DB$ PutCounter | Choices$ Creature.IsRemembered | ChoiceTitle$ Choose a creature to put a menace counter on | CounterType$ MENACE | CounterNum$ 1 | SubAbility$ DBCleanup | StackDescription$ None
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
+Oracle:Return up to two target creature cards with total converted mana cost 3 or less from your graveyard to the battlefield. Put a deathtouch counter on either of them. Then put a menace counter on either of them.
diff --git a/forge-gui/res/cardsfolder/upcoming/channeled_force.txt b/forge-gui/res/cardsfolder/upcoming/channeled_force.txt
new file mode 100755
index 00000000000..3d6aae1c6bf
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/channeled_force.txt
@@ -0,0 +1,7 @@
+Name:Channeled Force
+ManaCost:2 U R
+Types:Instant
+A:SP$ Draw | Cost$ 2 U R Discard | CostDesc$ As an additional cost to cast this spell, discard X cards. | NumCards$ ChosenX | ValidTgts$ Player | TgtPrompt$ Choose a player | References$ X | SubAbility$ DBDamage | SpellDescription$ Target player draws X cards. CARDNAME deals X damage to up to one target creature or planeswalker.
+SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature,Planeswalker | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select target creature or planeswalker. | NumDmg$ ChosenX | References$ X
+SVar:X:XChoice
+Oracle:As an additional cost to cast this spell, discard X cards.\nTarget player draws X cards. Channeled Force deals X damage to up to one target creature or planeswalker.
diff --git a/forge-gui/res/cardsfolder/upcoming/charge_of_the_forever_beast.txt b/forge-gui/res/cardsfolder/upcoming/charge_of_the_forever_beast.txt
new file mode 100755
index 00000000000..e72b1faf332
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/charge_of_the_forever_beast.txt
@@ -0,0 +1,7 @@
+Name:Charge of the Forever-Beast
+ManaCost:2 G
+Types:Sorcery
+A:SP$ DealDamage | Cost$ 2 G Reveal<1/Creature> | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker | NumDmg$ X | References$ X | SpellDescription$ As an additional cost to cast this spell, reveal a creature card from your hand. CARDNAME deals damage to target creature or planeswalker equal to the revealed card's power.
+SVar:X:Revealed$CardPower
+AI:RemoveDeck:All
+Oracle:As an additional cost to cast this spell, reveal a creature card from your hand.\nCharge of the Forever-Beast deals damage to target creature or planeswalker equal to the revealed card's power.
diff --git a/forge-gui/res/cardsfolder/upcoming/checkpoint_officer.txt b/forge-gui/res/cardsfolder/upcoming/checkpoint_officer.txt
new file mode 100755
index 00000000000..6b10c88adc8
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/checkpoint_officer.txt
@@ -0,0 +1,7 @@
+Name:Checkpoint Officer
+ManaCost:1 W
+Types:Creature Human Soldier
+PT:1/2
+A:AB$ Tap | Cost$ 1 W T | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Tap target creature.
+SVar:NonCombatPriority:5
+Oracle:{1}{W}, {T}: Tap target creature.
diff --git a/forge-gui/res/cardsfolder/upcoming/chevill_bane_of_monsters.txt b/forge-gui/res/cardsfolder/upcoming/chevill_bane_of_monsters.txt
new file mode 100755
index 00000000000..f2a537b3240
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/chevill_bane_of_monsters.txt
@@ -0,0 +1,13 @@
+Name:Chevill, Bane of Monsters
+ManaCost:B G
+Types:Legendary Creature Human Rogue
+PT:1/3
+K:Deathtouch
+T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | IsPresent$ Permanent.OppCtrl+counters_GE1_BOUNTY | PresentCompare$ EQ0 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of your upkeep, if your opponents control no permanents with bounty counters on them, put a bounty counter on target creature or planeswalker an opponent controls.
+SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.OppCtrl,Planeswalker.OppCtrl | TgtPrompt$ Select target creature or planeswalker an opponent controls | CounterType$ BOUNTY | CounterNum$ 1 | IsCurse$ True
+T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Permanent.OppCtrl+counters_GE1_BOUNTY | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever a permanent an opponent controls with a bounty counter on it dies, you gain 3 life and draw a card.
+SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 3 | SubAbility$ DBDraw
+SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1
+SVar:PlayMain1:TRUE
+DeckHints:Ability$Counters
+Oracle:Deathtouch\nAt the beginning of your upkeep, if your opponents control no permanents with bounty counters on them, put a bounty counter on target creature or planeswalker an opponent controls.\nWhenever a permanent an opponent controls with a bounty counter on it dies, you gain 3 life and draw card.
diff --git a/forge-gui/res/cardsfolder/upcoming/clash_of_titans.txt b/forge-gui/res/cardsfolder/upcoming/clash_of_titans.txt
new file mode 100644
index 00000000000..1ce323436f1
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/clash_of_titans.txt
@@ -0,0 +1,5 @@
+Name:Clash of Titans
+ManaCost:3 R R
+Types:Instant
+A:SP$ Fight | Cost$ 3 R R | ValidTgts$ Creature | TgtPrompt$ Select target creatures | TargetMin$ 2 | TargetMax$ 2 | SpellDescription$ Target creature fights another target creature. (Each creature deals damage equal to its power to the other.)
+Oracle:Target creature fights another target creature. (Each creature deals damage equal to its power to the other.)
diff --git a/forge-gui/res/cardsfolder/upcoming/colossification.txt b/forge-gui/res/cardsfolder/upcoming/colossification.txt
new file mode 100755
index 00000000000..3ebd87ad154
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/colossification.txt
@@ -0,0 +1,9 @@
+Name:Colossification
+ManaCost:5 G G
+Types:Enchantment Aura
+K:Enchant creature
+A:SP$ Attach | Cost$ 5 G G | ValidTgts$ Creature | AILogic$ Pump
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigTap | TriggerDescription$ When CARDNAME enters the battlefield, tap enchanted creature.
+SVar:TrigTap:DB$ Tap | Defined$ Enchanted
+S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 20 | AddToughness$ 20 | Description$ Enchanted creature gets +20/+20.
+Oracle:Enchant creature\nWhen Colossification enters the battlefield, tap enchanted creature.\nEnchanted creature gets +20/+20.
diff --git a/forge-gui/res/cardsfolder/upcoming/coordinated_charge.txt b/forge-gui/res/cardsfolder/upcoming/coordinated_charge.txt
new file mode 100755
index 00000000000..76fa65863e7
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/coordinated_charge.txt
@@ -0,0 +1,6 @@
+Name:Coordinated Charge
+ManaCost:4 W
+Types:Instant
+A:SP$ PumpAll | Cost$ 4 W | ValidCards$ Creature.YouCtrl | NumAtt$ +2 | NumDef$ +1 | SpellDescription$ Creatures you control get +2/+1 until end of turn.
+K:Cycling:2
+Oracle:Creatures you control get +2/+1 until end of turn.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/crystacean.txt b/forge-gui/res/cardsfolder/upcoming/crystacean.txt
new file mode 100755
index 00000000000..b80f6750440
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/crystacean.txt
@@ -0,0 +1,6 @@
+Name:Crystacean
+ManaCost:3 U
+Types:Creature Crab
+PT:1/6
+K:Flash
+Oracle:Flash
diff --git a/forge-gui/res/cardsfolder/upcoming/crystalline_giant.txt b/forge-gui/res/cardsfolder/upcoming/crystalline_giant.txt
new file mode 100755
index 00000000000..06cc2104249
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/crystalline_giant.txt
@@ -0,0 +1,19 @@
+Name:Crystalline Giant
+ManaCost:3
+Types:Artifact Creature Giant
+PT:3/3
+T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | Execute$ TrigGenericChoice | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of combat on your turn, choose a kind of counter at random that CARDNAME doesn’t have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on CARDNAME.
+SVar:TrigGenericChoice:DB$ GenericChoice | AtRandom$ True | Choices$ Flying,FirstStrike,Deathtouch,Hexproof,Lifelink,Menace,Reach,Trample,Vigilance,P1P1
+SVar:Flying:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_FLYING | RememberCards$ True | CounterType$ FLYING | CounterNum$ 1 | SpellDescription$ FLY
+SVar:FirstStrike:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_FIRSTSTRIKE | RememberCards$ True | CounterType$ FIRSTSTRIKE | CounterNum$ 1 | SpellDescription$ FIR
+SVar:Deathtouch:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_DEATHTOUCH | RememberCards$ True | CounterType$ DEATHTOUCH | CounterNum$ 1 | SpellDescription$ DEA
+SVar:Hexproof:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_HEXPROOF | RememberCards$ True | CounterType$ HEXPROOF | CounterNum$ 1 | SpellDescription$ HEX
+SVar:Lifelink:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_LIFELINK | RememberCards$ True | CounterType$ LIFELINK | CounterNum$ 1 | SpellDescription$ LIF
+SVar:Menace:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_MENACE | RememberCards$ True | CounterType$ MENACE | CounterNum$ 1 | SpellDescription$ MEN
+SVar:Reach:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_REACH | RememberCards$ True | CounterType$ REACH | CounterNum$ 1 | SpellDescription$ REA
+SVar:Trample:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_TRAMPLE | RememberCards$ True | CounterType$ TRAMPLE | CounterNum$ 1 | SpellDescription$ TRA
+SVar:Vigilance:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_VIGILANCE | RememberCards$ True | CounterType$ VIGILANCE | CounterNum$ 1 | SpellDescription$ VIG
+SVar:P1P1:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_P1P1 | RememberCards$ True | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ P1P1
+SVar:PlayMain1:TRUE
+DeckHas:Ability$Counters
+Oracle:At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn’t have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.
diff --git a/forge-gui/res/cardsfolder/upcoming/cunning_nightbonder.txt b/forge-gui/res/cardsfolder/upcoming/cunning_nightbonder.txt
new file mode 100755
index 00000000000..07a2a4a4b4f
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/cunning_nightbonder.txt
@@ -0,0 +1,8 @@
+Name:Cunning Nightbonder
+ManaCost:U/B U/B
+Types:Creature Human Rogue
+PT:2/2
+K:Flash
+S:Mode$ ReduceCost | ValidCard$ Card.hasKeywordFlash | Type$ Spell | Activator$ You | Amount$ 1 | Description$ Spells you cast with flash cost {1} less to cast and can't be countered.
+S:Mode$ Continuous | Affected$ Card.hasKeywordFlash+YouCtrl | AffectedZone$ Stack | AddHiddenKeyword$ CARDNAME can't be countered. | Secondary$ True | Description$ Spells you cast with flash cost {1} less to cast and can't be countered.
+Oracle:Flash\nSpells you cast with flash cost {1} less to cast and can't be countered.
diff --git a/forge-gui/res/cardsfolder/upcoming/daysquad_marshal.txt b/forge-gui/res/cardsfolder/upcoming/daysquad_marshal.txt
new file mode 100755
index 00000000000..449063bc772
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/daysquad_marshal.txt
@@ -0,0 +1,8 @@
+Name:Daysquad Marshal
+ManaCost:3 W
+Types:Creature Human Soldier
+PT:3/3
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a 1/1 white Human Soldier creature token.
+SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_human_soldier | TokenOwner$ You
+DeckHas:Ability$Token
+Oracle:When Daysquad Marshal enters the battlefield, create a 1/1 white Human Soldier creature token.
diff --git a/forge-gui/res/cardsfolder/upcoming/deaths_oasis.txt b/forge-gui/res/cardsfolder/upcoming/deaths_oasis.txt
new file mode 100755
index 00000000000..c0ee84512b7
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/deaths_oasis.txt
@@ -0,0 +1,10 @@
+Name:Death's Oasis
+ManaCost:W B G
+Types:Enchantment
+T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouCtrl+nonToken | TriggerZones$ Battlefield | Execute$ TrigMill | TriggerDescription$ Whenever a nontoken creature you control dies, put the top two cards of your library into your graveyard. Then return a creature card with lesser converted mana cost than the creature that died from the graveyard to your hand.
+SVar:TrigMill:DB$ Mill | NumCards$ 2 | Defined$ You | SubAbility$ DBReturn
+SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ChangeType$ Creature.YouOwn+cmcLTY | References$ Y | Hidden$ True | ChangeNum$ 1
+SVar:Y:TriggeredCard$CardManaCost
+A:AB$ GainLife | Cost$ 1 Sac<1/CARDNAME> | LifeAmount$ X | References$ X | SpellDescription$ You gain life equal to the greatest converted mana cost among creatures you control.
+SVar:X:Count$HighestCMC_Creature.YouCtrl+inZoneBattlefield
+Oracle:Whenever a nontoken creature you control dies, put the top two cards of your library into your graveyard. Then return a creature card with lesser converted mana cost than the creature that died from the graveyard to your hand.\n{1}, Sacrifice Death's Oasis: You gain life equal to the greatest converted mana cost among creatures you control.
diff --git a/forge-gui/res/cardsfolder/upcoming/dire_tactics.txt b/forge-gui/res/cardsfolder/upcoming/dire_tactics.txt
new file mode 100755
index 00000000000..e9fde96bc74
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/dire_tactics.txt
@@ -0,0 +1,7 @@
+Name:Dire Tactics
+ManaCost:W B
+Types:Instant
+A:SP$ ChangeZone | Cost$ W B | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBLoseLife | StackDescription$ SpellDescription | SpellDescription$ Exile target creature. If you don't control a Human, you lose life equal to that creature's toughness.
+SVar:DBLoseLife:DB$ LoseLife | ConditionPresent$ Human.YouCtrl | ConditionCompare$ EQ0 | Defined$ You | LifeAmount$ X | References$ X | StackDescription$ None
+SVar:X:Targeted$CardToughness
+Oracle:Exile target creature. If you don't control a Human, you lose life equal to that creature's toughness.
diff --git a/forge-gui/res/cardsfolder/upcoming/drannith_healer.txt b/forge-gui/res/cardsfolder/upcoming/drannith_healer.txt
new file mode 100755
index 00000000000..43dddc36fab
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/drannith_healer.txt
@@ -0,0 +1,8 @@
+Name:Drannith Healer
+ManaCost:1 W
+Types:Creature Human Cleric
+PT:2/2
+T:Mode$ Cycled | ValidCard$ Card.Other+YouOwn | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever you cycle another card, you gain 1 life.
+SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1
+K:Cycling:1
+Oracle:Whenever you cycle another card, you gain 1 life.\nCycling {1} ({1}, Discard a card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/drannith_magistrate.txt b/forge-gui/res/cardsfolder/upcoming/drannith_magistrate.txt
new file mode 100755
index 00000000000..c4121042a56
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/drannith_magistrate.txt
@@ -0,0 +1,6 @@
+Name:Drannith Magistrate
+ManaCost:1 W
+Types:Creature Human Wizard
+PT:1/3
+S:Mode$ CantBeCast | ValidCard$ Card | Caster$ Opponent | Origin$ Library,Graveyard,Exile | Description$ Your opponents can't cast spells from anywhere other than their hands.
+Oracle:Your opponents can't cast spells from anywhere other than their hands.
diff --git a/forge-gui/res/cardsfolder/upcoming/drannith_stinger.txt b/forge-gui/res/cardsfolder/upcoming/drannith_stinger.txt
new file mode 100755
index 00000000000..3dfa1fd988d
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/drannith_stinger.txt
@@ -0,0 +1,8 @@
+Name:Drannith Stinger
+ManaCost:1 R
+Types:Creature Human Wizard
+PT:2/2
+T:Mode$ Cycled | ValidCard$ Card.Other+YouOwn | TriggerZones$ Battlefield | Execute$ TrigDamage | TriggerDescription$ Whenever you cycle another card, CARDNAME deals 1 damage to each opponent.
+SVar:TrigDamage:DB$ DealDamage | Defined$ Player.Opponent | NumDmg$ 1
+K:Cycling:1
+Oracle:Whenever you cycle another card, Drannith Stinger deals 1 damage to each opponent.\nCycling {1} ({1}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/durable_coilbug.txt b/forge-gui/res/cardsfolder/upcoming/durable_coilbug.txt
new file mode 100755
index 00000000000..6636df1ada3
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/durable_coilbug.txt
@@ -0,0 +1,6 @@
+Name:Durable Coilbug
+ManaCost:1 B
+Types:Creature Insect
+PT:2/2
+A:AB$ ChangeZone | Cost$ 4 B | Origin$ Graveyard | Destination$ Hand | ActivationZone$ Graveyard | SpellDescription$ Return CARDNAME from your graveyard to your hand.
+Oracle:{4}{B}: Return Durable Coilbug from your graveyard to your hand.
diff --git a/forge-gui/res/cardsfolder/upcoming/duskfang_mentor.txt b/forge-gui/res/cardsfolder/upcoming/duskfang_mentor.txt
new file mode 100755
index 00000000000..6abc7ca7361
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/duskfang_mentor.txt
@@ -0,0 +1,9 @@
+Name:Duskfang Mentor
+ManaCost:2 B
+Types:Creature Human Cleric
+PT:1/3
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPut | TriggerDescription$ When CARDNAME enters the battlefield, put a lifelink counter on target non-Human creature you control.
+SVar:TrigPut:DB$ PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature | CounterType$ LIFELINK | CounterNum$ 1
+A:AB$ PutCounterAll | Cost$ 1 B T | ValidCards$ Creature.YouCtrl+withLifelink | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on each creature you control with lifelink.
+DeckHas:Ability$Counters
+Oracle:When Duskfang Mentor enters the battlefield, put a lifelink counter on target non-Human creature you control.\n{1}{B}, {T}: Put a +1/+1 counter on each creature you control with lifelink.
diff --git a/forge-gui/res/cardsfolder/upcoming/easy_prey.txt b/forge-gui/res/cardsfolder/upcoming/easy_prey.txt
new file mode 100755
index 00000000000..181bf0bd26b
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/easy_prey.txt
@@ -0,0 +1,6 @@
+Name:Easy Prey
+ManaCost:1 B
+Types:Instant
+A:SP$ Destroy | Cost$ 1 B | ValidTgts$ Creature.cmcLE2 | TgtPrompt$ Select target creature with converted mana cost 2 or less | SpellDescription$ Destroy target creature with converted mana cost 2 or less.
+K:Cycling:2
+Oracle:Destroy target creature with converted mana cost 2 or less.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/eerie_ultimatum.txt b/forge-gui/res/cardsfolder/upcoming/eerie_ultimatum.txt
new file mode 100755
index 00000000000..eb0697408bc
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/eerie_ultimatum.txt
@@ -0,0 +1,7 @@
+Name:Eerie Ultimatum
+ManaCost:W W B B B G G
+Types:Sorcery
+A:SP$ ChangeZone | Cost$ W W B B B G G | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Permanent.YouOwn | DifferentNames$ True | ChangeNum$ X | References$ X | Hidden$ True | StackDescription$ SpellDescription | SpellDescription$ Return any number of permanent cards with different names from your graveyard to the battlefield.
+SVar:X:Count$DifferentCardNames_Permanent.YouOwn+inZoneGraveyard
+DeckHints:Ability$ DifferentNames
+Oracle:Return any number of permanent cards with different names from your graveyard to the battlefield.
diff --git a/forge-gui/res/cardsfolder/upcoming/emergent_ultimatum.txt b/forge-gui/res/cardsfolder/upcoming/emergent_ultimatum.txt
new file mode 100755
index 00000000000..64b4ad710d4
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/emergent_ultimatum.txt
@@ -0,0 +1,10 @@
+Name:Emergent Ultimatum
+ManaCost:B B G G G U U
+Types:Sorcery
+A:SP$ ChangeZone | Cost$ B B G G G U U | Origin$ Library | Hidden$ True | ChangeNum$ 3 | ChangeType$ Card.MonoColor | DifferentNames$ True | Destination$ Exile | RememberChanged$ True | SubAbility$ DBChooseCard | Shuffle$ False | StackDescription$ SpellDescription | SpellDescription$ Search your library for up to three different monocolored cards with different names and exile them. An opponent chooses one of those cards. Shuffle that card into your library. You may cast the other cards without paying their mana costs. Exile CARDNAME.
+SVar:DBChooseCard:DB$ ChooseCard | Defined$ Opponent | Choices$ Card.IsRemembered | Amount$ 1 | Mandatory$ True | ChoiceTitle$ Choose a card to shuffle back into the library | ChoiceZone$ Exile | AILogic$ BestCard | SubAbility$ DBShuffle | StackDescription$ None
+SVar:DBShuffle:DB$ ChangeZone | Origin$ Exile | Destination$ Library | Defined$ ChosenCard | ForgetChanged$ True | Shuffle$ True | SubAbility$ DBCast | StackDescription$ None
+SVar:DBCast:DB$ Play | Valid$ Card.IsRemembered | ValidZone$ Exile | WithoutManaCost$ True | Optional$ True | Amount$ All | SubAbility$ DBExileSelf | StackDescription$ None
+SVar:DBExileSelf:DB$ ChangeZone | Origin$ Stack | Destination$ Exile | SubAbility$ DBCleanup | StackDescription$ None
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True
+Oracle:Search your library for up to three different monocolored cards with different names and exile them. An opponent chooses one of those cards. Shuffle that card into your library. You may cast the other cards without paying their mana costs. Exile Emergent Ultimatum.
diff --git a/forge-gui/res/cardsfolder/upcoming/escape_protocol.txt b/forge-gui/res/cardsfolder/upcoming/escape_protocol.txt
new file mode 100644
index 00000000000..168c9d7586e
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/escape_protocol.txt
@@ -0,0 +1,7 @@
+Name:Escape Protocol
+ManaCost:1 U
+Types:Enchantment
+T:Mode$ Cycled | ValidCard$ Card.YouOwn | Execute$ TrigExile | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cycle a card, you may pay {1}. When you do, exile target creature or artifact you control, then return it to the battlefield under its owner's control.
+SVar:TrigExile:AB$ ChangeZone | Cost$ 1 | ValidTgts$ Creature.YouCtrl,Artifact.YouCtrl | TgtPrompt$ Select target creature or artifact you control | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBReturn
+SVar:DBReturn:DB$ ChangeZone | Defined$ Targeted | Origin$ Exile | Destination$ Battlefield
+Oracle:Whenever you cycle a card, you may pay {1}. When you do, exile target creature or artifact you control, then return it to the battlefield under its owner's control.
diff --git a/forge-gui/res/cardsfolder/upcoming/excavation_mole.txt b/forge-gui/res/cardsfolder/upcoming/excavation_mole.txt
new file mode 100755
index 00000000000..9715a550ad0
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/excavation_mole.txt
@@ -0,0 +1,8 @@
+Name:Excavation Mole
+ManaCost:2 G
+Types:Creature Mole
+PT:3/3
+K:Trample
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMill | TriggerDescription$ When CARDNAME enters the battlefield, put the top three cards of your library into your graveyard.
+SVar:TrigMill:DB$ Mill | NumCards$ 3 | Defined$ You
+Oracle:Trample\nWhen Excavation Mole enters the battlefield, put the top three cards of your library into your graveyard.
diff --git a/forge-gui/res/cardsfolder/upcoming/exuberant_wolfbear.txt b/forge-gui/res/cardsfolder/upcoming/exuberant_wolfbear.txt
new file mode 100644
index 00000000000..27726fe127e
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/exuberant_wolfbear.txt
@@ -0,0 +1,10 @@
+Name:Exuberant Wolfbear
+ManaCost:3 G
+Types:Creature Wolf Bear
+PT:4/4
+T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigAnimate | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ Whenever CARDNAME attacks, you may change the base power and toughness of target Human you control to CARDNAME's power and toughness until end of turn.
+SVar:TrigAnimate:DB$ Animate | ValidTgts$ Creature.Human+YouCtrl | TgtPrompt$ Select target Human you control | Power$ X | Toughness$ Y | References$ X,Y
+SVar:X:Count$CardPower
+SVar:Y:Count$CardToughness
+SVar:HasAttackEffect:TRUE
+Oracle:Whenever Exuberant Wolfbear attacks, you may change the base power and toughness of target Human you control to Exuberant Wolfbear's power and toughness until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/facet_reader.txt b/forge-gui/res/cardsfolder/upcoming/facet_reader.txt
new file mode 100755
index 00000000000..6a6dcaa496f
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/facet_reader.txt
@@ -0,0 +1,7 @@
+Name:Facet Reader
+ManaCost:1 U
+Types:Creature Human Wizard
+PT:1/2
+A:AB$ Draw | Cost$ 1 T | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard
+SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose
+Oracle:{1}, {T}: Draw a card, then discard a card.
diff --git a/forge-gui/res/cardsfolder/upcoming/farfinder.txt b/forge-gui/res/cardsfolder/upcoming/farfinder.txt
new file mode 100755
index 00000000000..002090bdbba
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/farfinder.txt
@@ -0,0 +1,8 @@
+Name:Farfinder
+ManaCost:3
+Types:Creature Fox
+PT:1/1
+K:Vigilance
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChange | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.
+SVar:TrigChange:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Basic | ChangeNum$ 1 | ShuffleNonMandatory$ True
+Oracle:Vigilance\nWhen Farfinder enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.
diff --git a/forge-gui/res/cardsfolder/upcoming/ferocious_tigorilla.txt b/forge-gui/res/cardsfolder/upcoming/ferocious_tigorilla.txt
new file mode 100755
index 00000000000..2ee099b69b2
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/ferocious_tigorilla.txt
@@ -0,0 +1,10 @@
+Name:Ferocious Tigorilla
+ManaCost:3 R
+Types:Creature Cat Ape
+PT:4/3
+K:ETBReplacement:Other:CounterChoice
+SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Trample,Menace | SpellDescription$ CARDNAME enters the battlefield with your choice of a trample counter or a menace counter on it.
+SVar:Trample:DB$ PutCounter | CounterType$ TRAMPLE | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a trample counter
+SVar:Menace:DB$ PutCounter | CounterType$ MENACE | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a menace counter
+DeckHas:Ability$Counters
+Oracle:Ferocious Tigorilla enters the battlefield with your choice of a trample counter or a menace counter on it. (A creature with menace can't be blocked except by two or more creatures.)
diff --git a/forge-gui/res/cardsfolder/upcoming/fiend_artisan.txt b/forge-gui/res/cardsfolder/upcoming/fiend_artisan.txt
new file mode 100755
index 00000000000..3300037e039
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/fiend_artisan.txt
@@ -0,0 +1,11 @@
+Name:Fiend Artisan
+ManaCost:B/G B/G
+Types:Creature Nightmare
+PT:1/1
+S:Mode$ Continuous | Affected$ Card.Self | AddPower$ Y | AddToughness$ Y | Description$ CARDNAME gets +1/+1 for each creature card in your graveyard.
+A:AB$ ChangeZone | Cost$ X BG T Sac<1/Creature.Other/another creature> | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.cmcLEX | ChangeNum$ 1 | References$ X | ChangeNum$ 1 | SorcerySpeed$ True | StackDescription$ SpellDescription | SpellDescription$ Search your library for a creature card with converted mana cost X or less, put it onto the battlefield, then shuffle your library. Activate this ability only any time you could cast a sorcery.
+SVar:Y:Count$TypeInYourYard.Creature
+SVar:X:Count$xPaid
+AI:RemoveDeck:All
+DeckHints:Ability$Graveyard
+Oracle:Fiend Artisan gets +1/+1 for each creature card in your graveyard.\n{X}{B/G}, {T}, Sacrifice another creature: Search your library for a creature card with converted mana cost X or less, put it onto the battlefield, then shuffle your library. Activate this ability only any time you could cast a sorcery.
diff --git a/forge-gui/res/cardsfolder/upcoming/fight_as_one.txt b/forge-gui/res/cardsfolder/upcoming/fight_as_one.txt
new file mode 100755
index 00000000000..f2258e0659b
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/fight_as_one.txt
@@ -0,0 +1,7 @@
+Name:Fight as One
+ManaCost:W
+Types:Instant
+A:SP$ Charm | Cost$ W | Choices$ Human,NonHuman | MinCharmNum$ 1 | CharmNum$ 2
+SVar:Human:DB$ Pump | ValidTgts$ Creature.Human+YouCtrl | TgtPrompt$ Select target Human creature you control | NumAtt$ 1 | NumDef$ 1 | KW$ Indestructible | SpellDescription$ Target Human creature you control gets +1/+1 and gains indestructible until end of turn.
+SVar:NonHuman:DB$ Pump | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature you control | NumAtt$ 1 | NumDef$ 1 | KW$ Indestructible | SpellDescription$ Target non-Human creature you control gets +1/+1 and gains indestructible until end of turn.
+Oracle:Choose one or both—\n• Target Human creature you control gets +1/+1 and gains indestructible until end of turn.\n• Target non-Human creature you control gets +1/+1 and gains indestructible until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/fire_prophecy.txt b/forge-gui/res/cardsfolder/upcoming/fire_prophecy.txt
new file mode 100644
index 00000000000..17041dbf737
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/fire_prophecy.txt
@@ -0,0 +1,8 @@
+Name:Fire Prophecy
+ManaCost:1 R
+Types:Instant
+A:SP$ DealDamage | Cost$ 1 R | ValidTgts$ Creature | NumDmg$ 3 | SubAbility$ DBBottom | SpellDescription$ CARDNAME deals 3 damage to target creature. You may put a card from your hand on the bottom of your library. If you do, draw a card.
+SVar:DBBottom:DB$ ChangeZone | Origin$ Hand | Destination$ Library | Hand$ Library | LibraryPosition$ -1 | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DBDraw
+SVar:DBDraw:DB$ Draw | NumCards$ 1 | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBCleanup
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
+Oracle:Fire Prophecy deals 3 damage to target creature. You may put a card from your hand on the bottom of your library. If you do, draw a card.
diff --git a/forge-gui/res/cardsfolder/upcoming/flourishing_fox.txt b/forge-gui/res/cardsfolder/upcoming/flourishing_fox.txt
new file mode 100755
index 00000000000..c74da09394e
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/flourishing_fox.txt
@@ -0,0 +1,8 @@
+Name:Flourishing Fox
+ManaCost:W
+Types:Creature Fox
+PT:1/1
+T:Mode$ Cycled | ValidCard$ Card.Other | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you cycle another card, put a +1/+1 counter on CARDNAME.
+SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
+K:Cycling:1
+Oracle:Whenever you cycle another card, put a +1/+1 counter on Flourishing Fox.\nCycling {1} ({1}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/flycatcher_giraffid.txt b/forge-gui/res/cardsfolder/upcoming/flycatcher_giraffid.txt
new file mode 100644
index 00000000000..e8aa64dfced
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/flycatcher_giraffid.txt
@@ -0,0 +1,10 @@
+Name:Flycatcher Giraffid
+ManaCost:4 G
+Types:Creature Antelope Lizard
+PT:3/5
+K:ETBReplacement:Other:CounterChoice
+SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Vigilance,Reach | SpellDescription$ CARDNAME enters the battlefield with your choice of a vigilance counter or a reach counter on it.
+SVar:Vigilance:DB$ PutCounter | CounterType$ VIGILANCE | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a vigilance counter on it
+SVar:Reach:DB$ PutCounter | CounterType$ REACH | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a reach counter on it
+DeckHas:Ability$Counters
+Oracle:Flycatcher Giraffid enters the battlefield with your choice of a vigilance counter or a reach counter on it.
diff --git a/forge-gui/res/cardsfolder/upcoming/footfall_crater.txt b/forge-gui/res/cardsfolder/upcoming/footfall_crater.txt
new file mode 100755
index 00000000000..7d33bd9b541
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/footfall_crater.txt
@@ -0,0 +1,11 @@
+Name:Footfall Crater
+ManaCost:R
+Types:Enchantment Aura
+K:Enchant land
+K:Cycling:1
+A:SP$ Attach | Cost$ R | ValidTgts$ Land | TgtPrompt$ Select target land | AILogic$ Pump
+S:Mode$ Continuous | Affected$ Land.AttachedBy | AddAbility$ DBPump | Description$ Enchanted land has "{T}: Target creature gains trample and haste until end of turn."
+SVar:DBPump:AB$ Pump | Cost$ T | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ Trample & Haste | SpellDescription$ Target creature gains trample and haste until end of turn.
+SVar:NonStackingAttachEffect:True
+SVar:PlayMain1:TRUE
+Oracle:Enchant land\nEnchanted land has "{T}: Target creature gains trample and haste until end of turn."\nCycling {1} ({1}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/forbidden_friendship.txt b/forge-gui/res/cardsfolder/upcoming/forbidden_friendship.txt
new file mode 100755
index 00000000000..499acd9578a
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/forbidden_friendship.txt
@@ -0,0 +1,7 @@
+Name:Forbidden Friendship
+ManaCost:1 R
+Types:Sorcery
+A:SP$ Token | Cost$ 1 R | TokenAmount$ 1 | TokenScript$ r_1_1_dinosaur_haste | TokenOwner$ You | LegacyImage$ r 1 1 dinosaur haste iko | SubAbility$ DBToken | SpellDescription$ Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token.
+SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_human_soldier | TokenOwner$ You | LegacyImage$ w 1 1 human soldier iko
+DeckHas:Ability$Token
+Oracle:Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token.
diff --git a/forge-gui/res/cardsfolder/upcoming/frillscare_mentor.txt b/forge-gui/res/cardsfolder/upcoming/frillscare_mentor.txt
new file mode 100644
index 00000000000..65549f7dcba
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/frillscare_mentor.txt
@@ -0,0 +1,9 @@
+Name:Frillscare Mentor
+ManaCost:2 R
+Types:Creature Human Warrior
+PT:3/2
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPut | TriggerDescription$ When CARDNAME enters the battlefield, put a menace counter on target non-Human creature you control.
+SVar:TrigPut:DB$ PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature you control | CounterType$ MENACE | CounterNum$ 1
+SVar:PlayMain1:TRUE
+A:AB$ PutCounterAll | Cost$ 2 R T | ValidCards$ Creature.YouCtrl+withMenace | CounterType$ P1P1 | CounterNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Put a +1/+1 counter on each creature you control with menace.
+Oracle:When Frillscare Mentor enters the battlefield, put a menace counter on target non-Human creature you control. (It can't be blocked except by two or more creatures)\n{2}{R}, {T}: Put a +1/+1 counter on each creature you control with menace.
diff --git a/forge-gui/res/cardsfolder/upcoming/frondland_felidar.txt b/forge-gui/res/cardsfolder/upcoming/frondland_felidar.txt
new file mode 100755
index 00000000000..54e6dc6d350
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/frondland_felidar.txt
@@ -0,0 +1,9 @@
+Name:Frondland Felidar
+ManaCost:2 G W
+Types:Creature Cat Beast
+PT:3/5
+K:Vigilance
+S:Mode$ Continuous | Affected$ Creature.YouCtrl+withVigilance | AddAbility$ ABTap | Description$ Creatures you control with vigilance have "{1}, {T}: Tap target creature."
+SVar:ABTap:AB$ Tap | Cost$ 1 | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Tap target creature.
+SVar:PlayMain1:TRUE
+Oracle:Vigilance\nCreatures you control with vigilance have "{1}, {T}: Tap target creature."
diff --git a/forge-gui/res/cardsfolder/upcoming/frostveil_ambush.txt b/forge-gui/res/cardsfolder/upcoming/frostveil_ambush.txt
new file mode 100644
index 00000000000..ef8a7570c07
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/frostveil_ambush.txt
@@ -0,0 +1,7 @@
+Name:Frostveil Ambush
+ManaCost:3 U U
+Types:Instant
+A:SP$ Tap | Cost$ 3 U U | TargetMin$ 0 | TargetMax$ 2 | TgtPrompt$ Choose up to two target creatures | ValidTgts$ Creature | SubAbility$ TrigPump | StackDescription$ SpellDescription | SpellDescription$ Tap up to two target creatures. Those creatures don't untap during their controller's next untap step.
+SVar:TrigPump:DB$ Pump | Defined$ Targeted | KW$ HIDDEN This card doesn't untap during your next untap step. | Permanent$ True | StackDescription$ None
+K:Cycling:1
+Oracle:Tap up to two target creatures. Those creatures don't untap during their controller's next untap step.\nCycling {1} ({1}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/fully_grown.txt b/forge-gui/res/cardsfolder/upcoming/fully_grown.txt
new file mode 100644
index 00000000000..edfefc1522a
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/fully_grown.txt
@@ -0,0 +1,6 @@
+Name:Fully Grown
+ManaCost:2 G
+Types:Instant
+A:SP$ Pump | Cost$ 2 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +3 | NumDef$ +3 | SubAbility$ PutCounter | SpellDescription$ Target creature gets +3/+3 until end of turn. Put a trample counter on it.
+SVar:PutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ TRAMPLE | CounterNum$ 1
+Oracle:Target creature gets +3/+3 until end of turn. Put a trample counter on it.
diff --git a/forge-gui/res/cardsfolder/upcoming/garrison_cat.txt b/forge-gui/res/cardsfolder/upcoming/garrison_cat.txt
new file mode 100755
index 00000000000..31482e7de6c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/garrison_cat.txt
@@ -0,0 +1,10 @@
+Name:Garrison Cat
+ManaCost:W
+Types:Creature Cat
+PT:1/1
+T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, create a 1/1 white Human Soldier creature token.
+SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_human_soldier | TokenOwner$ TriggeredCardController | LegacyImage$ w 1 1 human soldier iko
+SVar:SacMe:1
+DeckHints:Type$Human
+DeckHas:Ability$Token
+Oracle:When Garrison Cat dies, create a 1/1 white Human Soldier creature token.
diff --git a/forge-gui/res/cardsfolder/upcoming/general_kudro_of_drannith.txt b/forge-gui/res/cardsfolder/upcoming/general_kudro_of_drannith.txt
new file mode 100755
index 00000000000..54765c7e001
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/general_kudro_of_drannith.txt
@@ -0,0 +1,11 @@
+Name:General Kudro of Drannith
+ManaCost:1 W B
+Types:Legendary Creature Human Soldier
+PT:3/3
+S:Mode$ Continuous | Affected$ Human.Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Humans you control get +1/+1.
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ Whenever CARDNAME or another Human enters the battlefield under your control, exile target card from an opponent's graveyard.
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Other+Human+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigExile | Secondary$ True | TriggerDescription$ Whenever CARDNAME or another Human enters the battlefield under your control, exile target card from an opponent's graveyard.
+SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | TgtPrompt$ Choose target card in an opponent's graveyard | ValidTgts$ Card.OppOwn
+A:AB$ Destroy | Cost$ 2 Sac<2/Human> | ValidTgts$ Creature.powerGE4 | TgtPrompt$ Select target creature with power 4 or greater | SpellDescription$ Destroy target creature with power 4 or greater.
+DeckHints:Type$Human
+Oracle:Other Humans you control get +1/+1.\nWhenever General Kudro of Drannith or another Human enters the battlefield under your control, exile target card from an opponent's graveyard.\n{2}, Sacrifice two Humans: Destroy target creature with power 4 or greater.
diff --git a/forge-gui/res/cardsfolder/upcoming/generals_enforcer.txt b/forge-gui/res/cardsfolder/upcoming/generals_enforcer.txt
new file mode 100755
index 00000000000..a1a15e1f3d9
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/generals_enforcer.txt
@@ -0,0 +1,9 @@
+Name:General's Enforcer
+ManaCost:W B
+Types:Creature Human Soldier
+PT:2/3
+S:Mode$ Continuous | Affected$ Human.YouCtrl+Legendary | AddKeyword$ Indestructible | Description$ Legendary Humans you control have indestructible.
+A:AB$ ChangeZone | Cost$ 2 W B | Origin$ Graveyard | Destination$ Exile | TgtPrompt$ Choose target card in a graveyard | ValidTgts$ Card | SubAbility$ DBToken | SpellDescription$ Exile target card from a graveyard. If it was a creature card, create a 1/1 white Human Soldier creature token.
+SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_human_soldier | TokenOwner$ You | LegacyImage$ w 1 1 human soldier iko | ConditionDefined$ Targeted | ConditionPresent$ Creature | ConditionCompare$ EQ1 | StackDescription$ If it was a creature card, create a 1/1 white Human Soldier creature token.
+DeckHints:Type$Human
+Oracle:Legendary Humans you control have indestructible.\n{2}{W}{B}: Exile target card from a graveyard. If it was a creature card, create a 1/1 white Human Soldier creature token.
diff --git a/forge-gui/res/cardsfolder/upcoming/genesis_ultimatum.txt b/forge-gui/res/cardsfolder/upcoming/genesis_ultimatum.txt
new file mode 100755
index 00000000000..f30c17ff67d
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/genesis_ultimatum.txt
@@ -0,0 +1,6 @@
+Name:Genesis Ultimatum
+ManaCost:G G U U U R R
+Types:Sorcery
+A:SP$ Dig | Cost$ G G U U U R R | DigNum$ 5 | AnyNumber$ True | ChangeValid$ Permanent | DestinationZone$ Battlefield | DestinationZone2$ Hand | SubAbility$ DBChange | StackDescription$ SpellDescription | SpellDescription$ Look at the top five cards of your library. Put any number of permanent cards from among them onto the battlefield and the rest into your hand. Exile CARDNAME.
+SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile | StackDescription$ None
+Oracle:Look at the top five cards of your library. Put any number of permanent cards from among them onto the battlefield and the rest into your hand. Exile Genesis Ultimatum.
diff --git a/forge-gui/res/cardsfolder/upcoming/glimmerbell.txt b/forge-gui/res/cardsfolder/upcoming/glimmerbell.txt
new file mode 100755
index 00000000000..94f761135ce
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/glimmerbell.txt
@@ -0,0 +1,7 @@
+Name:Glimmerbell
+ManaCost:1 U
+Types:Creature Elemental Jellyfish
+PT:1/3
+K:Flying
+A:AB$ Untap | Cost$ 1 U | SpellDescription$ Untap CARDNAME.
+Oracle:Flying\n{1}{U}: Untap Glimmerbell.
diff --git a/forge-gui/res/cardsfolder/upcoming/gloom_pangolin.txt b/forge-gui/res/cardsfolder/upcoming/gloom_pangolin.txt
new file mode 100755
index 00000000000..2e495da70b0
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/gloom_pangolin.txt
@@ -0,0 +1,5 @@
+Name:Gloom Pangolin
+ManaCost:2 B
+Types:Creature Nightmare Pangolin
+PT:1/5
+Oracle:
diff --git a/forge-gui/res/cardsfolder/upcoming/go_for_blood.txt b/forge-gui/res/cardsfolder/upcoming/go_for_blood.txt
new file mode 100755
index 00000000000..b95f323350c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/go_for_blood.txt
@@ -0,0 +1,7 @@
+Name:Go for Blood
+ManaCost:1 R
+Types:Sorcery
+A:SP$ Pump | Cost$ 1 R | AILogic$ Fight | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Choose target creature you control | SubAbility$ DBFight | StackDescription$ None | SpellDescription$ Target creature you control fights target creature you don't control.
+SVar:DBFight:DB$ Fight | Defined$ ParentTarget | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Choose target creature you don't control
+K:Cycling:1
+Oracle:Target creature you control fights target creature you don't control. (Each deals damage equal to its power to the other.)\nCycling {1} ({1}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/grimdancer.txt b/forge-gui/res/cardsfolder/upcoming/grimdancer.txt
new file mode 100755
index 00000000000..23037a2fe79
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/grimdancer.txt
@@ -0,0 +1,13 @@
+Name:Grimdancer
+ManaCost:1 B B
+Types:Creature Nightmare
+PT:3/3
+K:ETBReplacement:Other:CounterChoice
+SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ MeDe,DeLi,MeLi | SpellDescription$ CARDNAME enters the battlefield with your choice of two different counters on it from among menace, deathtouch, and lifelink.
+SVar:MeDe:DB$ PutCounter | CounterType$ MENACE | CounterNum$ 1 | SubAbility$ Deathtouch | SpellDescription$ Menace and Deathtouch
+SVar:Deathtouch:DB$ PutCounter | CounterType$ DEATHTOUCH | CounterNum$ 1
+SVar:DeLi:DB$ PutCounter | CounterType$ DEATHTOUCH | CounterNum$ 1 | SubAbility$ Lifelink | SpellDescription$ Deathtouch and Lifelink
+SVar:Lifelink:DB$ PutCounter | CounterType$ LIFELINK | CounterNum$ 1
+SVar:MeLi:DB$ PutCounter | CounterType$ MENACE | CounterNum$ 1 | SubAbility$ Lifelink | SpellDescription$ Menace and Lifelink
+DeckHas:Ability$Counters
+Oracle:Grimdancer enters the battlefield with your choice of two different counters on it from among menace, deathtouch, and lifelink.
diff --git a/forge-gui/res/cardsfolder/upcoming/gust_of_wind.txt b/forge-gui/res/cardsfolder/upcoming/gust_of_wind.txt
new file mode 100755
index 00000000000..b6a85d1a937
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/gust_of_wind.txt
@@ -0,0 +1,9 @@
+Name:Gust of Wind
+ManaCost:3 U
+Types:Sorcery
+S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ 2 | EffectZone$ All | IsPresent$ Creature.YouCtrl+withFlying | Description$ CARDNAME costs {2} less to cast if you control a creature with flying.
+A:SP$ ChangeZone | Cost$ 3 U | ValidTgts$ Permanent.nonLand+YouDontCtrl | TgtPrompt$ Select target nonland permanent you don't control | Origin$ Battlefield | Destination$ Hand | SubAbility$ DBDraw | SpellDescription$ Return target nonland permanent you don't control to its owner's hand.
+SVar:DBDraw:DB$ Draw | NumCards$ 1 | SpellDescription$ Draw a card.
+SVar:BuffedBy:Creature.withFlying
+DeckHints:Keyword$Flying
+Oracle:This spell costs {2} less to cast if you control a creature with flying.\nReturn target nonland permanent you don't control to its owner's hand.\nDraw a card.
diff --git a/forge-gui/res/cardsfolder/upcoming/hampering_snare.txt b/forge-gui/res/cardsfolder/upcoming/hampering_snare.txt
new file mode 100755
index 00000000000..d26e4c9522d
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/hampering_snare.txt
@@ -0,0 +1,6 @@
+Name:Hampering Snare
+ManaCost:1 U
+Types:Instant
+A:SP$ PumpAll | Cost$ 1 U | ValidCards$ Creature.OppCtrl | IsCurse$ True | NumAtt$ -2 | SpellDescription$ Creatures your opponents control get -2/-0 until end of turn.
+K:Cycling:2
+Oracle:Creatures your opponents control get -2/-0 until end of turn.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/heartless_act.txt b/forge-gui/res/cardsfolder/upcoming/heartless_act.txt
new file mode 100755
index 00000000000..20b3c089562
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/heartless_act.txt
@@ -0,0 +1,7 @@
+Name:Heartless Act
+ManaCost:1 B
+Types:Instant
+A:SP$ Charm | Cost$ 1 B | Choices$ Destroy,Remove | CharmNum$ 1
+SVar:Destroy:DB$ Destroy | ValidTgts$ Creature.NoCounters | TgtPrompt$ Select target creature with no counters on it | SpellDescription$ Destroy target creature with no counters on it.
+SVar:Remove:DB$ RemoveCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ Any | CounterNum$ 3 | UpTo$ True | SpellDescription$ Remove up to three counters from target creature.
+Oracle:Choose one —\n• Destroy target creature with no counters on it.\n• Remove up to three counters from target creature.
diff --git a/forge-gui/res/cardsfolder/upcoming/heightened_reflexes.txt b/forge-gui/res/cardsfolder/upcoming/heightened_reflexes.txt
new file mode 100755
index 00000000000..97481588db6
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/heightened_reflexes.txt
@@ -0,0 +1,6 @@
+Name:Heightened Reflexes
+ManaCost:R
+Types:Instant
+A:SP$ Pump | Cost$ R | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +1 | SubAbility$ DBPutCounter | SpellDescription$ Target creature gets +1/+0 until end of turn. Put a first strike counter on it.
+SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ FIRSTSTRIKE | CounterNum$ 1
+Oracle:Target creature gets +1/+0 until end of turn. Put a first strike counter on it.
diff --git a/forge-gui/res/cardsfolder/upcoming/helica_glider.txt b/forge-gui/res/cardsfolder/upcoming/helica_glider.txt
new file mode 100755
index 00000000000..84c11c62d9c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/helica_glider.txt
@@ -0,0 +1,10 @@
+Name:Helica Glider
+ManaCost:2 W
+Types:Creature Nightmare Squirrel
+PT:2/2
+K:ETBReplacement:Other:CounterChoice
+SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Flying,FirstStrike | SpellDescription$ CARDNAME enters the battlefield with your choice of a flying counter or a first strike counter on it.
+SVar:Flying:DB$ PutCounter | CounterType$ FLYING | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a flying counter on it
+SVar:FirstStrike:DB$ PutCounter | CounterType$ FIRSTSTRIKE | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a first strike counter on it
+DeckHints:Ability$Counters
+Oracle:Helica Glider enters the battlefield with your choice of a flying counter or a first strike counter on it.
diff --git a/forge-gui/res/cardsfolder/upcoming/honey_mammoth.txt b/forge-gui/res/cardsfolder/upcoming/honey_mammoth.txt
new file mode 100755
index 00000000000..cdaf260c3e7
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/honey_mammoth.txt
@@ -0,0 +1,7 @@
+Name:Honey Mammoth
+ManaCost:4 G G
+Types:Creature Elephant
+PT:6/6
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigGainLife | TriggerDescription$ When CARDNAME enters the battlefield, you gain 4 life.
+SVar:TrigGainLife:DB$ GainLife | LifeAmount$ 4
+Oracle:When Honey Mammoth enters the battlefield, you gain 4 life.
diff --git a/forge-gui/res/cardsfolder/upcoming/hornbash_mentor.txt b/forge-gui/res/cardsfolder/upcoming/hornbash_mentor.txt
new file mode 100755
index 00000000000..42b6b9fb2e3
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/hornbash_mentor.txt
@@ -0,0 +1,8 @@
+Name:Hornbash Mentor
+ManaCost:2 G
+Types:Creature Human Warrior
+PT:3/3
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Self | Execute$ TrigPut | TriggerDescription$ When CARDNAME enters the battlefield, put a trample counter on target non-Human creature you control.
+SVar:TrigPut:DB$ PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature you control | CounterType$ TRAMPLE | CounterNum$ 1
+A:AB$ PutCounterAll | Cost$ 2 G T | ValidCards$ Creature.YouCtrl+withTrample | CounterType$ P1P1 | CounterNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Put a +1/+1 counter on each creature you control with trample.
+Oracle:When Hornbash Mentor enters the battlefield, put a trample counter on target non-Human creature you control.\n{2}{G}, {T}: Put a +1/+1 counter on each creature you control with trample.
diff --git a/forge-gui/res/cardsfolder/upcoming/humble_naturalist.txt b/forge-gui/res/cardsfolder/upcoming/humble_naturalist.txt
new file mode 100755
index 00000000000..131ee853760
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/humble_naturalist.txt
@@ -0,0 +1,6 @@
+Name:Humble Naturalist
+ManaCost:1 G
+Types:Creature Human Druid
+PT:1/3
+A:AB$ Mana | Cost$ T | Produced$ Any | RestrictValid$ Creature | SpellDescription$ Add one mana of any color. Spend this mana only to cast a creature spell.
+Oracle:{T}: Add one mana of any color. Spend this mana only to cast a creature spell.
diff --git a/forge-gui/res/cardsfolder/upcoming/imposing_vantasaur.txt b/forge-gui/res/cardsfolder/upcoming/imposing_vantasaur.txt
new file mode 100755
index 00000000000..9419b96cf8a
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/imposing_vantasaur.txt
@@ -0,0 +1,7 @@
+Name:Imposing Vantasaur
+ManaCost:5 W
+Types:Creature Dinosaur
+PT:3/6
+K:Vigilance
+K:Cycling:1
+Oracle:Vigilance\nCycling {1} ({1}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/indatha_crystal.txt b/forge-gui/res/cardsfolder/upcoming/indatha_crystal.txt
new file mode 100755
index 00000000000..d2a68855418
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/indatha_crystal.txt
@@ -0,0 +1,8 @@
+Name:Indatha Crystal
+ManaCost:3
+Types:Artifact
+A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add {W}.
+A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add {B}.
+A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add {G}.
+K:Cycling:2
+Oracle:{T}: Add {W}, {B}, or {G}.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/indatha_triome.txt b/forge-gui/res/cardsfolder/upcoming/indatha_triome.txt
new file mode 100755
index 00000000000..b0786f5b295
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/indatha_triome.txt
@@ -0,0 +1,6 @@
+Name:Indatha Triome
+ManaCost:no cost
+Types:Land Plains Swamp Forest
+K:CARDNAME enters the battlefield tapped.
+K:Cycling:3
+Oracle:({T}: Add {W}, {B}, or {G}.)\nIndatha Triome enters the battlefield tapped.\nCycling {3} ({3}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/inspired_ultimatum.txt b/forge-gui/res/cardsfolder/upcoming/inspired_ultimatum.txt
new file mode 100755
index 00000000000..8c0fa2cd311
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/inspired_ultimatum.txt
@@ -0,0 +1,7 @@
+Name:Inspired Ultimatum
+ManaCost:U U R R R W W
+Types:Sorcery
+A:SP$ GainLife | Cost$ U U R R R W W | LifeAmount$ 5 | ValidTgts$ Player | TgtPrompt$ Choose a player | SubAbility$ DBDamage | SpellDescription$ Target player gains 5 life. CARDNAME deals 5 damage to any target. You draw five cards.
+SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 5 | SubAbility$ DBDraw
+SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 5
+Oracle:Target player gains 5 life. Inspired Ultimatum deals 5 damage to any target. You draw five cards.
diff --git a/forge-gui/res/cardsfolder/upcoming/jubilant_skybonder.txt b/forge-gui/res/cardsfolder/upcoming/jubilant_skybonder.txt
new file mode 100755
index 00000000000..038b6add4f8
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/jubilant_skybonder.txt
@@ -0,0 +1,8 @@
+Name:Jubilant Skybonder
+ManaCost:1 W/U W/U
+Types:Creature Human Wizard
+PT:2/2
+K:Flying
+S:Mode$ Continuous | Affected$ Creature.YouCtrl+withFlying | AddStaticAbility$ RaiseCost | Description$ Creatures you control with flying have "Spells your opponents cast that target this creature cost {2} more to cast."
+SVar:RaiseCost:Mode$ RaiseCost | ValidTarget$ Card.Self | Activator$ Opponent | Type$ Spell | Amount$ 2 | Description$ Spells your opponents cast that target CARDNAME cost {2} more to cast.
+Oracle:Flying\nCreatures you control with flying have "Spells your opponents cast that target this creature cost {2} more to cast."
diff --git a/forge-gui/res/cardsfolder/upcoming/keensight_mentor.txt b/forge-gui/res/cardsfolder/upcoming/keensight_mentor.txt
new file mode 100755
index 00000000000..776860c7257
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/keensight_mentor.txt
@@ -0,0 +1,9 @@
+Name:Keensight Mentor
+ManaCost:2 W
+Types:Creature Human Cleric
+PT:1/4
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPut | TriggerDescription$ When CARDNAME enters the battlefield, put a vigilance counter on target non-Human creature you control.
+SVar:TrigPut:DB$ PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature you control | CounterType$ VIGILANCE | CounterNum$ 1
+SVar:PlayMain1:TRUE
+A:AB$ PutCounterAll | Cost$ 1 W T | ValidCards$ Creature.YouCtrl+withVigilance | CounterType$ P1P1 | CounterNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Put a +1/+1 counter on each creature you control with vigilance.
+Oracle:When Keensight Mentor enters the battlefield, put a vigilance counter on target non-Human creature you control.\n{1}{W}, {T}: Put a +1/+1 counter on each creature you control with vigilance.
diff --git a/forge-gui/res/cardsfolder/upcoming/keep_safe.txt b/forge-gui/res/cardsfolder/upcoming/keep_safe.txt
new file mode 100755
index 00000000000..6f30256043e
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/keep_safe.txt
@@ -0,0 +1,6 @@
+Name:Keep Safe
+ManaCost:1 U
+Types:Instant
+A:SP$ Counter | Cost$ 1 U | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | TargetValidTargeting$ Permanent.YouCtrl+inZoneBattlefield | SubAbility$ DBDraw | SpellDescription$ Counter target spell that targets a permanent you control.
+SVar:DBDraw:DB$ Draw | NumCards$ 1 | SpellDescription$ Draw a card.
+Oracle:Counter target spell that targets a permanent you control.\nDraw a card.
diff --git a/forge-gui/res/cardsfolder/upcoming/ketria_crystal.txt b/forge-gui/res/cardsfolder/upcoming/ketria_crystal.txt
new file mode 100755
index 00000000000..f625a617f3c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/ketria_crystal.txt
@@ -0,0 +1,8 @@
+Name:Ketria Crystal
+ManaCost:3
+Types:Artifact
+A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add {G}.
+A:AB$ Mana | Cost$ T | Produced$ U | SpellDescription$ Add {U}.
+A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add {R}.
+K:Cycling:2
+Oracle:{T}: Add {G}, {U}, or {R}.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/ketria_triome.txt b/forge-gui/res/cardsfolder/upcoming/ketria_triome.txt
new file mode 100755
index 00000000000..bc69ec98715
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/ketria_triome.txt
@@ -0,0 +1,6 @@
+Name:Ketria Triome
+ManaCost:no cost
+Types:Land Forest Island Mountain
+K:CARDNAME enters the battlefield tapped.
+K:Cycling:3
+Oracle:({T}: Add {G}, {U}, or {R}.)\nKetria Triome enters the battlefield tapped.\nCycling {3} ({3}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/kinnan_bonder_prodigy.txt b/forge-gui/res/cardsfolder/upcoming/kinnan_bonder_prodigy.txt
new file mode 100755
index 00000000000..69077cc3d6a
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/kinnan_bonder_prodigy.txt
@@ -0,0 +1,8 @@
+Name:Kinnan, Bonder Prodigy
+ManaCost:G U
+Types:Legendary Creature Human Druid
+PT:2/2
+T:Mode$ TapsForMana | ValidCard$ Permanent.nonLand+YouCtrl | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a nonland permanent for mana, add one mana of any type that permanent produced.
+SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer
+A:AB$ Dig | Cost$ 5 G U | DigNum$ 5 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Creature.nonHuman | DestinationZone$ Battlefield | RestRandomOrder$ True | SpellDescription$ Look at the top five cards of your library. You may put a non-Human creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order.
+Oracle:Whenever you tap a nonland permanent for mana, add one mana of any type that permanent produced.\n{5}{G}{U}: Look at the top five cards of your library. You may put a non-Human creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order.
diff --git a/forge-gui/res/cardsfolder/upcoming/kogla_the_titan_ape.txt b/forge-gui/res/cardsfolder/upcoming/kogla_the_titan_ape.txt
new file mode 100644
index 00000000000..5f368e332b8
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/kogla_the_titan_ape.txt
@@ -0,0 +1,11 @@
+Name:Kogla, the Titan Ape
+ManaCost:3 G G G
+Types:Legendary Creature Ape
+PT:7/6
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigFight | TriggerDescription$ When CARDNAME enters the battlefield, it fights up to one target creature you don't control.
+SVar:TrigFight:DB$ Fight | Defined$ TriggeredCardLKICopy | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Choose target creature you don't control | TargetMin$ 0 | TargetMax$ 1
+T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDestroy | TriggerDescription$ Whenever CARDNAME attacks, destroy target artifact or enchantment defending player controls.
+SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Artifact.DefenderCtrl,Enchantment.DefenderCtrl | TgtPrompt$ Select target artifact or enchantment defending player controls
+A:AB$ ChangeZone | Cost$ 1 G | ValidTgts$ Human.YouCtrl | TgtPrompt$ Choose target Human you control | Origin$ Battlefield | Destination$ Hand | SubAbility$ DBPump | SpellDescription$ Return target Human you control to its owner's hand. CARDNAME gains indestructible until end of turn.
+SVar:DBPump:DB$ Pump | Defined$ Self | KW$ Indestructible
+Oracle:When Kogla, the Titan Ape enters the battlefield, it fights up to one target creature you don't control.\nWhenever Kogla attacks, destroy target artifact or enchantment defending player controls.\n{1}{G}: Return target Human you control to its owner's hand. Kogla gains indestructible until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/labyrinth_raptor.txt b/forge-gui/res/cardsfolder/upcoming/labyrinth_raptor.txt
new file mode 100755
index 00000000000..663387e9577
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/labyrinth_raptor.txt
@@ -0,0 +1,10 @@
+Name:Labyrinth Raptor
+ManaCost:B R
+Types:Creature Nightmare Dinosaur
+PT:2/2
+K:Menace
+T:Mode$ AttackerBlocked | ValidCard$ Creature.YouCtrl+withMenace | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ Whenever a creature you control with menace becomes blocked, defending player sacrifices a creature blocking it.
+SVar:TrigSac:DB$ Sacrifice | Defined$ DefendingPlayer | SacValid$ Creature.blockingSource | ChangeNum$ 1
+A:AB$ PumpAll | Cost$ B R | ValidCards$ Creature.YouCtrl+withMenace | NumAtt$ 1 | SpellDescription$ Creatures you control with menace get +1/+0 until end of turn.
+SVar:PlayMain1:TRUE
+Oracle:Menace\nWhenever a creature you control with menace becomes blocked, defending player sacrifices a creature blocking it.\n{B}{R}: Creatures you control with menace get +1/+0 until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/lava_serpent.txt b/forge-gui/res/cardsfolder/upcoming/lava_serpent.txt
new file mode 100755
index 00000000000..4f28838f5e2
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/lava_serpent.txt
@@ -0,0 +1,7 @@
+Name:Lava Serpent
+ManaCost:5 R
+Types:Creature Elemental Serpent
+PT:5/5
+K:Haste
+K:Cycling:2
+Oracle:Haste\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/light_of_hope.txt b/forge-gui/res/cardsfolder/upcoming/light_of_hope.txt
new file mode 100755
index 00000000000..e6094ca2ef2
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/light_of_hope.txt
@@ -0,0 +1,8 @@
+Name:Light of Hope
+ManaCost:W
+Types:Instant
+A:SP$ Charm | Cost$ W | Choices$ DBGainLife,DBDestroy,DBPutCounter | Defined$ You
+SVar:DBGainLife:DB$ GainLife | LifeAmount$ 4 | SpellDescription$ You gain 4 life.
+SVar:DBDestroy:DB$ Destroy | ValidTgts$ Enchantment | TgtPrompt$ Select target enchantment | SpellDescription$ Destroy target enchantment.
+SVar:DBPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on target creature.
+Oracle:Choose one —\n• You gain 4 life.\n• Destroy target enchantment.\n• Put a +1/+1 counter on target creature.
diff --git a/forge-gui/res/cardsfolder/upcoming/lukka_coppercoat_outcast.txt b/forge-gui/res/cardsfolder/upcoming/lukka_coppercoat_outcast.txt
new file mode 100755
index 00000000000..f73ff690b0e
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/lukka_coppercoat_outcast.txt
@@ -0,0 +1,18 @@
+Name:Lukka, Coppercoat Outcast
+ManaCost:3 R R
+Types:Legendary Planeswalker Lukka
+Loyalty:5
+A:AB$ Mill | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | NumCards$ 3 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBRepeat | StackDescription$ SpellDescription | SpellDescription$ Exile the top three cards of your library. Creature cards exiled this way gain "You may cast this card from exile as long as you control a Lukka planeswalker."
+SVar:DBRepeat:DB$ RepeatEach | UseImprinted$ True | RepeatSubAbility$ DBAnimate | RepeatCards$ Creature.IsRemembered | Zone$ Exile | SubAbility$ DBCleanup
+SVar:DBAnimate:DB$ Animate | Defined$ Imprinted | staticAbilities$ STMayPlay | Permanent$ True
+SVar:STMayPlay:Mode$ Continuous | Affected$ Card.Self | AffectedZone$ Exile | EffectZone$ Exile | MayPlay$ True | IsPresent$ Planeswalker.Lukka+YouCtrl | Description$ You may cast this card from exile as long as you control a Lukka planeswalker.
+A:AB$ ChangeZone | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBDigUntil | RememberChanged$ True | StackDescription$ SpellDescription | SpellDescription$ Exile target creature you control, then reveal cards from the top of your library until you reveal a creature card with higher converted mana cost. Put that card onto the battlefield and the rest on the bottom of your library in a random order.
+SVar:DBDigUntil:DB$ DigUntil | Valid$ Creature.cmcGEX | References$ X | ValidDescription$ creature card with higher converted mana cost | FoundDestination$ Battlefield | RevealedDestination$ Library | RevealedLibraryPosition$ -1 | RestRandomOrder$ True | SubAbility$ DBCleanup | StackDescription$ None
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
+A:AB$ RepeatEach | Cost$ SubCounter<7/LOYALTY> | Planeswalker$ True | Ultimate$ True | RepeatCards$ Creature.YouCtrl | RepeatSubAbility$ DBDamage | DamageMap$ True | SpellDescription$ Each creature you control deals damage equal to its power to each opponent.
+SVar:DBDamage:DB$ DealDamage | Defined$ Player.Opponent | DamageSource$ Remembered | NumDmg$ Y | References$ Y
+SVar:X:Remembered$CardManaCost/Plus.1
+SVar:Y:Remembered$CardPower
+SVar:NonStackingEffect:True
+AI:RemoveDeck:Random
+Oracle:[+1]: Exile the top three cards of your library. Creature cards exiled this way gain "You may cast this card from exile as long as you control a Lukka planeswalker."\n[−2]: Exile target creature you control, then reveal cards from the top of your library until you reveal a creature card with higher converted mana cost. Put that card onto the battlefield and the rest on the bottom of your library in a random order.\n[−7]: Each creature you control deals damage equal to its power to each opponent.
diff --git a/forge-gui/res/cardsfolder/upcoming/luminous_broodmoth.txt b/forge-gui/res/cardsfolder/upcoming/luminous_broodmoth.txt
new file mode 100755
index 00000000000..4c31a46ea3f
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/luminous_broodmoth.txt
@@ -0,0 +1,9 @@
+Name:Luminous Broodmoth
+ManaCost:2 W W
+Types:Creature Insect
+PT:3/4
+K:Flying
+T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouCtrl+withoutFlying | Execute$ TrigReturn | TriggerZones$ Battlefield | TriggerDescription$ Whenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it.
+SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard | SubAbility$ DBPutCounter
+SVar:DBPutCounter:DB$ PutCounter | CounterType$ FLYING | CounterNum$ 1 | Defined$ TriggeredCard
+Oracle:Flying\nWhenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it.
diff --git a/forge-gui/res/cardsfolder/upcoming/lurking_deadeye.txt b/forge-gui/res/cardsfolder/upcoming/lurking_deadeye.txt
new file mode 100755
index 00000000000..235d6d2f1b4
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/lurking_deadeye.txt
@@ -0,0 +1,8 @@
+Name:Lurking Deadeye
+ManaCost:3 B
+Types:Creature Human Assassin
+PT:4/2
+K:Flash
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroy | TriggerDescription$ When CARDNAME enters the battlefield, destroy target creature that was dealt damage this turn.
+SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Creature.wasDealtDamageThisTurn | TgtPrompt$ Select target creature that was dealt damage this turn
+Oracle:Flash\nWhen Lurking Deadeye enters the battlefield, destroy target creature that was dealt damage this turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/maned_serval.txt b/forge-gui/res/cardsfolder/upcoming/maned_serval.txt
new file mode 100755
index 00000000000..13c31d6c25d
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/maned_serval.txt
@@ -0,0 +1,6 @@
+Name:Maned Serval
+ManaCost:1 W
+Types:Creature Cat
+PT:1/4
+K:Vigilance
+Oracle:Vigilance
diff --git a/forge-gui/res/cardsfolder/upcoming/memory_leak.txt b/forge-gui/res/cardsfolder/upcoming/memory_leak.txt
new file mode 100755
index 00000000000..254d84b79b8
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/memory_leak.txt
@@ -0,0 +1,6 @@
+Name:Memory Leak
+ManaCost:2 B
+Types:Sorcery
+A:SP$ ChangeZone | Cost$ 2 B | Origin$ Hand,Graveyard | Destination$ Exile | ValidTgts$ Opponent | DefinedPlayer$ Targeted | Chooser$ You | ChangeType$ Card.nonLand | ChangeNum$ 1 | IsCurse$ True | Mandatory$ True | StackDescription$ SpellDescription | SpellDescription$ Target opponent reveals their hand. You choose a nonland card from that player's graveyard or hand and exile it.
+K:Cycling:1
+Oracle:Target opponent reveals their hand. You choose a nonland card from that player's graveyard or hand and exile it.\nCycling {1} ({1}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/migration_path.txt b/forge-gui/res/cardsfolder/upcoming/migration_path.txt
new file mode 100755
index 00000000000..74cbf679d8b
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/migration_path.txt
@@ -0,0 +1,6 @@
+Name:Migration Path
+ManaCost:3 G
+Types:Sorcery
+A:SP$ ChangeZone | Cost$ 3 G | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ 2 | StackDescription$ SpellDescription | SpellDescription$ Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library.
+K:Cycling:2
+Oracle:Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/momentum_rumbler.txt b/forge-gui/res/cardsfolder/upcoming/momentum_rumbler.txt
new file mode 100755
index 00000000000..590c1cdbb3c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/momentum_rumbler.txt
@@ -0,0 +1,9 @@
+Name:Momentum Rumbler
+ManaCost:3 R
+Types:Creature Dinosaur
+PT:3/3
+T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPutCounter | IsPresent$ Card.Self+withFirst Strike | PresentCompare$ EQ0 | TriggerDescription$ Whenever CARDNAME attacks, if it doesn't have first strike, put a first strike counter on it.
+SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredAttackerLKICopy | CounterType$ FIRSTSTRIKE | CounterNum$ 1
+T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPump | IsPresent$ Card.Self+withFirst Strike | PresentCompare$ EQ1 | TriggerDescription$ Whenever CARDNAME attacks, if it has first strike, it gains double strike until end of turn.
+SVar:TrigPump:DB$ Pump | Defined$ TriggeredAttacker | KW$ Double Strike
+Oracle:Whenever Momentum Rumbler attacks, if it doesn't have first strike, put a first strike counter on it.\nWhenever Momentum Rumbler attacks, if it has first strike, it gains double strike until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/monstrous_step.txt b/forge-gui/res/cardsfolder/upcoming/monstrous_step.txt
new file mode 100755
index 00000000000..e9657f47684
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/monstrous_step.txt
@@ -0,0 +1,7 @@
+Name:Monstrous Step
+ManaCost:4 G
+Types:Sorcery
+A:SP$ Pump | Cost$ 4 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +7 | NumDef$ +7 | SubAbility$ DBMustBlock | SpellDescription$ Target creature gets +7/+7 until end of turn. Up to one other target creature blocks it this turn if able.
+SVar:DBMustBlock:DB$ MustBlock | DefinedAttacker$ ParentTarget | ValidTgts$ Creature | TargetUnique$ True | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select up to one other target creature to block
+K:Cycling:2
+Oracle:Target creature gets +7/+7 until end of turn. Up to one other target creature blocks it this turn if able.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/mosscoat_goriak.txt b/forge-gui/res/cardsfolder/upcoming/mosscoat_goriak.txt
new file mode 100755
index 00000000000..c13b33335f6
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/mosscoat_goriak.txt
@@ -0,0 +1,6 @@
+Name:Mosscoat Goriak
+ManaCost:2 G
+Types:Creature Beast
+PT:2/4
+K:Vigilance
+Oracle:Vigilance
diff --git a/forge-gui/res/cardsfolder/upcoming/mutual_destruction.txt b/forge-gui/res/cardsfolder/upcoming/mutual_destruction.txt
new file mode 100755
index 00000000000..348034cea5d
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/mutual_destruction.txt
@@ -0,0 +1,8 @@
+Name:Mutual Destruction
+ManaCost:B
+Types:Sorcery
+S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Flash | IsPresent$ Permanent.YouCtrl+hasKeywordFlash | AffectedZone$ All | Description$ CARDNAME has flash as long as you control a permanent with flash.
+A:SP$ Destroy | Cost$ B Sac<1/Creature> | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Destroy target creature.
+SVar:AICostPreference:SacCost$Creature.Token,Creature.cmcLE2
+AI:RemoveDeck:All
+Oracle:This spell has flash as long as you control a permanent with flash.\nAs an additional cost to cast this spell, sacrifice a creature.\nDestroy target creature.
diff --git a/forge-gui/res/cardsfolder/upcoming/mythos_of_brokkos.txt b/forge-gui/res/cardsfolder/upcoming/mythos_of_brokkos.txt
new file mode 100644
index 00000000000..349028de6a5
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/mythos_of_brokkos.txt
@@ -0,0 +1,6 @@
+Name:Mythos of Brokkos
+ManaCost:2 G G
+Types:Sorcery
+A:SP$ ChangeZone | Cost$ 2 G G | Origin$ Library | Destination$ Graveyard | ChangeNum$ 1 | ChangeType$ Card | ConditionManaSpent$ U B | SubAbility$ DBChangeZone | StackDescription$ SpellDescription | SpellDescription$ If {U}{B} was spent to cast this spell, search your library for a card, put that card into your graveyard, then shuffle your library. Return up to two permanent cards from your graveyard to your hand.
+SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ChangeNum$ 2 | ChangeType$ Permanent.YouOwn | Hidden$ True | StackDescription$ None
+Oracle:If {U}{B} was spent to cast this spell, search your library for a card, put that card into your graveyard, then shuffle your library.\nReturn up to two permanent cards from your graveyard to your hand.
diff --git a/forge-gui/res/cardsfolder/upcoming/mythos_of_illuna.txt b/forge-gui/res/cardsfolder/upcoming/mythos_of_illuna.txt
new file mode 100644
index 00000000000..cb3e8c523fc
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/mythos_of_illuna.txt
@@ -0,0 +1,8 @@
+Name:Mythos of Illuna
+ManaCost:2 U U
+Types:Sorcery
+A:SP$ CopyPermanent | Cost$ 2 U U | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | ConditionManaNotSpent$ R G | SubAbility$ CopyFight | StackDescription$ SpellDescription | SpellDescription$ Create a token that’s a copy of target permanent. If {R}{G} was spent to cast this spell, instead create a token that’s a copy of that permanent, except the token has “When this permanent enters the battlefield, if it’s a creature, it fights up to one target creature you don’t control.”
+SVar:CopyFight:DB$ CopyPermanent | Defined$ Targeted | ConditionManaSpent$ R G | AddTriggers$ TrigChange | AddSVars$ TrigFight,TrigChange | StackDescription$ None
+SVar:TrigChange:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Self | Execute$ TrigFight | TriggerDescription$ When CARDNAME enters the battlefield, if it’s a creature, it fights up to one target creature you don't control. (Each deals damage equal to its power to the other.)
+SVar:TrigFight:DB$ Fight | Defined$ TriggeredCardLKICopy | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Choose target creature you don't control | TargetMin$ 0 | TargetMax$ 1
+Oracle:Create a token that’s a copy of target permanent. If {R}{G} was spent to cast this spell, instead create a token that’s a copy of that permanent, except the token has “When this permanent enters the battlefield, if it’s a creature, it fights up to one target creature you don’t control.”
diff --git a/forge-gui/res/cardsfolder/upcoming/mythos_of_nethroi.txt b/forge-gui/res/cardsfolder/upcoming/mythos_of_nethroi.txt
new file mode 100644
index 00000000000..451ad1c4384
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/mythos_of_nethroi.txt
@@ -0,0 +1,6 @@
+Name:Mythos of Nethroi
+ManaCost:2 B
+Types:Instant
+A:SP$ Destroy | Cost$ 2 B | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | ConditionDefined$ Targeted | ConditionPresent$ Creature | ConditionCompare$ EQ1 | ConditionManaNotSpent$ G W | SubAbility$ DBDestroy | StackDescription$ SpellDescription | SpellDescription$ Destroy target nonland permanent if it's a creature or if {G}{W} was spent to cast this spell.
+SVar:DBDestroy:DB$ Destroy | Defined$ Targeted | ConditionManaSpent$ G W | StackDescription$ None
+Oracle:Destroy target nonland permanent if it's a creature or if {G}{W} was spent to cast this spell.
diff --git a/forge-gui/res/cardsfolder/upcoming/mythos_of_snapdax.txt b/forge-gui/res/cardsfolder/upcoming/mythos_of_snapdax.txt
new file mode 100644
index 00000000000..f336b4db057
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/mythos_of_snapdax.txt
@@ -0,0 +1,17 @@
+Name:Mythos of Snapdax
+ManaCost:2 W W
+Types:Sorcery
+A:SP$ RepeatEach | Cost$ 2 W W | ConditionManaSpent$ B R | RepeatPlayers$ Player | RepeatSubAbility$ ChooseArtfYou | SubAbility$ AllChoose | SpellDescription$ Each player chooses an artifact, a creature, an enchantment, and a planeswalker from among the nonland permanents they control, then sacrifices the rest. If {B}{R} was spent to cast this spell, you choose the permanents for each player instead.
+SVar:ChooseArtfYou:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Artifact.nonLand+RememberedPlayerCtrl | ChoiceTitle$ Choose an artifact to keep | SubAbility$ ChooseCrtrYou | RememberChosen$ True | Mandatory$ True
+SVar:ChooseCrtrYou:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Creature.nonLand+RememberedPlayerCtrl | ChoiceTitle$ Choose a creature to keep | SubAbility$ ChooseEnchYou | RememberChosen$ True | Mandatory$ True
+SVar:ChooseEnchYou:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Enchantment.nonLand+RememberedPlayerCtrl | ChoiceTitle$ Choose an enchantment to keep | SubAbility$ ChooseWalkYou | RememberChosen$ True | Mandatory$ True
+SVar:ChooseWalkYou:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Planeswalker.RememberedPlayerCtrl | ChoiceTitle$ Choose a planeswalker to keep | RememberChosen$ True | Mandatory$ True
+SVar:AllChoose:DB$ RepeatEach | ConditionManaNotSpent$ B R | RepeatPlayers$ Player | RepeatSubAbility$ ChooseArtf | SubAbility$ SacAllOthers
+SVar:ChooseArtf:DB$ ChooseCard | Defined$ Remembered | Amount$ 1 | Choices$ Artifact.nonLand+RememberedPlayerCtrl | ChoiceTitle$ Choose an artifact to keep | SubAbility$ ChooseCrtr | RememberChosen$ True | Mandatory$ True
+SVar:ChooseCrtr:DB$ ChooseCard | Defined$ Remembered | Amount$ 1 | Choices$ Creature.nonLand+RememberedPlayerCtrl | ChoiceTitle$ Choose a creature to keep | SubAbility$ ChooseEnch | RememberChosen$ True | Mandatory$ True
+SVar:ChooseEnch:DB$ ChooseCard | Defined$ Remembered | Amount$ 1 | Choices$ Enchantment.nonLand+RememberedPlayerCtrl | ChoiceTitle$ Choose an enchantment to keep | SubAbility$ ChooseWalk | RememberChosen$ True | Mandatory$ True
+SVar:ChooseWalk:DB$ ChooseCard | Defined$ Remembered | Amount$ 1 | Choices$ Planeswalker.RememberedPlayerCtrl | ChoiceTitle$ Choose a planeswalker to keep | RememberChosen$ True | Mandatory$ True
+SVar:SacAllOthers:DB$ SacrificeAll | ValidCards$ Permanent.nonLand+IsNotRemembered | SubAbility$ DBCleanup | StackDescription$ None
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True
+AI:RemoveDeck:All
+Oracle:Each player chooses an artifact, a creature, an enchantment, and a planeswalker from among the nonland permanents they control, then sacrifices the rest. If {B}{R} was spent to cast this spell, you choose the permanents for each player instead.
diff --git a/forge-gui/res/cardsfolder/upcoming/mythos_of_vadrok.txt b/forge-gui/res/cardsfolder/upcoming/mythos_of_vadrok.txt
new file mode 100644
index 00000000000..8c2c4f15785
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/mythos_of_vadrok.txt
@@ -0,0 +1,6 @@
+Name:Mythos of Vadrok
+ManaCost:2 R R
+Types:Sorcery
+A:SP$ DealDamage | Cost$ 2 R R | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select any number of target creatures or planeswalkers to distribute damage to | NumDmg$ 5 | TargetMin$ 1 | TargetMax$ 5 | DividedAsYouChoose$ 5 | SubAbility$ DBPump | StackDescription$ SpellDescription | SpellDescription$ CARDNAME deals 5 damage divided as you choose among any number of target creatures and/or planeswalkers. If {W}{U} was spent to cast this spell, until your next turn, those permanents can’t attack or block and their activated abilities can’t be activated.
+SVar:DBPump:DB$ Pump | Defined$ Targeted | UntilYourNextTurn$ True | KW$ HIDDEN CARDNAME can't attack or block. & HIDDEN CARDNAME's activated abilities can't be activated. | ConditionManaSpent$ W U | StackDescription$ None
+Oracle:Mythos of Vadrok deals 5 damage divided as you choose among any number of target creatures and/or planeswalkers. If {W}{U} was spent to cast this spell, until your next turn, those permanents can’t attack or block and their activated abilities can’t be activated.
diff --git a/forge-gui/res/cardsfolder/upcoming/narset_of_the_ancient_way.txt b/forge-gui/res/cardsfolder/upcoming/narset_of_the_ancient_way.txt
new file mode 100644
index 00000000000..b3849b2871e
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/narset_of_the_ancient_way.txt
@@ -0,0 +1,15 @@
+Name:Narset of the Ancient Way
+ManaCost:1 U R W
+Types:Legendary Planeswalker Narset
+Loyalty:4
+A:AB$ GainLife | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | LifeAmount$ 2 | Defined$ You | SubAbility$ DBMana | StackDescription$ SpellDescription | SpellDescription$ You gain 2 life. Add {U}, {R}, or {W}. Spend this mana only to cast a noncreature spell.
+SVar:DBMana:DB$ Mana | Produced$ Combo U R W | Amount$ 1 | RestrictValid$ Card.nonCreature | StackDescription$ None
+A:AB$ Draw | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard | StackDescription$ SpellDescription | SpellDescription$ Draw a card, then you may discard a card. When you discard a nonland card this way, CARDNAME deals damage equal to that card's converted mana cost to target creature or planeswalker.
+SVar:DBDiscard:DB$ Discard | Defined$ You | Mode$ TgtChoose | Optional$ True | NumCards$ 1 | RememberDiscarded$ True | SubAbility$ DBDamage | StackDescription$ None
+SVar:DBDamage:DB$ DealDamage | ConditionDefined$ Remembered | ConditionPresent$ Card.nonLand | ConditionCompare$ EQ1 | NumDmg$ X | References$ X | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker | SubAbility$ DBCleanup | StackDescription$ None
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
+SVar:X:Remembered$CardManaCost
+A:AB$ Effect | Cost$ SubCounter<6/LOYALTY> | Planeswalker$ True | Ultimate$ True | Name$ Emblem - Narset of the Ancient Way | Image$ emblem_narset_of_the_ancient_way | Triggers$ TrigSpellCast | SVars$ EffSpellCast | Duration$ Permanent | Duration$ Permanent | AILogic$ Always | SpellDescription$ You get an emblem with "Whenever you cast a noncreature spell, this emblem deals 2 damage to any target."
+SVar:TrigSpellCast:Mode$ SpellCast | ValidActivatingPlayer$ You | ValidCard$ Card.nonCreature | Execute$ EffSpellCast | TriggerDescription$ Whenever you cast a noncreature spell, this emblem deals 2 damage to any target.
+SVar:EffSpellCast:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 2
+Oracle:[+1]: You gain 2 life. Add {U}, {R}, or {W}. Spend this mana only to cast a noncreature spell.\n[−2]: Draw a card, then you may discard a card. When you discard a nonland card this way, Narset of the Ancient Way deals damage equal to that card's converted mana cost to target creature or planeswalker.\n[−6]: You get an emblem with "Whenever you cast a noncreature spell, this emblem deals 2 damage to any target."
diff --git a/forge-gui/res/cardsfolder/upcoming/neutralize.txt b/forge-gui/res/cardsfolder/upcoming/neutralize.txt
new file mode 100755
index 00000000000..757fe250adf
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/neutralize.txt
@@ -0,0 +1,6 @@
+Name:Neutralize
+ManaCost:1 U U
+Types:Instant
+A:SP$ Counter | Cost$ 1 U U | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | SpellDescription$ Counter target spell.
+K:Cycling:2
+Oracle:Counter target spell.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/nightsquad_commando.txt b/forge-gui/res/cardsfolder/upcoming/nightsquad_commando.txt
new file mode 100755
index 00000000000..8cfe56342f1
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/nightsquad_commando.txt
@@ -0,0 +1,8 @@
+Name:Nightsquad Commando
+ManaCost:2 B
+Types:Creature Human Soldier
+PT:2/3
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | CheckSVar$ RaidTest | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, if you attacked this turn, create a 1/1 white Human Soldier creature token.
+SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_human_soldier | TokenOwner$ You
+SVar:RaidTest:Count$AttackersDeclared
+Oracle:When Nightsquad Commando enters the battlefield, if you attacked this turn, create a 1/1 white Human Soldier creature token.
diff --git a/forge-gui/res/cardsfolder/upcoming/of_one_mind.txt b/forge-gui/res/cardsfolder/upcoming/of_one_mind.txt
new file mode 100755
index 00000000000..96f69d1a1ec
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/of_one_mind.txt
@@ -0,0 +1,10 @@
+Name:Of One Mind
+ManaCost:2 U
+Types:Sorcery
+S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ 2 | EffectZone$ All | CheckSVar$ Z | SVarCompare$ GE2 | References$ X,Y,Z | Description$ CARDNAME costs {2} less to cast if you control a Human creature and a non-Human creature.
+SVar:X:Count$Valid Creature.Human+YouCtrl/LimitMax.1
+SVar:Y:Count$Valid Creature.nonHuman+YouCtrl/LimitMax.1
+SVar:Z:SVar$X/Plus.Y
+DeckHints:Type$Human
+A:SP$ Draw | Cost$ 2 U | NumCards$ 2 | SpellDescription$ Draw two cards.
+Oracle:This spell costs {2} less to cast if you control a Human creature and a non-Human creature.\nDraw two cards.
diff --git a/forge-gui/res/cardsfolder/upcoming/offsprings_revenge.txt b/forge-gui/res/cardsfolder/upcoming/offsprings_revenge.txt
new file mode 100755
index 00000000000..73e88f7003d
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/offsprings_revenge.txt
@@ -0,0 +1,11 @@
+Name:Offspring's Revenge
+ManaCost:2 R W B
+Types:Enchantment
+T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigExile | TriggerDescription$ At the beginning of combat on your turn, exile target red, white, or black creature card from your graveyard. Create a token that's a copy of that card, except it's 1/1. It gains haste until your next turn.
+SVar:TrigExile:DB$ ChangeZone | ValidTgts$ Creature.Red+YouCtrl,Creature.White+YouCtrl,Creature.Black+YouCtrl | TgtPrompt$ Select target red, white, or black creature card in your graveyard | Origin$ Graveyard | Destination$ Exile | RememberChanged$ True | SubAbility$ DBCopy
+SVar:DBCopy:DB$ CopyPermanent | Defined$ Remembered | SetPower$ 1 | SetToughness$ 1 | ImprintCopied$ True | SubAbility$ DBPump
+SVar:DBPump:DB$ Pump | Defined$ Imprinted | KW$ Haste | UntilYourNextTurn$ True | SubAbility$ DBCleanup
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
+SVar:PlayMain1:TRUE
+DeckHas:Ability$Token
+Oracle:At the beginning of combat on your turn, exile target red, white, or black creature card from your graveyard. Create a token that's a copy of that card, except it's 1/1. It gains haste until your next turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/ominous_seas.txt b/forge-gui/res/cardsfolder/upcoming/ominous_seas.txt
new file mode 100755
index 00000000000..d6f88938997
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/ominous_seas.txt
@@ -0,0 +1,8 @@
+Name:Ominous Seas
+ManaCost:1 U
+Types:Enchantment
+T:Mode$ Drawn | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you draw a card, put a foreshadow counter on CARDNAME.
+SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ FORESHADOW | CounterNum$ 1
+A:AB$ Token | Cost$ SubCounter<8/FORESHADOW> | TokenAmount$ 1 | TokenOwner$ You | TokenScript$ u_8_8_kraken | LegacyImage$ u 8 8 kraken iko | SpellDescription$ Create an 8/8 blue Kraken creature token.
+K:Cycling:2
+Oracle:Whenever you draw a card, put a foreshadow counter on Ominous Seas.\nRemove eight foreshadow counters from Ominous Seas: Create an 8/8 blue Kraken creature token.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/patagia_tiger.txt b/forge-gui/res/cardsfolder/upcoming/patagia_tiger.txt
new file mode 100755
index 00000000000..75a9cca2c76
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/patagia_tiger.txt
@@ -0,0 +1,10 @@
+Name:Patagia Tiger
+ManaCost:4 W
+Types:Creature Cat
+PT:3/4
+K:Flying
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ When CARDNAME enters the battlefield, target Human you control gets +2/+2 until end of turn.
+SVar:TrigPump:DB$ Pump | ValidTgts$ Human.YouCtrl | TgtPrompt$ Select target Human you control | NumAtt$ 2 | NumDef$ 2
+SVar:PlayMain1:TRUE
+DeckHints:Type$Human
+Oracle:Flying\nWhen Patagia Tiger enters the battlefield, target Human you control gets +2/+2 until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/perimeter_sergeant.txt b/forge-gui/res/cardsfolder/upcoming/perimeter_sergeant.txt
new file mode 100755
index 00000000000..6c6431b193b
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/perimeter_sergeant.txt
@@ -0,0 +1,7 @@
+Name:Perimeter Sergeant
+ManaCost:2 W
+Types:Creature Human Soldier
+PT:3/2
+T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPumpAll | TriggerDescription$ When CARDNAME attacks, other Humans you control get +1/+0 until end of turn.
+SVar:TrigPumpAll:DB$ PumpAll | ValidCards$ Human.Other+YouCtrl | NumAtt$ +1
+Oracle:Whenever Perimeter Sergeant attacks, other Humans you control get +1/+0 until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/phase_dolphin.txt b/forge-gui/res/cardsfolder/upcoming/phase_dolphin.txt
new file mode 100755
index 00000000000..5986f560749
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/phase_dolphin.txt
@@ -0,0 +1,7 @@
+Name:Phase Dolphin
+ManaCost:2 U
+Types:Creature Elemental Whale
+PT:1/4
+T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks, another target attacking creature can't be blocked this turn.
+SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.attacking+Other | TgtPrompt$ Select another target attacking creature | KW$ HIDDEN Unblockable
+Oracle:Whenever Phase Dolphin attacks, another target attacking creature can't be blocked this turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/prickly_marmoset.txt b/forge-gui/res/cardsfolder/upcoming/prickly_marmoset.txt
new file mode 100755
index 00000000000..b951f407a7a
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/prickly_marmoset.txt
@@ -0,0 +1,8 @@
+Name:Prickly Marmoset
+ManaCost:2 R
+Types:Creature Monkey
+PT:2/3
+K:First Strike
+T:Mode$ Cycled | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever you cycle a card, CARDNAME gets +2/+0 until end of turn.
+SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ +2
+Oracle:First strike\nWhenever you cycle a card, Prickly Marmoset gets +2/+0 until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/primal_empathy.txt b/forge-gui/res/cardsfolder/upcoming/primal_empathy.txt
new file mode 100755
index 00000000000..0f7a0520c31
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/primal_empathy.txt
@@ -0,0 +1,11 @@
+Name:Primal Empathy
+ManaCost:1 G U
+Types:Enchantment
+T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigBranch | TriggerDescription$ At the beginning of your upkeep, draw a card if you control a creature with the greatest power among creatures on the battlefield. Otherwise, put a +1/+1 counter on a creature you control.
+SVar:TrigBranch:DB$ Branch | References$ Y,Z | ConditionPresent$ Creature.YouCtrl | ConditionCompare$ GE1 | BranchConditionSVar$ Y | BranchConditionSVarCompare$ GEZ | TrueSubAbility$ Draw | FalseSubAbility$ PutCounter
+SVar:Draw:DB$ Draw | Defined$ You | NumCards$ 1
+SVar:PutCounter:DB$ PutCounter | Choices$ Creature.YouCtrl | CounterType$ P1P1 | CounterNum$ 1
+SVar:Y:Count$GreatestPower_Creature.YouCtrl
+SVar:Z:Count$GreatestPower_Creature.YouDontCtrl
+DeckHas:Ability$Counters
+Oracle:At the beginning of your upkeep, draw a card if you control a creature with the greatest power among creatures on the battlefield. Otherwise, put a +1/+1 counter on a creature you control.
diff --git a/forge-gui/res/cardsfolder/upcoming/proud_wildbonder.txt b/forge-gui/res/cardsfolder/upcoming/proud_wildbonder.txt
new file mode 100755
index 00000000000..509ba6ab126
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/proud_wildbonder.txt
@@ -0,0 +1,7 @@
+Name:Proud Wildbonder
+ManaCost:2 R/G R/G
+Types:Creature Human Warrior
+PT:4/3
+K:Trample
+S:Mode$ Continuous | Affected$ Creature.YouCtrl+withTrample | AddKeyword$ You may have CARDNAME assign its combat damage as though it weren't blocked. | Description$ Creatures you control with trample have "You may have this creature assign its combat damage as though it weren't blocked."
+Oracle:Trample\nCreatures you control with trample have "You may have this creature assign its combat damage as though it weren't blocked."
diff --git a/forge-gui/res/cardsfolder/upcoming/pyroceratops.txt b/forge-gui/res/cardsfolder/upcoming/pyroceratops.txt
new file mode 100755
index 00000000000..9afccdf31d0
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/pyroceratops.txt
@@ -0,0 +1,9 @@
+Name:Pyroceratops
+ManaCost:3 R
+Types:Creature Elemental Dinosaur
+PT:2/3
+K:Trample
+T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, put a +1/+1 counter on CARDNAME.
+SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
+SVar:BuffedBy:nonCreature
+Oracle:Trample\nWhenever you cast a noncreature spell, put a +1/+1 counter on Pyroceratops.
diff --git a/forge-gui/res/cardsfolder/upcoming/quartzwood_crasher.txt b/forge-gui/res/cardsfolder/upcoming/quartzwood_crasher.txt
new file mode 100755
index 00000000000..5c32f19facf
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/quartzwood_crasher.txt
@@ -0,0 +1,9 @@
+Name:Quartzwood Crasher
+ManaCost:2 R R G
+Types:Creature Dinosaur Beast
+PT:6/6
+K:Trample
+T:Mode$ DamageDoneOnce | CombatDamage$ True | ValidSource$ Creature.YouCtrl+withTrample | TriggerZones$ Battlefield | ValidTarget$ Player | Execute$ DBToken | TriggerDescription$ Whenever one or more creatures you control with trample deal combat damage to a player, create an X/X green Dinosaur Beast creature token with trample, where X is the amount of damage those creatures dealt to that player.
+SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenOwner$ You | TokenScript$ g_x_x_dinosaur_beast_trample | TokenPower$ X | TokenToughness$ X | References$ X | TokenOwner$ You | LegacyImage$ g x x dinosaur beast trample iko
+SVar:X:TriggerCount$DamageAmount
+Oracle:Trample\nWhenever one or more creatures you control with trample deal combat damage to a player, create an X/X green Dinosaur Beast creature token with trample, where X is the amount of damage those creatures dealt to that player.
diff --git a/forge-gui/res/cardsfolder/upcoming/raking_claws.txt b/forge-gui/res/cardsfolder/upcoming/raking_claws.txt
new file mode 100755
index 00000000000..5e0b2f3905c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/raking_claws.txt
@@ -0,0 +1,6 @@
+Name:Raking Claws
+ManaCost:1 R
+Types:Instant
+A:SP$ Pump | Cost$ 1 R | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ Double Strike | SpellDescription$ Target creature gains double strike until end of turn.
+K:Cycling:2
+Oracle:Target creature gains double strike until end of turn.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/raugrin_crystal.txt b/forge-gui/res/cardsfolder/upcoming/raugrin_crystal.txt
new file mode 100755
index 00000000000..a82eb62f2d5
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/raugrin_crystal.txt
@@ -0,0 +1,8 @@
+Name:Raugrin Crystal
+ManaCost:3
+Types:Artifact
+A:AB$ Mana | Cost$ T | Produced$ U | SpellDescription$ Add {U}.
+A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add {R}.
+A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add {W}.
+K:Cycling:2
+Oracle:{T}: Add {U}, {R}, or {W}.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/raugrin_triome.txt b/forge-gui/res/cardsfolder/upcoming/raugrin_triome.txt
new file mode 100755
index 00000000000..74ec276f3d9
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/raugrin_triome.txt
@@ -0,0 +1,6 @@
+Name:Raugrin Triome
+ManaCost:no cost
+Types:Land Island Mountain Plains
+K:CARDNAME enters the battlefield tapped.
+K:Cycling:3
+Oracle:({T}: Add {U}, {R}, or {W}.)\nRaugrin Triome enters the battlefield tapped.\nCycling {3} ({3}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/reconnaissance_mission.txt b/forge-gui/res/cardsfolder/upcoming/reconnaissance_mission.txt
new file mode 100755
index 00000000000..6d23f87cd14
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/reconnaissance_mission.txt
@@ -0,0 +1,7 @@
+Name:Reconnaissance Mission
+ManaCost:2 U U
+Types:Enchantment
+T:Mode$ DamageDone | ValidSource$ Creature.YouCtrl | ValidTarget$ Player | CombatDamage$ True | OptionalDecider$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever a creature you control deals combat damage to a player, you may draw a card.
+SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1
+K:Cycling:2
+Oracle:Whenever a creature you control deals combat damage to a player, you may draw a card.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/reptilian_reflection.txt b/forge-gui/res/cardsfolder/upcoming/reptilian_reflection.txt
new file mode 100755
index 00000000000..4eefadc17f4
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/reptilian_reflection.txt
@@ -0,0 +1,7 @@
+Name:Reptilian Reflection
+ManaCost:2 R
+Types:Enchantment
+T:Mode$ Cycled | ValidCard$ Card.YouOwn | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigAnimate | TriggerDescription$ Whenever you cycle a card, you may have CARDNAME become a 5/4 Dinosaur creature with trample and haste in addition to its other types until end of turn.
+SVar:TrigAnimate:DB$ Animate | Defined$ Self | Power$ 5 | Toughness$ 4 | Types$ Creature,Dinosaur | Keywords$ Trample & Haste
+SVar:PlayMain1:Always
+Oracle:Whenever you cycle a card, you may have Reptilian Reflection become a 5/4 Dinosaur creature with trample and haste in addition to its other types until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/rooting_moloch.txt b/forge-gui/res/cardsfolder/upcoming/rooting_moloch.txt
new file mode 100755
index 00000000000..492d645509c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/rooting_moloch.txt
@@ -0,0 +1,10 @@
+Name:Rooting Moloch
+ManaCost:4 R
+Types:Creature Lizard
+PT:4/4
+T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigChangeZone | TriggerDescription$ When CARDNAME enters the battlefield, exile target card with a cycling ability from your graveyard. Until the end of your next turn, you may play that card.
+SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card.YouOwn+withCycling,Card.YouOwn+withTypeCycling | TgtPrompt$ Select target card with a cycling ability from your graveyard | SubAbility$ DBEffect
+SVar:DBEffect:DB$ Effect | RememberObjects$ Targeted | ForgetOnMoved$ Exile | StaticAbilities$ Play | Duration$ UntilTheEndOfYourNextTurn
+SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ Until the end of your next turn, you may play this card.
+K:Cycling:2
+Oracle:When Rooting Moloch enters the battlefield, exile target card with a cycling ability from your graveyard. Until the end of your next turn, you may play that card.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/ruinous_ultimatum.txt b/forge-gui/res/cardsfolder/upcoming/ruinous_ultimatum.txt
new file mode 100755
index 00000000000..e258cf7c080
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/ruinous_ultimatum.txt
@@ -0,0 +1,5 @@
+Name:Ruinous Ultimatum
+ManaCost:R R W W W B B
+Types:Sorcery
+A:SP$ DestroyAll | Cost$ R R W W W B B | ValidCards$ Permanent.nonLand+OppCtrl | SpellDescription$ Destroy all nonland permanents your opponents control.
+Oracle:Destroy all nonland permanents your opponents control.
diff --git a/forge-gui/res/cardsfolder/upcoming/rumbling_rockslide.txt b/forge-gui/res/cardsfolder/upcoming/rumbling_rockslide.txt
new file mode 100755
index 00000000000..c215dd2fbdc
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/rumbling_rockslide.txt
@@ -0,0 +1,6 @@
+Name:Rumbling Rockslide
+ManaCost:3 R
+Types:Sorcery
+A:SP$ DealDamage | Cost$ 3 R | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ X | References$ X | SpellDescription$ CARDNAME deals damage to target creature equal to the number of lands you control.
+SVar:X:Count$Valid Land.YouCtrl
+Oracle:Rumbling Rockslide deals damage to target creature equal to the number of lands you control.
diff --git a/forge-gui/res/cardsfolder/upcoming/sanctuary_lockdown.txt b/forge-gui/res/cardsfolder/upcoming/sanctuary_lockdown.txt
new file mode 100755
index 00000000000..4df2e4caa18
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/sanctuary_lockdown.txt
@@ -0,0 +1,7 @@
+Name:Sanctuary Lockdown
+ManaCost:2 W
+Types:Enchantment
+S:Mode$ Continuous | Affected$ Creature.Human+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Humans you control get +1/+1.
+A:AB$ Tap | Cost$ 2 tapXType<2/Human> | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | SpellDescription$ Tap target creature an opponent controls.
+SVar:PlayMain1:TRUE
+Oracle:Humans you control get +1/+1.\n{2}, Tap two untapped Humans you control: Tap target creature an opponent controls.
diff --git a/forge-gui/res/cardsfolder/upcoming/sanctuary_smasher.txt b/forge-gui/res/cardsfolder/upcoming/sanctuary_smasher.txt
new file mode 100755
index 00000000000..b0d499f2e2c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/sanctuary_smasher.txt
@@ -0,0 +1,9 @@
+Name:Sanctuary Smasher
+ManaCost:4 R R
+Types:Creature Rhino Beast
+PT:6/4
+K:First Strike
+K:Cycling:2 R
+T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When you cycle CARDNAME, put a first strike counter on target creature you control.
+SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ FIRSTSTRIKE | CounterNum$ 1
+Oracle:First strike\nCycling {2}{R} ({2}{R}, Discard this card: Draw a card.)\nWhen you cycle Sanctuary Smasher, put a first strike counter on target creature you control.
diff --git a/forge-gui/res/cardsfolder/upcoming/savai_crystal.txt b/forge-gui/res/cardsfolder/upcoming/savai_crystal.txt
new file mode 100755
index 00000000000..761abfc2946
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/savai_crystal.txt
@@ -0,0 +1,8 @@
+Name:Savai Crystal
+ManaCost:3
+Types:Artifact
+A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add {R}.
+A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add {W}.
+A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add {B}.
+K:Cycling:2
+Oracle:{T}: Add {R}, {W}, or {B}.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/savai_sabertooth.txt b/forge-gui/res/cardsfolder/upcoming/savai_sabertooth.txt
new file mode 100755
index 00000000000..cf1abcd40a7
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/savai_sabertooth.txt
@@ -0,0 +1,5 @@
+Name:Savai Sabertooth
+ManaCost:1 W
+Types:Creature Cat
+PT:3/1
+Oracle:
diff --git a/forge-gui/res/cardsfolder/upcoming/savai_thundermane.txt b/forge-gui/res/cardsfolder/upcoming/savai_thundermane.txt
new file mode 100755
index 00000000000..6d739dd9abb
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/savai_thundermane.txt
@@ -0,0 +1,9 @@
+Name:Savai Thundermane
+ManaCost:R W
+Types:Creature Elemental Cat
+PT:3/2
+T:Mode$ Cycled | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigDealDamage | TriggerDescription$ Whenever you cycle a card, you may pay {2}. When you do, CARDNAME deals 2 damage to target creature and you gain 2 life.
+SVar:TrigDealDamage:AB$ DealDamage | Cost$ 2 | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 2 | SubAbility$ DBGainLife
+SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2
+DeckHas:Ability$LifeGain
+Oracle:Whenever you cycle a card, you may pay {2}. When you do, Savai Thundermane deals 2 damage to target creature and you gain 2 life.
diff --git a/forge-gui/res/cardsfolder/upcoming/savai_triome.txt b/forge-gui/res/cardsfolder/upcoming/savai_triome.txt
new file mode 100755
index 00000000000..b8fe9b5d48b
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/savai_triome.txt
@@ -0,0 +1,6 @@
+Name:Savai Triome
+ManaCost:no cost
+Types:Land Mountain Plains Swamp
+K:CARDNAME enters the battlefield tapped.
+K:Cycling:3
+Oracle:({T}: Add {R}, {W}, or {B}.)\nSavai Triome enters the battlefield tapped.\nCycling {3} ({3}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/serrated_scorpion.txt b/forge-gui/res/cardsfolder/upcoming/serrated_scorpion.txt
new file mode 100755
index 00000000000..e05e626c4b6
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/serrated_scorpion.txt
@@ -0,0 +1,8 @@
+Name:Serrated Scorpion
+ManaCost:B
+Types:Creature Scorpion
+PT:1/2
+T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigDealDamage | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, it deals 2 damage to each opponent and you gain 2 life.
+SVar:TrigDealDamage:DB$ DamageAll | ValidPlayers$ Player.Opponent | NumDmg$ 2 | SubAbility$ DBGainLife
+SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2
+Oracle:When Serrated Scorpion dies, it deals 2 damage to each opponent and you gain 2 life.
diff --git a/forge-gui/res/cardsfolder/upcoming/shark_typhoon.txt b/forge-gui/res/cardsfolder/upcoming/shark_typhoon.txt
new file mode 100644
index 00000000000..ba6e2a6a6c4
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/shark_typhoon.txt
@@ -0,0 +1,11 @@
+Name:Shark Typhoon
+ManaCost:5 U
+Types:Enchantment
+T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, create an X/X blue Shark creature token with flying, where X is that spell's converted mana cost.
+SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ u_x_x_shark_flying | TokenOwner$ You | LegacyImage$ u x x shark flying iko | TokenPower$ Y | TokenToughness$ Y | References$ Y | TokenAmount$ 1
+SVar:Y:TriggeredCard$CardManaCost
+K:Cycling:X 1 U
+T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigToken2 | TriggerZones$ Hand | TriggerDescription$ When you cycle CARDNAME, create an X/X blue Shark creature token with flying.
+SVar:TrigToken2:DB$ Token | TokenAmount$ 1 | TokenScript$ u_x_x_shark_flying | TokenOwner$ You | LegacyImage$ u x x shark flying iko | TokenPower$ X | TokenToughness$ X | References$ X | TokenAmount$ 1
+SVar:X:Count$xPaid
+Oracle:Whenever you cast a noncreature spell, create an X/X blue Shark creature token with flying, where X is that spell's converted mana cost.\nCycling {X}{1}{U} ({X}{1}{U}, Discard this card: Draw a card.)\nWhen you cycle Shark Typhoon, create an X/X blue Shark creature token with flying.
diff --git a/forge-gui/res/cardsfolder/upcoming/shredded_sails.txt b/forge-gui/res/cardsfolder/upcoming/shredded_sails.txt
new file mode 100755
index 00000000000..9f2b86e1505
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/shredded_sails.txt
@@ -0,0 +1,8 @@
+Name:Shredded Sails
+ManaCost:1 R
+Types:Instant
+A:SP$ Charm | Cost$ 1 R | CharmNum$ 1 | Choices$ DestArt,DmgFly
+SVar:DestArt:DB$ Destroy | ValidTgts$ Artifact | SpellDescription$ Destroy target artifact.
+SVar:DmgFly:DB$ DealDamage | ValidTgts$ Creature.withFlying | NumDmg$ 2 | SpellDescription$ CARDNAME deals 4 damage to target creature with flying.
+K:Cycling:2
+Oracle:Choose one —\n• Destroy target artifact.\n• Shredded Sails deals 4 damage to target creature with flying.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/skull_prophet.txt b/forge-gui/res/cardsfolder/upcoming/skull_prophet.txt
new file mode 100755
index 00000000000..90a2fd343e2
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/skull_prophet.txt
@@ -0,0 +1,9 @@
+Name:Skull Prophet
+ManaCost:B G
+Types:Creature Human Druid
+PT:3/1
+A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add {B}.
+A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add {G}.
+A:AB$ Mill | Cost$ T | Defined$ You | NumCards$ 2 | SpellDescription$ Put the top two cards of your library into your graveyard.
+DeckHas:Ability$Graveyard
+Oracle:{T}: Add {B} or {G}.\n{T}: Put the top two cards of your library into your graveyard.
diff --git a/forge-gui/res/cardsfolder/upcoming/skycat_sovereign.txt b/forge-gui/res/cardsfolder/upcoming/skycat_sovereign.txt
new file mode 100755
index 00000000000..4a3c3f55fa5
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/skycat_sovereign.txt
@@ -0,0 +1,10 @@
+Name:Skycat Sovereign
+ManaCost:W U
+Types:Creature Elemental Cat
+PT:1/1
+K:Flying
+S:Mode$ Continuous | Affected$ Card.Self | AddPower$ X | AddToughness$ X | Description$ CARDNAME gets +1/+1 for each other creature you control with flying.
+SVar:X:Count$Valid Creature.Other+YouCtrl+withFlying
+SVar:BuffedBy:Creature.withFlying
+A:AB$ Token | Cost$ 2 W U | TokenAmount$ 1 | TokenScript$ w_1_1_cat_bird_flying | TokenOwner$ You | SpellDescription$ Create a 1/1 white Cat Bird creature token with flying.
+Oracle:Flying\nSkycat Sovereign gets +1/+1 for each other creature you control with flying.\n{2}{W}{U}: Create a 1/1 white Cat Bird creature token with flying.
diff --git a/forge-gui/res/cardsfolder/upcoming/sleeper_dart.txt b/forge-gui/res/cardsfolder/upcoming/sleeper_dart.txt
new file mode 100755
index 00000000000..fe465e01231
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/sleeper_dart.txt
@@ -0,0 +1,7 @@
+Name:Sleeper Dart
+ManaCost:2
+Types:Artifact
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw a card.
+SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1
+A:AB$ Pump | Cost$ T Sac<1/CARDNAME> | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ HIDDEN This card doesn't untap during your next untap step. | Permanent$ True | IsCurse$ True | SpellDescription$ Target creature doesn't untap during its controller's next untap step.
+Oracle:When Sleeper Dart enters the battlefield, draw a card.\n{T}, Sacrifice Sleeper Dart: Target creature doesn't untap during its controller's next untap step.
diff --git a/forge-gui/res/cardsfolder/upcoming/slitherwisp.txt b/forge-gui/res/cardsfolder/upcoming/slitherwisp.txt
new file mode 100755
index 00000000000..74be44428e8
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/slitherwisp.txt
@@ -0,0 +1,9 @@
+Name:Slitherwisp
+ManaCost:U B B
+Types:Creature Elemental Nightmare
+PT:3/2
+K:Flash
+T:Mode$ SpellCast | ValidCard$ Card.hasKeywordFlash+Other | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever you cast another spell with flash, you draw a card and each opponent loses 1 life.
+SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBLoseLife
+SVar:DBLoseLife:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 1
+Oracle:Flash\nWhenever you cast another spell with flash, you draw a card and each opponent loses 1 life.
diff --git a/forge-gui/res/cardsfolder/upcoming/snare_tactician.txt b/forge-gui/res/cardsfolder/upcoming/snare_tactician.txt
new file mode 100755
index 00000000000..f38dc39050d
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/snare_tactician.txt
@@ -0,0 +1,8 @@
+Name:Snare Tactician
+ManaCost:2 W
+Types:Creature Human Soldier
+PT:2/3
+T:Mode$ Cycled | ValidCard$ Card.YouOwn | TriggerZones$ Battlefield | Execute$ TrigTap | TriggerDescription$ Whenever you cycle a card, tap target creature an opponent controls.
+SVar:TrigTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Choose target creature an opponent controls
+SVar:PlayMain1:TRUE
+Oracle:Whenever you cycle a card, tap target creature an opponent controls.
diff --git a/forge-gui/res/cardsfolder/upcoming/solid_footing.txt b/forge-gui/res/cardsfolder/upcoming/solid_footing.txt
new file mode 100755
index 00000000000..a63503ebdf9
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/solid_footing.txt
@@ -0,0 +1,9 @@
+Name:Solid Footing
+ManaCost:W
+Types:Enchantment Aura
+K:Flash
+K:Enchant creature
+A:SP$ Attach | Cost$ W | ValidTgts$ Creature | TgtPrompt$ Select target creature | AILogic$ Pump
+S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 1 | AddToughness$ 1 | Description$ Enchanted creature gets +1/+1.
+S:Mode$ Continuous | Affected$ Creature.EnchantedBy+withVigilance | AddHiddenKeyword$ CARDNAME assigns combat damage equal to its toughness rather than its power | Description$ As long as enchanted creature has vigilance, it assigns combat damage equal to its toughness rather than its power.
+Oracle:Flash\nEnchant creature\nEnchanted creature gets +1/+1.\nAs long as enchanted creature has vigilance, it assigns combat damage equal to its toughness rather than its power.
diff --git a/forge-gui/res/cardsfolder/upcoming/song_of_creation.txt b/forge-gui/res/cardsfolder/upcoming/song_of_creation.txt
new file mode 100755
index 00000000000..31ec7ee9007
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/song_of_creation.txt
@@ -0,0 +1,9 @@
+Name:Song of Creation
+ManaCost:1 G U R
+Types:Enchantment
+S:Mode$ Continuous | Affected$ You | AdjustLandPlays$ 1 | Description$ You may play an additional land on each of your turns.
+T:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever you cast a spell, draw two cards.
+SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 2
+T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDiscard | TriggerDescription$ At the beginning of your end step, discard your hand.
+SVar:TrigDiscard:DB$ Discard | Mode$ Hand | Defined$ You
+Oracle:You may play an additional land on each of your turns.\nWhenever you cast a spell, draw two cards.\nAt the beginning of your end step, discard your hand.
diff --git a/forge-gui/res/cardsfolder/upcoming/sonorous_howlbonder.txt b/forge-gui/res/cardsfolder/upcoming/sonorous_howlbonder.txt
new file mode 100755
index 00000000000..53ef5c6700c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/sonorous_howlbonder.txt
@@ -0,0 +1,8 @@
+Name:Sonorous Howlbonder
+ManaCost:1 B/R B/R
+Types:Creature Human Warrior
+PT:2/2
+K:Menace
+S:Mode$ Continuous | Affected$ Creature.YouCtrl+withMenace | AddHiddenKeyword$ CantBeBlockedByAmount LT3 | Description$ Each creature you control with menace can't be blocked except by three or more creatures.
+SVar:PlayMain1:TRUE
+Oracle:Menace\nEach creature you control with menace can't be blocked except by three or more creatures.
diff --git a/forge-gui/res/cardsfolder/upcoming/spelleater_wolverine.txt b/forge-gui/res/cardsfolder/upcoming/spelleater_wolverine.txt
new file mode 100755
index 00000000000..84b76059dbe
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/spelleater_wolverine.txt
@@ -0,0 +1,7 @@
+Name:Spelleater Wolverine
+ManaCost:2 R
+Types:Creature Wolverine
+PT:3/2
+S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Double Strike | CheckSVar$ X | SVarCompare$ GE3 | Description$ CARDNAME has double strike as long as there are three or more instant and/or sorcery cards in your graveyard.
+SVar:X:Count$ValidGraveyard Instant.YouOwn,Sorcery.YouOwn
+Oracle:Spelleater Wolverine has double strike as long as there are three or more instant and/or sorcery cards in your graveyard.
diff --git a/forge-gui/res/cardsfolder/upcoming/splendor_mare.txt b/forge-gui/res/cardsfolder/upcoming/splendor_mare.txt
new file mode 100755
index 00000000000..5faba38914e
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/splendor_mare.txt
@@ -0,0 +1,9 @@
+Name:Splendor Mare
+ManaCost:2 W
+Types:Creature Elk Unicorn
+PT:3/3
+K:Lifelink
+K:Cycling:1 W
+T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When you cycle CARDNAME, put a lifelink counter on target creature you control.
+SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ LIFELINK | CounterNum$ 1
+Oracle:Lifelink\nCycling {1}{W} ({1}{W}, Discard this card: Draw a card.)\nWhen you cycle Splendor Mare, put a lifelink counter on target creature you control.
diff --git a/forge-gui/res/cardsfolder/upcoming/spontaneous_flight.txt b/forge-gui/res/cardsfolder/upcoming/spontaneous_flight.txt
new file mode 100755
index 00000000000..29e3915630c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/spontaneous_flight.txt
@@ -0,0 +1,6 @@
+Name:Spontaneous Flight
+ManaCost:2 W
+Types:Instant
+A:SP$ Pump | Cost$ 2 W | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +2 | NumDef$ +2 | SubAbility$ DBPutCounter | SpellDescription$ Target creature gets +2/+2 until end of turn. Put a flying counter on it.
+SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ FLYING | CounterNum$ 1
+Oracle:Target creature gets +2/+2 until end of turn. Put a flying counter on it.
diff --git a/forge-gui/res/cardsfolder/upcoming/springjaw_trap.txt b/forge-gui/res/cardsfolder/upcoming/springjaw_trap.txt
new file mode 100755
index 00000000000..1e4eaabac55
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/springjaw_trap.txt
@@ -0,0 +1,6 @@
+Name:Springjaw Trap
+ManaCost:1
+Types:Artifact
+K:Flash
+A:AB$ DealDamage | Cost$ 4 T Sac<1/CARDNAME> | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 3 | SpellDescription$ CARDNAME deals 3 damage to any target.
+Oracle:Flash\n{4}, {T}, Sacrifice Springjaw Trap: It deals 3 damage to any target.
diff --git a/forge-gui/res/cardsfolder/upcoming/sprite_dragon.txt b/forge-gui/res/cardsfolder/upcoming/sprite_dragon.txt
new file mode 100755
index 00000000000..5cf45681438
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/sprite_dragon.txt
@@ -0,0 +1,10 @@
+Name:Sprite Dragon
+ManaCost:U R
+Types:Creature Faerie Dragon
+PT:1/1
+K:Flying
+K:Haste
+T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, put a +1/+1 counter on CARDNAME.
+SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
+SVar:BuffedBy:nonCreature
+Oracle:Flying, haste\nWhenever you cast a noncreature spell, put a +1/+1 counter on Sprite Dragon.
diff --git a/forge-gui/res/cardsfolder/upcoming/startling_development.txt b/forge-gui/res/cardsfolder/upcoming/startling_development.txt
new file mode 100755
index 00000000000..01fa738a037
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/startling_development.txt
@@ -0,0 +1,6 @@
+Name:Startling Development
+ManaCost:1 U
+Types:Instant
+A:SP$ Animate | Cost$ 1 U | ValidTgts$ Creature | TgtPrompt$ Select target creature | Colors$ Blue | OverwriteColors$ True | Types$ Serpent | RemoveCreatureTypes$ True | Power$ 4 | Toughness$ 4 | StackDescription$ SpellDescription | SpellDescription$ Until end of turn, target creature becomes a blue Serpent with base power and toughness 4/4.
+K:Cycling:1
+Oracle:Until end of turn, target creature becomes a blue Serpent with base power and toughness 4/4.\nCycling {1} ({1}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/stormwild_capridor.txt b/forge-gui/res/cardsfolder/upcoming/stormwild_capridor.txt
new file mode 100755
index 00000000000..3bbda0bd63b
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/stormwild_capridor.txt
@@ -0,0 +1,10 @@
+Name:Stormwild Capridor
+ManaCost:2 W
+Types:Creature Bird Goat
+PT:1/3
+K:Flying
+R:Event$ DamageDone | IsCombat$ False | ActiveZones$ Battlefield | ValidTarget$ Card.Self | ReplaceWith$ Counters | Description$ If noncombat damage would be dealt to CARDNAME, prevent that damage. Put a +1/+1 counter on CARDNAME for each 1 damage prevented this way.
+SVar:Counters:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ X | References$ X | Defined$ Self
+SVar:X:ReplaceCount$DamageAmount
+DeckHas:Ability$Counters
+Oracle:Flying\nIf noncombat damage would be dealt to Stormwild Capridor, prevent that damage. Put a +1/+1 counter on Stormwild Capridor for each 1 damage prevented this way.
diff --git a/forge-gui/res/cardsfolder/upcoming/sudden_spinnerets.txt b/forge-gui/res/cardsfolder/upcoming/sudden_spinnerets.txt
new file mode 100755
index 00000000000..80e1a932cb1
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/sudden_spinnerets.txt
@@ -0,0 +1,7 @@
+Name:Sudden Spinnerets
+ManaCost:G
+Types:Instant
+A:SP$ Pump | Cost$ G | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +1 | NumDef$ +3 | SubAbility$ DBPutCounter | SpellDescription$ Target creature gets +1/+3 until end of turn. Put a reach counter on it. Untap it.
+SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ REACH | CounterNum$ 1 | SubAbility$ DBUntap
+SVar:DBUntap:DB$ Untap | Defined$ Targeted
+Oracle:Target creature gets +1/+3 until end of turn. Put a reach counter on it. Untap it.
diff --git a/forge-gui/res/cardsfolder/upcoming/suffocating_fumes.txt b/forge-gui/res/cardsfolder/upcoming/suffocating_fumes.txt
new file mode 100755
index 00000000000..4746508e6e4
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/suffocating_fumes.txt
@@ -0,0 +1,6 @@
+Name:Suffocating Fumes
+ManaCost:2 B
+Types:Instant
+A:SP$ PumpAll | Cost$ 2 B | ValidCards$ Creature.OppCtrl | NumAtt$ -1 | NumDef$ -1 | IsCurse$ True | SpellDescription$ Creatures your opponents control get -1/-1 until end of turn.
+K:Cycling:2
+Oracle:Creatures your opponents control get -1/-1 until end of turn.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/survivors_bond.txt b/forge-gui/res/cardsfolder/upcoming/survivors_bond.txt
new file mode 100755
index 00000000000..334ff596a4e
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/survivors_bond.txt
@@ -0,0 +1,7 @@
+Name:Survivors' Bond
+ManaCost:1 G
+Types:Sorcery
+A:SP$ Charm | Cost$ 1 G | MinCharmNum$ 1 | CharmNum$ 2 | Choices$ Human,NonHuman
+SVar:Human:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TgtPrompt$ Select target Human creature card in your graveyard | ValidTgts$ Creature.Human+YouOwn | SpellDescription$ Return target Human creature card from your graveyard to your hand.
+SVar:NonHuman:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TgtPrompt$ Select target non-Human creature card in your graveyard | ValidTgts$ Creature.nonHuman+YouOwn | SpellDescription$ Return target non-Human creature card from your graveyard to your hand.
+Oracle:Choose one or both —\n• Return target Human creature card from your graveyard to your hand.\n• Return target non-Human creature card from your graveyard to your hand.
diff --git a/forge-gui/res/cardsfolder/upcoming/swallow_whole.txt b/forge-gui/res/cardsfolder/upcoming/swallow_whole.txt
new file mode 100755
index 00000000000..9dcf5e13baa
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/swallow_whole.txt
@@ -0,0 +1,6 @@
+Name:Swallow Whole
+ManaCost:W
+Types:Sorcery
+A:SP$ ChangeZone | Cost$ W tapXType<1/Creature> | ValidTgts$ Creature.tapped | TgtPrompt$ Select target tapped creature | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBPutCounter | SpellDescription$ As an additional cost to cast this spell, tap an untapped creature you control. Exile target tapped creature.
+SVar:DBPutCounter:DB$ PutCounter | Defined$ Tapped | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on the creature tapped to cast this spell.
+Oracle:As an additional cost to cast this spell, tap an untapped creature you control.\nExile target tapped creature. Put a +1/+1 counter on the creature tapped to cast this spell.
diff --git a/forge-gui/res/cardsfolder/upcoming/tentative_connection.txt b/forge-gui/res/cardsfolder/upcoming/tentative_connection.txt
new file mode 100755
index 00000000000..9fb6c5271ee
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/tentative_connection.txt
@@ -0,0 +1,6 @@
+Name:Tentative Connection
+ManaCost:3 R
+Types:Sorcery
+S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ 3 | EffectZone$ All | IsPresent$ Creature.YouCtrl+withMenace | Description$ This spell costs {3} less to cast if you control a creature with menace.
+A:SP$ GainControl | Cost$ 3 R | ValidTgts$ Creature | TgtPrompt$ Select target creature | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | StackDescription$ SpellDescription | SpellDescription$ Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.
+Oracle:This spell costs {3} less to cast if you control a creature with menace.\nGain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.
diff --git a/forge-gui/res/cardsfolder/upcoming/thieving_otter.txt b/forge-gui/res/cardsfolder/upcoming/thieving_otter.txt
new file mode 100755
index 00000000000..0a67e4425e9
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/thieving_otter.txt
@@ -0,0 +1,7 @@
+Name:Thieving Otter
+ManaCost:2 U
+Types:Creature Otter
+PT:2/2
+T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever CARDNAME deals damage to an opponent, draw a card.
+SVar:TrigDraw:DB$Draw | Defined$ You | NumCards$ 1
+Oracle:Whenever Thieving Otter deals damage to an opponent, draw a card.
diff --git a/forge-gui/res/cardsfolder/upcoming/thwart_the_enemy.txt b/forge-gui/res/cardsfolder/upcoming/thwart_the_enemy.txt
new file mode 100755
index 00000000000..d84edcafe88
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/thwart_the_enemy.txt
@@ -0,0 +1,7 @@
+Name:Thwart the Enemy
+ManaCost:2 G
+Types:Instant
+A:SP$ Effect | Cost$ 2 G | ReplacementEffects$ RPrevent | AILogic$ Fog | SpellDescription$ Prevent all damage that would be dealt this turn by creatures your opponents control.
+SVar:RPrevent:Event$ DamageDone | Prevent$ True | ActiveZones$ Command | ValidSource$ Creature.OppCtrl | Description$ Prevent all damage that would be dealt this turn by creatures your opponents control.
+AI:RemoveDeck:All
+Oracle:Prevent all damage that would be dealt this turn by creatures your opponents control.
diff --git a/forge-gui/res/cardsfolder/upcoming/titanoth_rex.txt b/forge-gui/res/cardsfolder/upcoming/titanoth_rex.txt
new file mode 100755
index 00000000000..8379bdf041c
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/titanoth_rex.txt
@@ -0,0 +1,9 @@
+Name:Titanoth Rex
+ManaCost:7 G G
+Types:Creature Dinosaur Beast
+PT:11/11
+K:Trample
+K:Cycling:1 G
+T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When you cycle CARDNAME, put a trample counter on target creature you control.
+SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ TRAMPLE | CounterNum$ 1
+Oracle:Trample\nCycling {1}{G} ({1}{G}, Discard this card: Draw a card.)\nWhen you cycle Titanoth Rex, put a trample counter on target creature you control.
diff --git a/forge-gui/res/cardsfolder/upcoming/unbreakable_bond.txt b/forge-gui/res/cardsfolder/upcoming/unbreakable_bond.txt
new file mode 100755
index 00000000000..e925363c634
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/unbreakable_bond.txt
@@ -0,0 +1,6 @@
+Name:Unbreakable Bond
+ManaCost:4 B
+Types:Sorcery
+A:SP$ ChangeZone | Cost$ 4 B | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouOwn | SubAbility$ DBPutCounter | SpellDescription$ Return target creature card from your graveyard to the battlefield with a lifelink counter on it.
+SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ LIFELINK | CounterNum$ 1
+Oracle:Return target creature card from your graveyard to the battlefield with a lifelink counter on it.
diff --git a/forge-gui/res/cardsfolder/upcoming/unexpected_fangs.txt b/forge-gui/res/cardsfolder/upcoming/unexpected_fangs.txt
new file mode 100755
index 00000000000..fe6371d7f02
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/unexpected_fangs.txt
@@ -0,0 +1,7 @@
+Name:Unexpected Fangs
+ManaCost:1 B
+Types:Instant
+A:SP$ PutCounter | Cost$ 1 B | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPutCounter | SpellDescription$ Put a +1/+1 counter and a lifelink counter on target creature.
+SVar:DBPutCounter:DB$ PutCounter | CounterType$ LIFELINK | CounterNum$ 1 | Defined$ Targeted
+DeckHas:Ability$Counters
+Oracle:Put a +1/+1 counter and a lifelink counter on target creature.
diff --git a/forge-gui/res/cardsfolder/upcoming/vivien_monsters_advocate.txt b/forge-gui/res/cardsfolder/upcoming/vivien_monsters_advocate.txt
new file mode 100755
index 00000000000..03a10bd91aa
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/vivien_monsters_advocate.txt
@@ -0,0 +1,19 @@
+Name:Vivien, Monsters' Advocate
+ManaCost:3 G G
+Types:Legendary Planeswalker Vivien
+Loyalty:3
+S:Mode$ Continuous | Affected$ Card.TopLibrary+YouCtrl | AffectedZone$ Library | MayLookAt$ You | Description$ You may look at the top card of your library any time.
+S:Mode$ Continuous | Affected$ Creature.TopLibrary+YouCtrl | MayPlay$ True | AffectedZone$ Library | Description$ You may cast creature spells from the top of your library.
+SVar:NonStackingEffect:True
+A:AB$ Token | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenScript$ g_3_3_beast | TokenOwner$ You | LegacyImage$ g 3 3 beast iko | RememberTokens$ True | SubAbility$ DBGenericChoice | SpellDescription$ Create a 3/3 green Beast creature token. Put your choice of a vigilance counter, a reach counter, or a trample counter on it.
+SVar:DBGenericChoice:DB$ GenericChoice | Defined$ You | Choices$ Vigilance,Reach,Trample
+SVar:Reach:DB$ PutCounter | Choices$ Card.IsRemembered | ChoiceTitle$ Choose a token to put a reach counter on | CounterType$ REACH | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Reach
+SVar:Trample:DB$ PutCounter | Choices$ Card.IsRemembered | ChoiceTitle$ Choose a token to put a trample counter on | CounterType$ TRAMPLE | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Trample
+SVar:Vigilance:DB$ PutCounter | Choices$ Card.IsRemembered | ChoiceTitle$ Choose a token to put a vigilance counter on | CounterType$ VIGILANCE | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Vigilance
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
+A:AB$ Effect | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | Triggers$ TrigSearch | SVars$ DBSearch,X | SpellDescription$ When you cast your next creature spell this turn, search your library for a creature card with lesser converted mana cost, put it onto the battlefield, then shuffle your library.
+SVar:TrigSearch:Mode$ SpellCast | ValidCard$ Creature | ValidActivatingPlayer$ You | OneOff$ True | TriggerZones$ Command | Execute$ DBSearch | TriggerDescription$ When you cast your next creature spell this turn, search your library for a creature card with lesser converted mana cost, put it onto the battlefield, then shuffle your library.
+SVar:DBSearch:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.cmcLTX | References$ X | ChangeNum$ 1
+SVar:X:TriggerCount$CastSACMC
+DeckHas:Ability$Token & Ability$Counters
+Oracle:You may look at the top card of your library any time.\nYou may cast creature spells from the top of your library.\n[+1]: Create a 3/3 green Beast creature token. Put your choice of a vigilance counter, a reach counter, or a trample counter on it.\n−2: When you cast your next creature spell this turn, search your library for a creature card with lesser converted mana cost, put it onto the battlefield, then shuffle your library.
diff --git a/forge-gui/res/cardsfolder/upcoming/void_beckoner.txt b/forge-gui/res/cardsfolder/upcoming/void_beckoner.txt
new file mode 100755
index 00000000000..1ecf14e8418
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/void_beckoner.txt
@@ -0,0 +1,9 @@
+Name:Void Beckoner
+ManaCost:6 B B
+Types:Creature Nightmare Horror
+PT:8/8
+K:Deathtouch
+K:Cycling:2 B
+T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When you cycle CARDNAME, put a deathtouch counter on target creature you control.
+SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ DEATHTOUCH | CounterNum$ 1
+Oracle:Deathtouch\nCycling {2}{B} ({2}{B}, Discard this card: Draw a card.)\nWhen you cycle Void Beckoner, put a deathtouch counter on target creature you control.
diff --git a/forge-gui/res/cardsfolder/upcoming/voracious_greatshark.txt b/forge-gui/res/cardsfolder/upcoming/voracious_greatshark.txt
new file mode 100755
index 00000000000..3af9def8de2
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/voracious_greatshark.txt
@@ -0,0 +1,8 @@
+Name:Voracious Greatshark
+ManaCost:3 U U
+Types:Creature Shark
+PT:5/4
+K:Flash
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigCounter | TriggerDescription$ When CARDNAME enters the battlefield, counter target artifact or creature spell.
+SVar:TrigCounter:DB$ Counter | TargetType$ Spell | ValidTgts$ Artifact,Creature | TgtPrompt$ Select target artifact or creature spell
+Oracle:Flash\nWhen Voracious Greatshark enters the battlefield, counter target artifact or creature spell.
diff --git a/forge-gui/res/cardsfolder/upcoming/weaponize_the_monsters.txt b/forge-gui/res/cardsfolder/upcoming/weaponize_the_monsters.txt
new file mode 100755
index 00000000000..f2b074c195d
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/weaponize_the_monsters.txt
@@ -0,0 +1,5 @@
+Name:Weaponize the Monsters
+ManaCost:R
+Types:Enchantment
+A:AB$ DealDamage | Cost$ 2 Sac<1/Creature> | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 2 | SpellDescription$ CARDNAME deals 2 damage to any target.
+Oracle:{2}, Sacrifice a creature: Weaponize the Monsters deals 2 damage to any target.
diff --git a/forge-gui/res/cardsfolder/upcoming/whirlwind_of_thought.txt b/forge-gui/res/cardsfolder/upcoming/whirlwind_of_thought.txt
new file mode 100755
index 00000000000..9244242bbf1
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/whirlwind_of_thought.txt
@@ -0,0 +1,7 @@
+Name:Whirlwind of Thought
+ManaCost:1 U R W
+Types:Enchantment
+T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, draw a card.
+SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1
+SVar:BuffedBy:Card.nonCreature
+Oracle:Whenever you cast a noncreature spell, draw a card.
diff --git a/forge-gui/res/cardsfolder/upcoming/whisper_squad.txt b/forge-gui/res/cardsfolder/upcoming/whisper_squad.txt
new file mode 100755
index 00000000000..db645dd8a23
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/whisper_squad.txt
@@ -0,0 +1,8 @@
+Name:Whisper Squad
+ManaCost:B
+Types:Creature Human Soldier
+PT:1/1
+A:AB$ ChangeZone | Cost$ 1 B | Origin$ Library | Destination$ Battlefield | ChangeType$ Card.namedWhisper Squad | ChangeNum$ 1 | Tapped$ True | StackDescription$ SpellDescription | SpellDescription$ Search your library for a card named CARDNAME, put it onto the battlefield tapped, then shuffle your library.
+DeckHints:Type$Human
+DeckNeeds:Name$Whisper Squad
+Oracle:{1}{B}: Search your library for a card named Whisper Squad, put it onto the battlefield tapped, then shuffle your library.
diff --git a/forge-gui/res/cardsfolder/upcoming/will_of_the_all_hunter.txt b/forge-gui/res/cardsfolder/upcoming/will_of_the_all_hunter.txt
new file mode 100755
index 00000000000..09c26a9cc89
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/will_of_the_all_hunter.txt
@@ -0,0 +1,10 @@
+Name:Will of the All-Hunter
+ManaCost:1 W
+Types:Instant
+A:SP$ Pump | Cost$ 1 W | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBBranch | StackDescription$ SpellDescription | SpellDescription$ Target creature gets +2/+2 until end of turn. If it's blocking, instead put two +1/+1 counters on it.
+SVar:DBBranch:DB$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$ EQ1 | References$ X | TrueSubAbility$ PutCounter | FalseSubAbility$ Pump
+SVar:PutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ P1P1 | CounterNum$ 2
+SVar:Pump:DB$ Pump | Defined$ Targeted | NumAtt$ 2 | NumDef$ 2
+SVar:X:Targeted$Valid Creature.blocking
+K:Cycling:2
+Oracle:Target creature gets +2/+2 until end of turn. If it's blocking, instead put two +1/+1 counters on it.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/wilt.txt b/forge-gui/res/cardsfolder/upcoming/wilt.txt
new file mode 100755
index 00000000000..533783fa3da
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/wilt.txt
@@ -0,0 +1,6 @@
+Name:Wilt
+ManaCost:1 G
+Types:Instant
+A:SP$ Destroy | Cost$ 1 G | ValidTgts$ Artifact,Enchantment | TgtPrompt$ Select target artifact or enchantment | SpellDescription$ Destroy target artifact or enchantment.
+K:Cycling:2
+Oracle:Destroy target artifact or enchantment.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/wingfold_pteron.txt b/forge-gui/res/cardsfolder/upcoming/wingfold_pteron.txt
new file mode 100755
index 00000000000..142c64fa85e
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/wingfold_pteron.txt
@@ -0,0 +1,10 @@
+Name:Wingfold Pteron
+ManaCost:5 U
+Types:Creature Dinosaur
+PT:3/6
+K:ETBReplacement:Other:CounterChoice
+SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Flying,Hexproof | SpellDescription$ CARDNAME enters the battlefield with your choice of a flying counter or a hexproof counter on it.
+SVar:Flying:DB$ PutCounter | CounterType$ FLYING | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a flying counter on it
+SVar:Hexproof:DB$ PutCounter | CounterType$ HEXPROOF | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a hexproof counter on it
+DeckHas:Ability$Counters
+Oracle:Wingfold Pteron enters the battlefield with your choice of a flying counter or a hexproof counter on it. (A creature with hexproof can't be the target of spells or abilities your opponents control.)
diff --git a/forge-gui/res/cardsfolder/upcoming/wingspan_mentor.txt b/forge-gui/res/cardsfolder/upcoming/wingspan_mentor.txt
new file mode 100755
index 00000000000..0c6fd836596
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/wingspan_mentor.txt
@@ -0,0 +1,10 @@
+Name:Wingspan Mentor
+ManaCost:2 U
+Types:Creature Human Wizard
+PT:1/3
+T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Self | Execute$ TrigPut | TriggerDescription$ When CARDNAME enters the battlefield, put a flying counter on target non-Human creature you control.
+SVar:TrigPut:DB$PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature | CounterType$ FLYING | CounterNum$ 1
+A:AB$ PutCounterAll | Cost$ 2 U T | ValidCards$ Creature.YouCtrl+withFlying | CounterType$ P1P1 | CounterNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Put a +1/+1 counter on each creature you control with flying.
+SVar:PlayMain1:TRUE
+DeckHas:Ability$Counters
+Oracle:When Wingspan Mentor enters the battlefield, put a flying counter on target non-Human creature you control.\n{2}{U}, {T}: Put a +1/+1 counter on each creature you control with flying.
diff --git a/forge-gui/res/cardsfolder/upcoming/yidaro_wandering_monster.txt b/forge-gui/res/cardsfolder/upcoming/yidaro_wandering_monster.txt
new file mode 100755
index 00000000000..c18dd785670
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/yidaro_wandering_monster.txt
@@ -0,0 +1,14 @@
+Name:Yidaro, Wandering Monster
+ManaCost:5 R R
+Types:Legendary Creature Dinosaur Turtle
+PT:8/8
+K:Haste
+K:Trample
+K:Cycling:1 R
+T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ DBEffect | TriggerDescription$ When you cycle CARDNAME, shuffle it into your library from your graveyard. If you've cycled a card named CARDNAME four or more times this game, put it onto the battlefield from your graveyard instead. (Do this before you draw.)
+SVar:DBEffect:DB$ Effect | Duration$ Permanent | Name$ Wandering Effect | SubAbility$ DBBranch
+SVar:DBBranch:DB$ Branch | References$ X | BranchConditionSVar$ X | BranchConditionSVarCompare$ GE4 | TrueSubAbility$ DBChangeZone1 | FalseSubAbility$ DBChangeZone2
+SVar:DBChangeZone1:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard
+SVar:DBChangeZone2:DB$ ChangeZone | Origin$ Graveyard | Destination$ Library | Shuffle$ True | Defined$ TriggeredCard
+SVar:X:Count$ValidCommand Effect.YouCtrl+namedWandering Effect
+Oracle:Trample, haste\nCycling {1}{R}\nWhen you cycle Yidaro, Wandering Monster, shuffle it into your library from your graveyard. If you've cycled a card named Yidaro, Wandering Monster four or more times this game, put it onto the battlefield from your graveyard instead. (Do this before you draw.)
diff --git a/forge-gui/res/cardsfolder/upcoming/zagoth_crystal.txt b/forge-gui/res/cardsfolder/upcoming/zagoth_crystal.txt
new file mode 100755
index 00000000000..38828a97c16
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/zagoth_crystal.txt
@@ -0,0 +1,8 @@
+Name:Zagoth Crystal
+ManaCost:3
+Types:Artifact
+A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add {B}.
+A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add {G}.
+A:AB$ Mana | Cost$ T | Produced$ U | SpellDescription$ Add {U}.
+K:Cycling:2
+Oracle:{T}: Add {B}, {G}, or {U}.\nCycling {2} ({2}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/zagoth_triome.txt b/forge-gui/res/cardsfolder/upcoming/zagoth_triome.txt
new file mode 100755
index 00000000000..68dd45d5710
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/zagoth_triome.txt
@@ -0,0 +1,6 @@
+Name:Zagoth Triome
+ManaCost:no cost
+Types:Land Swamp Forest Island
+K:CARDNAME enters the battlefield tapped.
+K:Cycling:3
+Oracle:({T}: Add {B}, {G}, or {U}.)\nZagoth Triome enters the battlefield tapped.\nCycling {3} ({3}, Discard this card: Draw a card.)
diff --git a/forge-gui/res/cardsfolder/upcoming/zenith_flare.txt b/forge-gui/res/cardsfolder/upcoming/zenith_flare.txt
new file mode 100755
index 00000000000..991042b4dcf
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/zenith_flare.txt
@@ -0,0 +1,7 @@
+Name:Zenith Flare
+ManaCost:2 R W
+Types:Instant
+A:SP$ DealDamage | Cost$ 2 R W | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | References$ X | SubAbility$ DBGainLife | SpellDescription$ CARDNAME deals X damage to any target and you gain X life, where X is the number of cards with a cycling ability in your graveyard.
+SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X | References$ X
+SVar:X:Count$ValidGraveyard Card.YouOwn+withCycling,Card.YouOwn+withTypeCycling
+Oracle:Zenith Flare deals X damage to any target and you gain X life, where X is the number of cards with a cycling ability in your graveyard.
diff --git a/forge-gui/res/cardsfolder/upcoming/zilortha_strength_incarnate.txt b/forge-gui/res/cardsfolder/upcoming/zilortha_strength_incarnate.txt
new file mode 100644
index 00000000000..37a13f585de
--- /dev/null
+++ b/forge-gui/res/cardsfolder/upcoming/zilortha_strength_incarnate.txt
@@ -0,0 +1,7 @@
+Name:Zilortha, Strength Incarnate
+ManaCost:3 R G
+Types:Legendary Creature Dinosaur
+K:Trample
+PT:7/3
+S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddHiddenKeyword$ Lethal damage dealt to CARDNAME is determined by its power rather than its toughness. | Description$ Lethal damage dealt to creatures you control is determined by their power rather than their toughness.
+Oracle:Trample\nLethal damage dealt to creatures you control is determined by their power rather than their toughness.
diff --git a/forge-gui/res/cube/MTGACube.dck b/forge-gui/res/cube/MTGACube.dck
new file mode 100644
index 00000000000..4b881f5b9f4
--- /dev/null
+++ b/forge-gui/res/cube/MTGACube.dck
@@ -0,0 +1,568 @@
+[metadata]
+Name=MTGACube
+[Avatar]
+
+[Main]
+1 Adanto Vanguard|XLN
+1 Admiral's Order|RIX
+1 Agent of Treachery|M20
+1 Agonizing Remorse|THB
+1 Ajani's Pridemate|IMA
+1 Ajani, Strength of the Pride|M20
+1 Ajani, the Greathearted|WAR|1
+1 Alirios, Enraptured|THB
+1 All That Glitters|ELD
+1 Alseid of Life's Bounty|THB|2
+1 Anax, Hardened in the Forge|THB|2
+1 Angrath's Rampage|WAR
+1 Angrath, Captain of Chaos|WAR|2
+1 Animating Faerie|ELD|1
+1 Anticipate|M20
+1 Aphemia, the Cacophony|THB|1
+1 Arasta of the Endless Web|THB|1
+1 Arcane Encyclopedia|M19
+1 Arcanist's Owl|ELD
+1 Arch of Orazca|RIX
+1 Archon of Sun's Grace|THB|1
+1 Ardenvale Tactician|ELD|1
+1 Arguel's Blood Fast|V17
+1 Arrester's Zeal|RNA
+1 Aryel, Knight of Windgrace|DOM
+1 Ashiok, Dream Render|WAR|1
+1 Ashiok, Nightmare Muse|THB|1
+1 Assassin's Trophy|GRN
+1 Atris, Oracle of Half-Truths|THB|1
+1 Audacious Thief|M20
+1 Augur of Bolas|WAR
+1 Aurelia, Exemplar of Justice|GRN
+1 Baffling End|RIX
+1 Bake into a Pie|ELD
+1 Banefire|M19
+1 Banishing Light|THB
+1 Barkhide Troll|M20
+1 Barren Moor|C19
+1 Beanstalk Giant|ELD|2
+1 Beast Whisperer|GRN
+1 Benthic Biomancer|RNA
+1 Biogenic Ooze|RNA
+1 Blackblade Reforged|SS2
+1 Blacklance Paragon|ELD|2
+1 Blade Juggler|RNA
+1 Blast Zone|WAR
+1 Blink of an Eye|DOM
+1 Blood Aspirant|THB
+1 Blood Crypt|RNA
+1 Blood Divination|M19
+1 Blood for Bones|M20
+1 Bloodfell Caves|C19
+1 Bloom Hulk|WAR
+1 Blossoming Sands|C19
+1 Board the Weatherlight|DOM
+1 Bolas's Citadel|WAR
+1 Bond of Insight|WAR
+1 Bonecrusher Giant|ELD|1
+1 Brain Maggot|JOU
+1 Brazen Borrower|ELD|2
+1 Breeding Pool|RNA
+1 Brineborn Cutthroat|M20
+1 Burning-Tree Emissary|GK2
+1 Captain Lannery Storm|XLN
+1 Captain Sisay|FVL
+1 Captivating Crew|GNT
+1 Careless Celebrant|THB
+1 Cast Down|DOM
+1 Castle Ardenvale|ELD|2
+1 Castle Embereth|ELD|1
+1 Castle Garenbrig|ELD|2
+1 Castle Locthwain|ELD|2
+1 Castle Vantress|ELD|1
+1 Casualties of War|WAR
+1 Cauldron Familiar|ELD
+1 Cauldron's Gift|ELD
+1 Cavalcade of Calamity|RNA
+1 Cavalier of Dawn|M20
+1 Cavalier of Flame|M20
+1 Cavalier of Night|M20
+1 Cavalier of Thorns|M20
+1 Cavalry Drillmaster|M19
+1 Chainweb Aracnir|THB
+1 Chandra's Pyrohelix|WAR
+1 Chandra, Acolyte of Flame|M20
+1 Chandra, Awakened Inferno|M20
+1 Charging Monstrosaur|XLN
+1 Charming Prince|ELD|2
+1 Chart a Course|XLN
+1 Chemister's Insight|C19
+1 Chromatic Lantern|GRN
+1 Clifftop Retreat|DOM
+1 Clockwork Servant|ELD
+1 Cloudkin Seer|M20
+1 Colossal Majesty|C19
+1 Command the Dreadhorde|WAR
+1 Commence the Endgame|WAR
+1 Conclave Tribunal|GRN
+1 Corpse Knight|M20
+1 Crash Through|M19
+1 Crucible of Worlds|M19
+1 Cryptbreaker|EMN
+1 Cryptic Caves|M20
+1 Curious Obsession|RIX
+1 Dalakos, Crafter of Wonders|THB|2
+1 Dark-Dweller Oracle|M19
+1 Dauntless Bodyguard|DOM
+1 Daxos, Blessed by the Sun|THB|2
+1 Dead Weight|GRN
+1 Deeproot Champion|XLN
+1 Demonlord Belzenlok|DOM
+1 Depose // Deploy|RNA
+1 Despark|WAR
+1 Destiny Spinner|THB
+1 Didn't Say Please|ELD
+1 Dire Fleet Daredevil|RIX
+1 Diregraf Ghoul|M19
+1 Disdainful Stroke|GRN
+1 Disenchant|M20
+1 Disfigure|M20
+1 Dismal Backwater|M20
+1 Dive Down|XLN
+1 Divine Visitation|GRN
+1 Domri, Anarch of Bolas|WAR|1
+1 Doom Whisperer|GRN
+1 Dragonmaster Outcast|C19
+1 Dragonskull Summit|XLN
+1 Drakuseth, Maw of Flames|M20
+1 Dread Presence|M20
+1 Dreadhorde Butcher|WAR
+1 Dreadhorde Invasion|WAR
+1 Dream Trawler|THB|1
+1 Dreamstalker Manticore|THB
+1 Drill Bit|RNA
+1 Drowned Catacomb|XLN
+1 Dryad Greenseeker|M19
+1 Dryad of the Ilysian Grove|THB|1
+1 Dungeon Geists|M20
+1 Duress|M20
+1 Dusk Legion Zealot|A25
+1 Eat to Extinction|THB|2
+1 Electrodominance|RNA
+1 Elspeth Conquers Death|THB
+1 Elspeth's Nightmare|THB
+1 Elspeth, Sun's Nemesis|THB|1
+1 Elvish Reclaimer|M20
+1 Elvish Rejuvenator|M19
+1 Elvish Visionary|BBD
+1 Ember Hauler|M20
+1 Embercleave|ELD|2
+1 Embereth Shieldbreaker|ELD|1
+1 Embodiment of Agonies|M20
+1 Empyrean Eagle|M20
+1 Emry, Lurker of the Loch|ELD|1
+1 End-Raze Forerunners|RNA
+1 Enter the God-Eternals|WAR
+1 Entrancing Lyre|THB
+1 Entrancing Melody|XLN
+1 Erebos's Intervention|THB|1
+1 Erebos, Bleak-Hearted|THB|2
+1 Eternal Taskmaster|WAR
+1 Ethereal Absolution|RNA
+1 Evolution Sage|WAR
+1 Evolving Wilds|C19
+1 Exclusion Mage|M19
+1 Expansion // Explosion|GRN
+1 Experimental Frenzy|GRN
+1 Fabled Passage|ELD|2
+1 Fae of Wishes|ELD|1
+1 Faeburrow Elder|ELD|1
+1 Faerie Formation|ELD
+1 Faerie Guidemother|ELD|1
+1 Fanatical Firebrand|RIX
+1 Fauna Shaman|UMA
+1 Favorable Winds|GNT
+1 Fblthp, the Lost|WAR
+1 Field of Ruin|THB
+1 Field of the Dead|M20
+1 Fiery Cannonade|XLN
+1 Fight with Fire|DOM
+1 Finale of Devastation|WAR
+1 Finale of Eternity|WAR
+1 Finale of Glory|WAR
+1 Find // Finality|GRN
+1 Firemind Vessel|WAR
+1 Fires of Invention|ELD|1
+1 Flame Sweep|M20
+1 Flaxen Intruder|ELD|2
+1 Fling|ELD
+1 Flood of Tears|M20
+1 Folio of Fancies|ELD|2
+1 Foreboding Fruit|ELD
+1 Forgotten Cave|C19
+1 Foulmire Knight|ELD|2
+1 Fountain of Renewal|M19
+1 Frilled Sandwalla|M20
+1 Furious Rise|THB
+1 Gallia of the Endless Dance|THB|1
+1 Garruk, Cursed Huntsman|ELD|1
+1 Ghalta, Primal Hunger|GN2
+1 Ghitu Lavarunner|DOM
+1 Giant Growth|WAR
+1 Giant Killer|ELD|1
+1 Gideon Blackblade|MPS_WAR|1
+1 Gilded Goose|ELD|1
+1 Gilded Lotus|DOM
+1 Gingerbrute|ELD
+1 Glacial Fortress|XLN
+1 Glass Casket|ELD|1
+1 Gleaming Barrier|RIX
+1 Goblin Banneret|GRN
+1 Goblin Cratermaker|GRN
+1 Goblin Electromancer|GRN
+1 Goblin Instigator|M19
+1 Goblin Motivator|M19
+1 Goblin Ruinblaster|ZEN
+1 Goblin Trashmaster|M19
+1 God-Eternal Bontu|WAR
+1 God-Eternal Kefnet|WAR
+1 God-Eternal Oketra|WAR
+1 Godless Shrine|RNA
+1 Gods Willing|M20
+1 Golden Demise|RIX
+1 Golden Egg|ELD
+1 Golos, Tireless Pilgrim|M20
+1 Goreclaw, Terror of Qal Sisma|M19
+1 Graveyard Marshal|M19
+1 Gray Merchant of Asphodel|THB|2
+1 Grim Initiate|WAR
+1 Grow from the Ashes|DOM
+1 Growth Spiral|RNA
+1 Gruul Spellbreaker|RNA
+1 Guild Globe|WAR
+1 Gutterbones|RNA
+1 Guttersnipe|C19
+1 Hallowed Fountain|RNA
+1 Hanged Executioner|M20
+1 Harmonious Archon|ELD|2
+1 Heliod, Sun-Crowned|THB|1
+1 Helm of the Host|DOM
+1 Heraldic Banner|ELD
+1 Hinterland Harbor|DOM
+1 History of Benalia|DOM
+1 Hostage Taker|XLN
+1 Huatli, Warrior Poet|XLN
+1 Hunted Witness|GRN
+1 Hydroid Krasis|RNA
+1 Hypnotic Specter|M10
+1 Hypnotic Sprite|ELD|2
+1 Icy Manipulator|DOM
+1 Ilharg, the Raze-Boar|WAR
+1 Ilysian Caryatid|THB
+1 Imperial Aerosaur|XLN
+1 Imperious Perfect|CMA
+1 In Bolas's Clutches|DOM
+1 Incubation Druid|RNA
+1 Inevitable End|THB
+1 Into the Story|ELD
+1 Ionize|GRN
+1 Isareth the Awakener|M19
+1 Isolated Chapel|DOM
+1 Jadelight Ranger|RIX
+1 Jaya's Greeting|WAR
+1 Jaya's Immolating Inferno|DOM
+1 Jhoira, Weatherlight Captain|DOM
+1 Josu Vess, Lich Knight|DOM
+1 Judith, the Scourge Diva|RNA
+1 Juggernaut|BBD
+1 Jungle Hollow|C19
+1 Karn's Bastion|WAR
+1 Karn's Temporal Sundering|DOM
+1 Karn, Scion of Urza|DOM|1
+1 Keeper of Fables|ELD
+1 Kenrith's Transformation|ELD|1
+1 Kiln Fiend|IMA
+1 Kinjalli's Sunwing|XLN
+1 Kiora Bests the Sea God|THB
+1 Kiora, Behemoth Beckoner|WAR|2
+1 Kitesail Freebooter|XLN
+1 Klothys, God of Destiny|THB|2
+1 Knight of Autumn|GRN
+1 Knight of Grace|DOM
+1 Knight of Malice|DOM
+1 Knight of the Ebon Legion|M20
+1 Kraul Harpooner|GRN
+1 Kronch Wrangler|WAR
+1 Kroxa, Titan of Death's Hunger|THB|1
+1 Kunoros, Hound of Athreos|THB|1
+1 Labyrinth of Skophos|THB|1
+1 Lava Coil|GRN
+1 Law-Rune Enforcer|WAR
+1 Lazav, the Multifarious|GRN
+1 Leafkin Druid|M20
+1 Legion Warboss|GRN
+1 Legion's Landing|XLN
+1 Leonin of the Lost Pride|THB
+1 Leonin Vanguard|M19
+1 Leonin Warleader|M19
+1 Light Up the Stage|RNA
+1 Lightning Strike|M19
+1 Liliana, Dreadhorde General|WAR|2
+1 Llanowar Elves|GNT
+1 Lonely Sandbar|MH1
+1 Lotleth Giant|GRN
+1 Lotus Field|M20
+1 Lovestruck Beast|ELD|2
+1 Loyal Pegasus|M20
+1 Lyra Dawnbringer|DOM
+1 Mace of the Valiant|ELD
+1 Manifold Key|M20
+1 Mantle of the Wolf|THB|2
+1 Massacre Girl|WAR
+1 Mastermind's Acquisition|RIX
+1 Medomai's Prophecy|THB
+1 Mentor of the Meek|M19
+1 Merfolk Secretkeeper|ELD|2
+1 Merfolk Trickster|DOM
+1 Midnight Clock|ELD|1
+1 Midnight Reaper|GRN
+1 Militia Bugler|M19
+1 Mind Stone|C18
+1 Ministrant of Obligation|RNA
+1 Mire's Grasp|THB
+1 Mist-Cloaked Herald|M19
+1 Mortify|RNA
+1 Mox Amber|DOM
+1 Murder|M20
+1 Murderous Rider|ELD|2
+1 Murmuring Mystic|GRN
+1 Nadir Kraken|THB|2
+1 Negate|M20
+1 Nessian Hornbeetle|THB
+1 Nessian Wanderer|THB
+1 Nightmare Shepherd|THB|1
+1 Nightmare's Thirst|M19
+1 Niv-Mizzet Reborn|WAR
+1 Nylea, Keen-Eyed|THB|2
+1 Nyx Lotus|THB|2
+1 Nyxbloom Ancient|THB|2
+1 Oathsworn Knight|ELD|2
+1 Omenspeaker|M19
+1 Once and Future|ELD
+1 Once Upon a Time|ELD|2
+1 Opt|ELD
+1 Order of Midnight|ELD|2
+1 Orzhov Enforcer|RNA
+1 Outlaws' Merriment|ELD|2
+1 Overgrown Tomb|GRN
+1 Ox of Agonas|THB|1
+1 Pacifism|M20
+1 Paradise Druid|WAR
+1 Patient Rebuilding|M19
+1 Pelt Collector|GRN
+1 Phoenix of Ash|THB|1
+1 Phyrexian Arena|CN2
+1 Piper of the Swarm|ELD|3
+1 Plaguecrafter|C19
+1 Planar Cleansing|M20
+1 Planewide Celebration|WAR
+1 Platinum Angel|CN2
+1 Polukranos, Unchained|THB|1
+1 Portal of Sanctuary|M20
+1 Prey Upon|UMA
+1 Priest of Forgotten Gods|RNA
+1 Prime Speaker Vannifar|RNA
+1 Prison Realm|WAR
+1 Psychic Corrosion|M19
+1 Pteramander|RNA
+1 Purphoros's Intervention|THB|1
+1 Quench|RNA
+1 Questing Beast|ELD|1
+1 Rabid Bite|M20
+1 Ral, Izzet Viceroy|GRN
+1 Rampaging Ferocidon|XLN
+1 Rankle, Master of Pranks|ELD|2
+1 Realm-Cloaked Giant|ELD|2
+1 Reclamation Sage|C18
+1 Rekindling Phoenix|RIX
+1 Relentless Pursuit|THB
+1 Relentless Raptor|RIX
+1 Remorseful Cleric|M19
+1 Resplendent Angel|M19
+1 Response // Resurgence|GRN
+1 Return to Nature|THB
+1 Revoke Existence|THB
+1 Rhys the Redeemed|SHM
+1 Rigging Runner|XLN
+1 Rimrock Knight|ELD|1
+1 Risen Reef|M20
+1 Risk Factor|GRN
+1 Roalesk, Apex Hybrid|WAR
+1 Robber of the Rich|ELD|1
+1 Rootbound Crag|XLN
+1 Rotting Regisaur|M20
+1 Rugged Highlands|C19
+1 Ruin Raider|XLN
+1 Rupture Spire|M19
+1 Sacred Foundry|GRN
+1 Saheeli, Sublime Artificer|WAR|1
+1 Sai, Master Thopterist|M19
+1 Saproling Migration|DOM
+1 Sarkhan the Masterless|WAR|1
+1 Savage Stomp|XLN
+1 Savvy Hunter|ELD
+1 Scorch Spitter|M20
+1 Scorching Dragonfire|ELD
+1 Scoured Barrens|M20
+1 Seal Away|DOM
+1 Search for Azcanta|XLN
+1 Season of Growth|M20
+1 Secluded Steppe|MH1
+1 Sentinel's Eyes|THB
+1 Sentinel's Mark|RNA
+1 Seraph of the Scales|RNA
+1 Setessan Champion|THB|1
+1 Settle the Wreckage|XLN
+1 Shadowspear|THB|1
+1 Shanna, Sisay's Legacy|DOM
+1 Shatter the Sky|THB|1
+1 Shepherd of the Flock|ELD|1
+1 Shivan Fire|DOM
+1 Shock|M20
+1 Siege-Gang Commander|GNT
+1 Sigil of the Empty Throne|C18
+1 Sigiled Sword of Valeron|M19
+1 Silverbeak Griffin|M19
+1 Sinister Sabotage|GRN
+1 Siren Stormtamer|XLN
+1 Skarrgan Hellkite|RNA
+1 Skewer the Critics|RNA
+1 Skilled Animator|M19
+1 Sky Terror|XLN
+1 Skymarcher Aspirant|RIX
+1 Slaying Fire|ELD|1
+1 Soul Warden|MM3
+1 Spark Double|WAR
+1 Spark Harvest|WAR
+1 Sparring Construct|DOM
+1 Spawn of Mayhem|RNA
+1 Spectral Sailor|M20
+1 Spell Pierce|XLN
+1 Sprouting Renewal|GRN
+1 Squee, the Immortal|DOM
+1 Staggering Insight|THB
+1 Starfield Mystic|M20
+1 Starlit Mantle|THB
+1 Steam Vents|GRN
+1 Steel Overseer|M20
+1 Stolen by the Fae|ELD|2
+1 Stomping Ground|RNA
+1 Stonecoil Serpent|ELD|1
+1 Storm Fleet Aerialist|XLN
+1 Storm's Wrath|THB|2
+1 Stormfist Crusader|ELD|1
+1 Sulfur Falls|DOM
+1 Summary Judgment|RNA
+1 Sunhome Stalwart|GRN
+1 Sunpetal Grove|XLN
+1 Swiftwater Cliffs|C19
+1 Sword-Point Diplomacy|XLN
+1 Syr Faren, the Hengehammer|ELD
+1 Tajic, Legion's Edge|GRN
+1 Talrand, Sky Summoner|C19
+1 Taranika, Akroan Veteran|THB|2
+1 Taste of Death|ELD
+1 Tectonic Giant|THB|1
+1 Teferi, Hero of Dominaria|DOM|1
+1 Temple Garden|GRN
+1 Temple of Abandon|THB|1
+1 Temple of Deceit|THB|1
+1 Temple of Enlightenment|THB|1
+1 Temple of Epiphany|M20
+1 Temple of Malady|M20
+1 Temple of Malice|THB|1
+1 Temple of Mystery|M20
+1 Temple of Plenty|THB|1
+1 Temple of Silence|M20
+1 Temple of Triumph|M20
+1 Tendershoot Dryad|RIX
+1 Tetsuko Umezawa, Fugitive|DOM
+1 Tezzeret, Artifice Master|M19
+1 Thalia, Guardian of Thraben|A25
+1 Thassa's Intervention|THB|1
+1 Thassa's Oracle|THB|2
+1 Thassa, Deep-Dwelling|THB|2
+1 The Akroan War|THB
+1 The Birth of Meletis|THB
+1 The Circle of Loyalty|ELD|2
+1 The Eldest Reborn|C19
+1 The First Iroan Games|THB
+1 The Great Henge|ELD|1
+1 The Immortal Sun|RIX
+1 The Mending of Dominaria|DOM
+1 The Mirari Conjecture|DOM
+1 Theater of Horrors|RNA
+1 Thirst for Meaning|THB|2
+1 Thorn Lieutenant|M19
+1 Thorn Mammoth|ELD
+1 Thornwood Falls|ELD
+1 Thought Erasure|GRN
+1 Thrash // Threat|RNA
+1 Thrashing Brontodon|M20
+1 Threnody Singer|THB
+1 Thryx, the Sudden Storm|THB|1
+1 Tibalt, Rakish Instigator|WAR|1
+1 Time Wipe|WAR
+1 Tin Street Dodger|RNA
+1 Tolsimir, Friend to Wolves|WAR
+1 Tomik, Distinguished Advokist|WAR
+1 Tranquil Cove|C19
+1 Tranquil Thicket|MH1
+1 Trapped in the Tower|ELD
+1 Traveler's Amulet|THB
+1 Traxos, Scourge of Kroog|DOM
+1 Treasure Map|XLN
+1 Tymaret, Chosen from Death|THB|1
+1 Ugin, the Ineffable|WAR|2
+1 Unbreakable Formation|RNA
+1 Underworld Rage-Hound|THB
+1 Unsummon|M20
+1 Untamed Kavu|DOM
+1 Uro, Titan of Nature's Wrath|THB|1
+1 Vantress Gargoyle|ELD|1
+1 Venerable Knight|ELD
+1 Venerated Loxodon|GRN
+1 Verix Bladewing|DOM
+1 Viashino Pyromancer|M19
+1 Vivien's Arkbow|WAR
+1 Vivien, Arkbow Ranger|M20
+1 Voltaic Servant|DOM
+1 Voracious Hydra|M20
+1 Vraska, Golgari Queen|GRN
+1 Warbriar Blessing|THB
+1 Warkite Marauder|RIX
+1 Warlord's Fury|DOM
+1 Watery Grave|GRN
+1 Wavebreak Hippocamp|THB|2
+1 Wayward Swordtooth|RIX
+1 Weaselback Redcap|ELD
+1 Weatherlight|DOM
+1 Wilderness Reclamation|RNA
+1 Wildwood Tracker|ELD
+1 Wind-Scarred Crag|ELD
+1 Winged Words|M20
+1 Witch's Oven|ELD
+1 Witch's Vengeance|ELD|1
+1 Witching Well|ELD
+1 Woe Strider|THB|1
+1 Wolfwillow Haven|THB|2
+1 Woodland Cemetery|DOM
+1 Woodland Champion|M20
+1 Yawgmoth's Vile Offering|DOM
+1 Zetalpa, Primal Dawn|C19
+1 Zhalfirin Void|DOM
+1 Zhur-Taa Goblin|RNA
+[Sideboard]
+
+[Planes]
+
+[Schemes]
+
+[Conspiracy]
+
diff --git a/forge-gui/res/deckgendecks/Pioneer.lda.dat b/forge-gui/res/deckgendecks/Pioneer.lda.dat
index 63f885b077e..5f0fbcaac3e 100644
Binary files a/forge-gui/res/deckgendecks/Pioneer.lda.dat and b/forge-gui/res/deckgendecks/Pioneer.lda.dat differ
diff --git a/forge-gui/res/deckgendecks/Pioneer.raw.dat b/forge-gui/res/deckgendecks/Pioneer.raw.dat
index c5e5a53bb18..0c6221bcb1b 100644
Binary files a/forge-gui/res/deckgendecks/Pioneer.raw.dat and b/forge-gui/res/deckgendecks/Pioneer.raw.dat differ
diff --git a/forge-gui/res/deckgendecks/Standard.lda.dat b/forge-gui/res/deckgendecks/Standard.lda.dat
index 759106c6bb1..1faf8cd03d0 100644
Binary files a/forge-gui/res/deckgendecks/Standard.lda.dat and b/forge-gui/res/deckgendecks/Standard.lda.dat differ
diff --git a/forge-gui/res/deckgendecks/Standard.raw.dat b/forge-gui/res/deckgendecks/Standard.raw.dat
index 264c1ae8b9d..d25714546dc 100644
Binary files a/forge-gui/res/deckgendecks/Standard.raw.dat and b/forge-gui/res/deckgendecks/Standard.raw.dat differ
diff --git a/forge-gui/res/draft/MTGACube.draft b/forge-gui/res/draft/MTGACube.draft
new file mode 100644
index 00000000000..77ca241d98a
--- /dev/null
+++ b/forge-gui/res/draft/MTGACube.draft
@@ -0,0 +1,6 @@
+Name:MTGACube
+DeckFile:MTGACube
+Singleton:True
+
+Booster: 15 Any
+NumPacks: 3
diff --git a/forge-gui/res/editions/Aether Revolt.txt b/forge-gui/res/editions/Aether Revolt.txt
index e2fcffcce5c..8f3e2c7cfd7 100644
--- a/forge-gui/res/editions/Aether Revolt.txt
+++ b/forge-gui/res/editions/Aether Revolt.txt
@@ -195,15 +195,15 @@ AdditionalSetUnlockedInQuest=MPS_KLD
182 C Watchful Automaton
183 C Welder Automaton
184 R Spire of Industry
-185 M Ajani, Valiant Protector
-186 R Ajani's Aid
+185 M Ajani, Valiant Protector
+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]
diff --git a/forge-gui/res/editions/Battle Royale.txt b/forge-gui/res/editions/Battle Royale.txt
index 76831b31973..13417672427 100644
--- a/forge-gui/res/editions/Battle Royale.txt
+++ b/forge-gui/res/editions/Battle Royale.txt
@@ -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
diff --git a/forge-gui/res/editions/Battlebond.txt b/forge-gui/res/editions/Battlebond.txt
index 18524d0e6c0..83e830766f5 100644
--- a/forge-gui/res/editions/Battlebond.txt
+++ b/forge-gui/res/editions/Battlebond.txt
@@ -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
diff --git a/forge-gui/res/editions/Beatdown.txt b/forge-gui/res/editions/Beatdown.txt
index b8244084ac0..1c32e2b20ee 100644
--- a/forge-gui/res/editions/Beatdown.txt
+++ b/forge-gui/res/editions/Beatdown.txt
@@ -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
@@ -97,4 +97,4 @@ Foil=NotSupported
87 L Mountain
88 L Forest
89 L Forest
-90 L Forest
+90 L Forest
\ No newline at end of file
diff --git a/forge-gui/res/editions/Commander 2020.txt b/forge-gui/res/editions/Commander 2020.txt
new file mode 100644
index 00000000000..ac7441824bd
--- /dev/null
+++ b/forge-gui/res/editions/Commander 2020.txt
@@ -0,0 +1,350 @@
+[metadata]
+Code=C20
+Date=2020-04-17
+Name=Commander 2020
+Type=Other
+
+[cards]
+1 M Trynn, Champion of Freedom
+2 M Haldan, Avid Arcanist
+3 M Nikara, Lair Scavenger
+4 M Brallin, Skyshark Rider
+5 M Cazur, Ruthless Stalker
+6 M Akim, the Soaring Wind
+7 M Gavi, Nest Warden
+8 M Jirina Kudro
+9 M Kalamax, the Stormsire
+10 M Kathril, Aspect Warper
+11 M Kelsien, the Plague
+12 M Otrimi, the Ever-Playful
+13 M Pako, Arcane Retriever
+14 M Shabraz, the Skyshark
+15 M Silvar, Devourer of the Free
+16 M Tayam, Luminous Enigma
+17 M Ukkima, Stalking Shadow
+18 M Xyris, the Writhing Storm
+19 M Yannik, Scavenging Sentinel
+20 M Zaxara, the Exemplary
+21 R Cryptic Trilobite
+22 R Avenging Huntbonder
+23 R Call the Coppercoats
+24 R Cartographer's Hawk
+25 R Dismantling Wave
+26 R Flawless Maneuver
+27 R Herald of the Forgotten
+28 U Martial Impetus
+29 R Verge Rangers
+30 R Vitality Hunter
+31 R Crystalline Resonance
+32 R Decoy Gambit
+33 R Eon Frolicker
+34 R Ethereal Forager
+35 R Fierce Guardianship
+36 R Nascent Metamorph
+37 U Psychic Impetus
+38 R Souvenir Snatcher
+39 R Tidal Barracuda
+40 R Boneyard Mycodrax
+41 R Daring Fiendbonder
+42 R Deadly Rollick
+43 R Dredge the Mire
+44 R Mindleecher
+45 R Netherborn Altar
+46 U Parasitic Impetus
+47 R Species Specialist
+48 R Titan Hunter
+49 R Agitator Ant
+50 R Deflecting Swat
+51 R Fireflux Squad
+52 R Frontier Warmonger
+53 R Lavabrink Floodgates
+54 R Molten Echoes
+55 U Shiny Impetus
+56 R Spellpyre Phoenix
+57 R Surly Badgersaur
+58 R Capricopian
+59 R Curious Herd
+60 R Glademuse
+61 R Obscuring Haze
+62 U Predatory Impetus
+63 R Ravenous Gigantotherium
+64 R Sawtusk Demolisher
+65 R Selective Adaptation
+66 R Slippery Bogbonder
+67 C Bonder's Ornament
+68 R Manascape Refractor
+69 R Sanctuary Blade
+70 R Twinning Staff
+71 R Nesting Grounds
+72 U Aerial Responder
+73 M Akroma, Angel of Wrath
+74 R Akroma's Vengeance
+75 R Angel of Finality
+76 R Astral Drift
+77 U Banisher Priest
+78 R Bounty Agent
+79 U Cast Out
+80 M Cataclysmic Gearhulk
+81 C Cavalry Pegasus
+82 R Citywide Bust
+83 R Cleansing Nova
+84 R Dearly Departed
+85 R Decree of Justice
+86 M Descend upon the Sinful
+87 U Devout Chaplain
+88 R Eternal Dragon
+89 R Frontline Medic
+90 R Hoofprints of the Stag
+91 R Increasing Devotion
+92 R Kalemne's Captain
+93 R Knight of the White Orchid
+94 R Magus of the Disk
+95 R Odric, Lunarch Marshal
+96 R Odric, Master Tactician
+97 R Reveillark
+98 R Riders of Gavony
+99 R Solemn Recruit
+100 U Spirit Cairn
+101 M Sun Titan
+102 R Sunblast Angel
+103 R Thalia's Lieutenant
+104 R Thraben Doomsayer
+105 R Together Forever
+106 R Unexpectedly Absent
+107 R Zetalpa, Primal Dawn
+108 U Chemister's Insight
+109 R Curator of Mysteries
+110 R Drake Haven
+111 C Frantic Search
+112 C Hieroglyphic Illumination
+113 U Illusory Ambusher
+114 M Jace, Architect of Thought
+115 R Lunar Mystic
+116 R Mind Spring
+117 U Mulldrifter
+118 U Murmuring Mystic
+119 R New Perspectives
+120 R Niblis of Frost
+121 R Nimble Obstructionist
+122 R Portal Mage
+123 U Propaganda
+124 R Swarm Intelligence
+125 R Talrand, Sky Summoner
+126 U Vizier of Tumbling Sands
+127 C Whiplash Trap
+128 U Windfall
+129 U Ambition's Cost
+130 R Cairn Wanderer
+131 R Deadly Tempest
+132 R Disciple of Bolas
+133 R Ever After
+134 R Painful Truths
+135 R Profane Command
+136 U Shriekmaw
+137 M Soul of Innistrad
+138 R Soulflayer
+139 U Unburial Rites
+140 U Vampire Nighthawk
+141 R Xathrid Necromancer
+142 U Zulaport Cutthroat
+143 R Alesha, Who Smiles at Death
+144 R Captivating Crew
+145 M Chandra, Flamecaller
+146 R Chaos Warp
+147 R Charmbreaker Devils
+148 M Comet Storm
+149 R Commune with Lava
+150 R Dualcaster Mage
+151 R Etali, Primal Storm
+152 R Fumiko the Lowblood
+153 R Goblin Dark-Dwellers
+154 U Humble Defector
+155 U Lightning Rift
+156 R Magus of the Wheel
+157 R Outpost Siege
+158 R Shared Animosity
+159 U Slice and Dice
+160 R Starstorm
+161 U Surreal Memoir
+162 R Tectonic Reformation
+163 R Titan of Eternal Fire
+164 U Vigilante Justice
+165 U Acidic Slime
+166 R Animist's Awakening
+167 R Beast Whisperer
+168 U Beast Within
+169 C Crop Rotation
+170 C Cultivate
+171 C Evolution Charm
+172 R Genesis Hydra
+173 U Harmonize
+174 C Harrow
+175 U Heroes' Bane
+176 R Hornet Queen
+177 R Hungering Hydra
+178 U Hunter's Insight
+179 U Hunting Pack
+180 C Kodama's Reach
+181 U Krosan Grip
+182 M Majestic Myriarch
+183 R Masked Admirers
+184 C Natural Connection
+185 R Predator Ooze
+186 U Reclamation Sage
+187 C Sakura-Tribe Elder
+188 C Satyr Wayfinder
+189 U Skullwinder
+190 U Slice in Twain
+191 R Splinterfright
+192 R Strength of the Tajuru
+193 U Tribute to the Wild
+194 R Vastwood Hydra
+195 M Vorapede
+196 U Wilderness Reclamation
+197 U Yavimaya Dryad
+198 R Abzan Ascendancy
+199 U Abzan Charm
+200 R Adriana, Captain of the Guard
+201 M Ajani Unyielding
+202 R Archon of Valor's Reach
+203 R Artifact Mutation
+204 R Cold-Eyed Selkie
+205 R Crackling Doom
+206 U Crackling Drake
+207 M Deadbridge Chant
+208 U Deathsprout
+209 U Despark
+210 R Djinn Illuminatus
+211 R Duneblast
+212 R Find // Finality
+213 U Garna, the Bloodflame
+214 R Gaze of Granite
+215 C Grisly Salvage
+216 C Growth Spiral
+217 M Isperia, Supreme Judge
+218 M Karametra, God of Harvests
+219 M The Locust God
+220 R Melek, Izzet Paragon
+221 R Mercurial Chemister
+222 U Migratory Route
+223 M Nahiri, the Harbinger
+224 M Nissa, Steward of Elements
+225 R Niv-Mizzet, the Firemind
+226 U Nyx Weaver
+227 R Prophetic Bolt
+228 U Putrefy
+229 R Rashmi, Eternities Crafter
+230 U Temur Charm
+231 U Terminate
+232 U Trygon Predator
+233 R Villainous Wealth
+234 R Wort, the Raidmother
+235 R Wydwen, the Biting Gale
+236 R Abandoned Sarcophagus
+237 C Arcane Signet
+238 U Azorius Signet
+239 U Boros Signet
+240 C Commander's Sphere
+241 R Fluctuator
+242 U Heirloom Blade
+243 U Izzet Signet
+244 R Lifecrafter's Bestiary
+245 U Lightning Greaves
+246 R Mimic Vat
+247 U Orzhov Signet
+248 R Psychosis Crawler
+249 U Rakdos Signet
+250 R Silent Arbiter
+251 U Skullclamp
+252 U Sol Ring
+253 R Solemn Simulacrum
+254 U Swiftfoot Boots
+255 C Ash Barrens
+256 U Azorius Chancery
+257 R Battlefield Forge
+258 U Blighted Woodland
+259 C Bojuka Bog
+260 C Boros Garrison
+261 R Canopy Vista
+262 R Caves of Koilos
+263 R Cinder Glade
+264 C Command Tower
+265 R Darkwater Catacombs
+266 C Desert of the Fervent
+267 C Desert of the Mindful
+268 C Desert of the True
+269 R Desolate Lighthouse
+270 U Dimir Aqueduct
+271 C Drifting Meadow
+272 R Endless Sands
+273 R Exotic Orchard
+274 U Forgotten Cave
+275 U Frontier Bivouac
+276 R Gavony Township
+277 U Golgari Rot Farm
+278 R Grim Backwoods
+279 U Gruul Turf
+280 C Halimar Depths
+281 R Hostile Desert
+282 R Irrigated Farmland
+283 U Izzet Boilerworks
+284 R Kessig Wolf Run
+285 U Krosan Verge
+286 R Llanowar Wastes
+287 U Lonely Sandbar
+288 U Memorial to Folly
+289 C Mortuary Mire
+290 R Mossfire Valley
+291 R Mosswort Bridge
+292 U Myriad Landscape
+293 U Mystic Monastery
+294 U Nomad Outpost
+295 U Opulent Palace
+296 R Oran-Rief, the Vastwood
+297 C Orzhov Basilica
+298 C Path of Ancestry
+299 R Prairie Stream
+300 C Rakdos Carnarium
+301 U Reliquary Tower
+302 C Remote Isle
+303 U Rogue's Passage
+304 C Rupture Spire
+305 U Sandsteppe Citadel
+306 R Scavenger Grounds
+307 U Secluded Steppe
+308 C Selesnya Sanctuary
+309 R Shadowblood Ridge
+310 R Shivan Reef
+311 U Simic Growth Chamber
+312 R Skycloud Expanse
+313 C Smoldering Crater
+314 R Smoldering Marsh
+315 C Soaring Seacliff
+316 R Spinerock Knoll
+317 R Sungrass Prairie
+318 R Sunken Hollow
+319 U Temple of the False God
+320 U Unclaimed Territory
+321 R Windbrisk Heights
+322 R Yavimaya Coast
+
+[tokens]
+b_2_2_zombie
+c_a_treasure_sac
+g_1_1_insect_flying_deathtouch
+g_1_1_saproling
+g_1_1_snake
+g_4_4_beast
+g_x_x_hydra
+r_3_1_elemental
+rg_1_1_goblin_warrior
+rw_2_2_dinosaur_cat
+ur_1_1_insect_flying_haste
+u_1_1_bird_illusion_flying
+u_2_2_drake_flying
+w_1_1_bird_flying
+w_1_1_human
+w_1_1_soldier
+w_1_1_spirit_flying
+w_3_3_elemental_flying
+w_4_4_angel_flying
diff --git a/forge-gui/res/editions/Conspiracy Take the Crown.txt b/forge-gui/res/editions/Conspiracy Take the Crown.txt
index c82c56ae90e..7d2d59cfc7d 100644
--- a/forge-gui/res/editions/Conspiracy Take the Crown.txt
+++ b/forge-gui/res/editions/Conspiracy Take the Crown.txt
@@ -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
@@ -242,4 +244,4 @@ r_1_1_goblin_noblock
r_8_8_lizard
g_3_3_beast
g_1_1_insect
-c_1_1_a_construct_defender
\ No newline at end of file
+c_1_1_a_construct_defender
diff --git a/forge-gui/res/editions/Fate Reforged.txt b/forge-gui/res/editions/Fate Reforged.txt
index 02752ed7073..e3810f09b24 100644
--- a/forge-gui/res/editions/Fate Reforged.txt
+++ b/forge-gui/res/editions/Fate Reforged.txt
@@ -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]
diff --git a/forge-gui/res/editions/Historic Anthology 1.txt b/forge-gui/res/editions/Historic Anthology 1.txt
new file mode 100644
index 00000000000..66d29db7ce2
--- /dev/null
+++ b/forge-gui/res/editions/Historic Anthology 1.txt
@@ -0,0 +1,27 @@
+[metadata]
+Code=HA1
+Date=2019-11-21
+Name=Historic Anthology 1
+Type=Other
+
+[cards]
+1 R Serra Ascendant
+2 C Soul Warden
+3 R Kinsbaile Cavalier
+4 C Treasure Hunt
+5 C Distant Melody
+6 R Cryptbreaker
+7 R Hypnotic Specter
+8 R Phyrexian Arena
+9 C Tendrils of Corruption
+10 C Kiln Fiend
+11 U Goblin Matron
+12 R Hidetsugu's Second Rite
+13 C Elvish Visionary
+14 R Fauna Shaman
+15 U Imperious Perfect
+16 U Burning-Tree Emissary
+17 R Captain Sisay
+18 U Ornithopter
+19 C Mind Stone
+20 R Darksteel Reactor
\ No newline at end of file
diff --git a/forge-gui/res/editions/Historic Anthology 2.txt b/forge-gui/res/editions/Historic Anthology 2.txt
new file mode 100644
index 00000000000..b1634ff631b
--- /dev/null
+++ b/forge-gui/res/editions/Historic Anthology 2.txt
@@ -0,0 +1,32 @@
+[metadata]
+Code=HA2
+Date=2020-03-12
+Name=Historic Anthology 2
+Type=Other
+
+[cards]
+1 U Nyx-Fleece Ram
+2 R Ranger of Eos
+3 R Sigil of the Empty Throne
+4 R Thalia, Guardian of Thraben
+5 U Merrow Reejerey
+6 R Inexorable Tide
+7 U Brain Maggot
+8 R Pack Rat
+9 U Virulent Plague
+10 R Waste Not
+11 M Dragonmaster Outcast
+12 U Goblin Ruinblaster
+13 C Ancestral Mask
+14 R Terravore
+15 R Knight of the Reliquary
+16 R Maelstrom Pulse
+17 R Meddling Mage
+18 M Platinum Angel
+19 C Barren Moor
+20 C Bojuka Bog
+21 C Forgotten Cave
+22 U Ghost Quarter
+23 C Lonely Sandbar
+24 C Secluded Steppe
+25 C Tranquil Thicket
\ No newline at end of file
diff --git a/forge-gui/res/editions/Ikoria Lair of Behemoths.txt b/forge-gui/res/editions/Ikoria Lair of Behemoths.txt
new file mode 100644
index 00000000000..6676bf2446e
--- /dev/null
+++ b/forge-gui/res/editions/Ikoria Lair of Behemoths.txt
@@ -0,0 +1,418 @@
+[metadata]
+Code=IKO
+Date=2020-04-24
+Name=Ikoria: Lair of Behemoths
+Code2=IKO
+MciCode=iko
+Type=Expansion
+BoosterCovers=5
+Booster=10 Common:!fromSheet("IKO Secret Cards"), 3 Uncommon:!fromSheet("IKO Secret Cards"), 1 RareMythic:!fromSheet("IKO Secret Cards"), 1 BasicLand
+Prerelease=6 Boosters, 1 RareMythic+
+
+[cards]
+1 C Adaptive Shimmerer
+2 C Farfinder
+3 C Mysterious Egg
+4 C Blade Banish
+5 C Checkpoint Officer
+6 C Coordinated Charge
+7 R Cubwarden
+8 C Daysquad Marshal
+9 C Divine Arrow
+10 C Drannith Healer
+11 R Drannith Magistrate
+12 U Fight as One
+13 U Flourishing Fox
+14 C Garrison Cat
+15 C Helica Glider
+16 U Huntmaster Liger
+17 C Imposing Vantasaur
+18 U Keensight Mentor
+19 R Lavabrink Venturer
+20 C Light of Hope
+21 M Luminous Broodmoth
+22 U Majestic Auricorn
+23 C Maned Serval
+24 R Mythos of Snapdax
+25 C Pacifism
+26 C Patagia Tiger
+27 C Perimeter Sergeant
+28 U Sanctuary Lockdown
+29 C Savai Sabertooth
+30 C Snare Tactician
+31 C Solid Footing
+32 U Splendor Mare
+33 C Spontaneous Flight
+34 U Stormwild Capridor
+35 U Swallow Whole
+36 U Valiant Rescuer
+37 C Vulpikeet
+38 U Will of the All-Hunter
+39 C Aegis Turtle
+40 C Anticipate
+41 U Archipelagore
+42 U Avian Oddity
+43 U Boon of the Wish-Giver
+44 C Capture Sphere
+45 C Convolute
+46 C Crystacean
+47 C Dreamtail Heron
+48 U Escape Protocol
+49 C Essence Scatter
+50 C Facet Reader
+51 C Frost Lynx
+52 C Frostveil Ambush
+53 C Glimmerbell
+54 C Gust of Wind
+55 C Hampering Snare
+56 C Keep Safe
+57 U Mystic Subdual
+58 R Mythos of Illuna
+59 U Neutralize
+60 C Of One Mind
+61 U Ominous Seas
+62 C Phase Dolphin
+63 U Pollywog Symbiote
+64 U Pouncing Shoreshark
+65 U Reconnaissance Mission
+66 R Sea-Dasher Octopus
+67 R Shark Typhoon
+68 C Startling Development
+69 C Thieving Otter
+70 R Voracious Greatshark
+71 C Wingfold Pteron
+72 U Wingspan Mentor
+73 U Bastion of Remembrance
+74 C Blitz Leech
+75 C Blood Curdle
+76 C Boot Nipper
+77 C Bushmeat Poacher
+78 U Call of the Death-Dweller
+79 C Cavern Whisperer
+80 U Chittering Harvester
+81 C Corpse Churn
+82 C Dark Bargain
+83 C Dead Weight
+84 R Dirge Bat
+85 C Durable Coilbug
+86 U Duskfang Mentor
+87 U Easy Prey
+88 R Extinction Event
+89 C Gloom Pangolin
+90 U Grimdancer
+91 U Heartless Act
+92 R Hunted Nightmare
+93 U Insatiable Hemophage
+94 C Lurking Deadeye
+95 C Memory Leak
+96 C Mutual Destruction
+97 R Mythos of Nethroi
+98 C Nightsquad Commando
+99 C Serrated Scorpion
+100 C Suffocating Fumes
+101 U Unbreakable Bond
+102 C Unexpected Fangs
+103 C Unlikely Aid
+104 U Void Beckoner
+105 C Whisper Squad
+106 U Zagoth Mamba
+107 C Blazing Volley
+108 C Blisterspit Gremlin
+109 U Blitz of the Thunder-Raptor
+110 C Cathartic Reunion
+111 U Clash of Titans
+112 C Cloudpiercer
+113 C Drannith Stinger
+114 R Everquill Phoenix
+115 C Ferocious Tigorilla
+116 C Fire Prophecy
+117 U Flame Spill
+118 U Footfall Crater
+119 C Forbidden Friendship
+120 C Frenzied Raptor
+121 U Frillscare Mentor
+122 C Go for Blood
+123 C Heightened Reflexes
+124 C Lava Serpent
+125 M Lukka, Coppercoat Outcast
+126 U Momentum Rumbler
+127 R Mythos of Vadrok
+128 U Porcuparrot
+129 C Prickly Marmoset
+130 C Pyroceratops
+131 C Raking Claws
+132 U Reptilian Reflection
+133 U Rooting Moloch
+134 C Rumbling Rockslide
+135 U Sanctuary Smasher
+136 C Shredded Sails
+137 C Spelleater Wolverine
+138 C Tentative Connection
+139 R Unpredictable Cyclone
+140 U Weaponize the Monsters
+141 M Yidaro, Wandering Monster
+142 C Adventurous Impulse
+143 C Almighty Brushwagg
+144 U Auspicious Starrix
+145 U Barrier Breach
+146 C Bristling Boar
+147 U Charge of the Forever-Beast
+148 R Colossification
+149 C Essence Symbiote
+150 C Excavation Mole
+151 U Exuberant Wolfbear
+152 C Fertilid
+153 C Flycatcher Giraffid
+154 C Fully Grown
+155 R Gemrazer
+156 U Glowstone Recluse
+157 C Greater Sandwurm
+158 C Honey Mammoth
+159 U Hornbash Mentor
+160 C Humble Naturalist
+161 U Ivy Elemental
+162 R Kogla, the Titan Ape
+163 U Lead the Stampede
+164 U Migration Path
+165 C Migratory Greathorn
+166 U Monstrous Step
+167 C Mosscoat Goriak
+168 R Mythos of Brokkos
+169 C Plummet
+170 C Ram Through
+171 C Sudden Spinnerets
+172 C Survivors' Bond
+173 C Thwart the Enemy
+174 U Titanoth Rex
+175 M Vivien, Monsters' Advocate
+176 C Wilt
+177 U Back for More
+178 U Boneyard Lurker
+179 M Brokkos, Apex of Forever
+180 U Channeled Force
+181 M Chevill, Bane of Monsters
+182 R Death's Oasis
+183 U Dire Tactics
+184 R Eerie Ultimatum
+185 R Emergent Ultimatum
+186 R Frondland Felidar
+187 M General Kudro of Drannith
+188 U General's Enforcer
+189 R Genesis Ultimatum
+190 M Illuna, Apex of Wishes
+191 R Inspired Ultimatum
+192 M Kinnan, Bonder Prodigy
+193 R Labyrinth Raptor
+194 U Lore Drakkis
+195 M Narset of the Ancient Way
+196 U Necropanther
+197 M Nethroi, Apex of Death
+198 R Offspring's Revenge
+199 U Parcelbeast
+200 U Primal Empathy
+201 R Quartzwood Crasher
+202 U Regal Leosaur
+203 M Rielle, the Everwise
+204 R Ruinous Ultimatum
+205 U Savai Thundermane
+206 U Skull Prophet
+207 R Skycat Sovereign
+208 R Slitherwisp
+209 M Snapdax, Apex of the Hunt
+210 R Song of Creation
+211 U Sprite Dragon
+212 R Titans' Nest
+213 U Trumpeting Gnarr
+214 M Vadrok, Apex of Thunder
+215 R Whirlwind of Thought
+216 M Winota, Joiner of Forces
+217 U Zenith Flare
+218 U Alert Heedbonder
+219 U Cunning Nightbonder
+220 M Fiend Artisan
+221 R Gyruda, Doom of Depths
+222 R Jegantha, the Wellspring
+223 U Jubilant Skybonder
+224 R Kaheera, the Orphanguard
+225 R Keruga, the Macrosage
+226 R Lurrus of the Dream-Den
+227 R Lutri, the Spellchaser
+228 R Obosh, the Preypiercer
+229 U Proud Wildbonder
+230 U Sonorous Howlbonder
+231 R Umori, the Collector
+232 R Yorion, Sky Nomad
+233 R Zirda, the Dawnwaker
+234 R Crystalline Giant
+235 U Indatha Crystal
+236 U Ketria Crystal
+237 R The Ozolith
+238 U Raugrin Crystal
+239 U Savai Crystal
+240 C Sleeper Dart
+241 C Springjaw Trap
+242 U Zagoth Crystal
+243 C Bloodfell Caves
+244 C Blossoming Sands
+245 R Bonders' Enclave
+246 C Dismal Backwater
+247 C Evolving Wilds
+248 R Indatha Triome
+249 C Jungle Hollow
+250 R Ketria Triome
+251 R Raugrin Triome
+252 C Rugged Highlands
+253 R Savai Triome
+254 C Scoured Barrens
+255 C Swiftwater Cliffs
+256 C Thornwood Falls
+257 C Tranquil Cove
+258 C Wind-Scarred Crag
+259 R Zagoth Triome
+260 L Plains
+261 L Plains
+262 L Plains
+263 L Island
+264 L Island
+265 L Island
+266 L Swamp
+267 L Swamp
+268 L Swamp
+269 L Mountain
+270 L Mountain
+271 L Mountain
+272 L Forest
+273 L Forest
+274 L Forest
+#Buy-A-Box Promo
+275 M Zilortha, Strength Incarnate
+#Borderless Planeswalkers
+276 M Lukka, Coppercoat Outcast
+277 M Vivien, Monsters' Advocate
+278 M Narset of the Ancient Way
+#Showcase Cards
+279 R Cubwarden
+280 U Huntmaster Liger
+281 U Majestic Auricorn
+282 C Vulpikeet
+283 U Archipelagore
+284 C Dreamtail Heron
+285 U Pouncing Shoreshark
+286 R Sea-Dasher Octopus
+287 C Cavern Whisperer
+288 U Chittering Harvester
+289 R Dirge Bat
+290 U Insatiable Hemophage
+291 C Cloudpiercer
+292 R Everquill Phoenix
+293 U Porcuparrot
+294 U Auspicious Starrix
+295 R Gemrazer
+296 U Glowstone Recluse
+297 C Migratory Greathorn
+298 U Boneyard Lurker
+299 M Brokkos, Apex of Forever
+300 M Illuna, Apex of Wishes
+301 U Lore Drakkis
+302 U Necropanther
+303 M Nethroi, Apex of Death
+304 U Parcelbeast
+305 U Regal Leosaur
+306 M Snapdax, Apex of the Hunt
+307 U Trumpeting Gnarr
+308 M Vadrok, Apex of Thunder
+309 R Indatha Triome
+310 R Ketria Triome
+311 R Raugrin Triome
+312 R Savai Triome
+313 R Zagoth Triome
+#Extended Art Cards
+314 R Drannith Magistrate
+315 R Lavabrink Venturer
+316 M Luminous Broodmoth
+317 R Mythos of Snapdax
+318 R Mythos of Illuna
+319 R Shark Typhoon
+320 R Voracious Greatshark
+321 R Extinction Event
+322 R Hunted Nightmare
+323 R Mythos of Nethroi
+324 R Mythos of Vadrok
+325 R Unpredictable Cyclone
+326 R Yidaro, Wandering Monster
+327 R Colossification
+328 R Kogla, the Titan Ape
+329 R Mythos of Brokkos
+330 M Chevill, Bane of Monsters
+331 R Death's Oasis
+332 R Eerie Ultimatum
+333 R Emergent Ultimatum
+334 R Frondland Felidar
+335 M General Kudro of Drannith
+336 R Genesis Ultimatum
+337 R Inspired Ultimatum
+338 M Kinnan, Bonder Prodigy
+339 R Labyrinth Raptor
+340 R Offspring's Revenge
+341 R Quartzwood Crasher
+342 M Rielle, the Everwise
+343 R Ruinous Ultimatum
+344 R Skycat Sovereign
+345 R Slitherwisp
+346 R Song of Creation
+347 R Titans' Nest
+348 R Whirlwind of Thought
+349 M Winota, Joiner of Forces
+350 M Fiend Artisan
+351 R Gyruda, Doom of Depths
+352 R Jegantha, the Wellspring
+353 R Kaheera, the Orphanguard
+354 R Keruga, the Macrosage
+355 R Lurrus of the Dream-Den
+356 R Lutri, the Spellchaser
+357 R Obosh, the Preypiercer
+358 R Umori, the Collector
+359 R Yorion, Sky Nomad
+360 R Zirda, the Dawnwaker
+361 R Crystalline Giant
+362 R The Ozolith
+363 R Bonders' Enclave
+#Promos
+364 R Colossification
+365 U Flourishing Fox
+366 U Heartless Act
+367 C Forbidden Friendship
+368 U Migration Path
+369 U Sprite Dragon
+#Godzilla Series Monsters
+370 U Huntmaster Liger
+371 M Luminous Broodmoth
+372 U Pollywog Symbiote
+373 U Void Beckoner
+374 R Everquill Phoenix
+375 R Yidaro, Wandering Monster
+376 R Gemrazer
+377 U Titanoth Rex
+378 M Brokkos, Apex of Forever
+379 M Illuna, Apex of Wishes
+380 M Nethroi, Apex of Death
+381 M Snapdax, Apex of the Hunt
+382 U Sprite Dragon
+383 M Vadrok, Apex of Thunder
+384 R Gyruda, Doom of Depths
+385 C Mysterious Egg
+386 R Dirge Bat
+387 R Crystalline Giant
+
+[tokens]
+c_a_feather_sac
+g_3_3_beast
+g_x_x_dinosaur_beast_trample
+r_1_1_dinosaur_haste
+u_8_8_kraken
+u_x_x_shark_flying
+w_1_1_cat_bird_flying
+w_1_1_cat_lifelink
+w_1_1_human_soldier
+w_1_1_human_soldier
+w_1_1_human_soldier
\ No newline at end of file
diff --git a/forge-gui/res/editions/Khans of Tarkir.txt b/forge-gui/res/editions/Khans of Tarkir.txt
index eb1ab10a7df..b1b96ce023e 100644
--- a/forge-gui/res/editions/Khans of Tarkir.txt
+++ b/forge-gui/res/editions/Khans of Tarkir.txt
@@ -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
diff --git a/forge-gui/res/editions/Magic 2014.txt b/forge-gui/res/editions/Magic 2014.txt
index d4daec8d83b..0934c02e1ef 100644
--- a/forge-gui/res/editions/Magic 2014.txt
+++ b/forge-gui/res/editions/Magic 2014.txt
@@ -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
diff --git a/forge-gui/res/editions/Magic 2015.txt b/forge-gui/res/editions/Magic 2015.txt
index 53ada72d465..8d560ecd154 100644
--- a/forge-gui/res/editions/Magic 2015.txt
+++ b/forge-gui/res/editions/Magic 2015.txt
@@ -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
diff --git a/forge-gui/res/editions/Mystery Booster Retail Edition Foils.txt b/forge-gui/res/editions/Mystery Booster Retail Edition Foils.txt
new file mode 100644
index 00000000000..86da737ae2b
--- /dev/null
+++ b/forge-gui/res/editions/Mystery Booster Retail Edition Foils.txt
@@ -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
diff --git a/forge-gui/res/editions/Mystery Booster.txt b/forge-gui/res/editions/Mystery Booster.txt
new file mode 100644
index 00000000000..4ca8e07217f
--- /dev/null
+++ b/forge-gui/res/editions/Mystery Booster.txt
@@ -0,0 +1,1704 @@
+[metadata]
+Code=MB1
+Date=2019-11-07
+Name=Mystery Booster
+Type=Reprint
+BoosterCovers=1
+Booster=2 fromSheet("MB1 White CommonUncommon"), 2 fromSheet("MB1 Blue CommonUncommon"), 2 fromSheet("MB1 Black CommonUncommon"), 2 fromSheet("MB1 Green CommonUncommon"), 2 fromSheet("MB1 Red CommonUncommon"), 1 fromSheet("MB1 Multi CommonUncommon"), 1 fromSheet("MB1 Artifact Land CommonUncommon"), 1 fromSheet("MB1 Pre M15"), 1 fromSheet("MB1 Post M15 RareMythic"), 1 fromSheet("FMB1 Foils")
+Foil=NotSupported
+
+[cards]
+1 R All Is Dust
+2 U Artisan of Kozilek
+3 U Breaker of Armies
+4 R Desolation Twin
+5 C Eldrazi Devastator
+6 U Pathrazer of Ulamog
+7 U Abzan Falconer
+8 C Abzan Runemark
+9 C Acrobatic Maneuver
+10 U Adanto Vanguard
+11 R Adorned Pouncer
+12 C Affa Protector
+13 C Ainok Bond-Kin
+14 U Ajani's Pridemate
+15 C Alley Evasion
+16 M Angelic Destiny
+17 C Angelic Gift
+18 C Angelic Purge
+19 C Angel of Mercy
+20 U Angel of Renewal
+21 R Angel of the Dire Hour
+22 C Angelsong
+23 C Apostle's Blessing
+24 R Approach of the Second Sun
+25 U Archangel
+26 C Arrest
+27 C Arrester's Zeal
+28 C Artful Maneuver
+29 U Aura of Silence
+30 C Aven Battle Priest
+31 C Aven Sentry
+32 C Ballynock Cohort
+33 C Bartered Cow
+34 U Battle Mastery
+35 R Beacon of Immortality
+36 C Benevolent Ancestor
+37 C Blade Instructor
+38 U Blessed Spirits
+39 C Bonds of Faith
+40 C Borrowed Grace
+41 C Built to Last
+42 C Bulwark Giant
+43 C Candlelight Vigil
+44 C Caravan Escort
+45 C Cartouche of Solidarity
+46 U Cast Out
+47 C Cathar's Companion
+48 C Caught in the Brights
+49 U Celestial Crusader
+50 C Celestial Flare
+51 C Center Soul
+52 C Champion of Arashin
+53 R Champion of the Parish
+54 R Chancellor of the Annex
+55 C Charge
+56 C Cliffside Lookout
+57 C Cloudshift
+58 C Coalition Honor Guard
+59 C Collar the Culprit
+60 U Congregate
+61 C Conviction
+62 C Countless Gears Renegade
+63 C Court Homunculus
+64 C Court Street Denizen
+65 U Crib Swap
+66 U Danitha Capashen, Paragon
+67 C Daring Skyjek
+68 U Darksteel Mutation
+69 C Dauntless Cathar
+70 C Dawnglare Invoker
+71 C Decommission
+72 R Decree of Justice
+73 C Defiant Strike
+74 C Desperate Sentry
+75 C Devilthorn Fox
+76 R Dictate of Heliod
+77 C Disenchant
+78 U Dismantling Blow
+79 C Disposal Mummy
+80 C Divine Favor
+81 C Djeru's Renunciation
+82 C Djeru's Resolve
+83 C Doomed Traveler
+84 C Dragon Bell Monk
+85 C Dragon's Eye Sentry
+86 C Dragon's Presence
+87 C Eddytrail Hawk
+88 M Elesh Norn, Grand Cenobite
+89 C Emerge Unscathed
+90 C Empyrial Armor
+91 C Encampment Keeper
+92 U Encircling Fissure
+93 C Enduring Victory
+94 C Enlightened Ascetic
+95 C Ephemeral Shields
+96 C Ephemerate
+97 R Evra, Halcyon Witness
+98 C Excavation Elephant
+99 C Excoriate
+100 C Expedition Raptor
+101 C Expose Evil
+102 C Exultant Skymarcher
+103 C Eyes in the Skies
+104 C Faithbearer Paladin
+105 C Faith's Fetters
+106 C Feat of Resistance
+107 U Felidar Guardian
+108 R Felidar Sovereign
+109 U Felidar Umbra
+110 C Fencing Ace
+111 U Fiend Hunter
+112 C Firehoof Cavalry
+113 C Forsake the Worldly
+114 C Fortify
+115 C Fragmentize
+116 C Geist of the Moors
+117 U Ghostblade Eidolon
+118 M Gideon Jura
+119 C Gideon's Lawkeeper
+120 U Gift of Estates
+121 C Glaring Aegis
+122 C Gleam of Resistance
+123 C Glint-Sleeve Artisan
+124 C God-Pharaoh's Faithful
+125 C Gods Willing
+126 R Grasp of Fate
+127 C Grasp of the Hieromancer
+128 C Great-Horn Krushok
+129 C Guided Strike
+130 U Gustcloak Skirmisher
+131 C Gust Walker
+132 C Healer's Hawk
+133 C Healing Grace
+134 C Healing Hands
+135 C Heavy Infantry
+136 C Humble
+137 C Hyena Umbra
+138 C Infantry Veteran
+139 C Inquisitor's Ox
+140 C Inspired Charge
+141 C Intrusive Packbeast
+142 C Iona's Judgment
+143 C Isolation Zone
+144 U Jubilant Mascot
+145 C Knight of Cliffhaven
+146 U Knight of Dawn
+147 C Knight of Old Benalia
+148 C Knight of Sorrows
+149 C Knight of the Skyward Eye
+150 C Knight of the Tusk
+151 U Kor Bladewhirl
+152 C Kor Chant
+153 U Kor Firewalker
+154 C Kor Hookmaster
+155 C Kor Sky Climber
+156 C Kor Skyfisher
+157 U Lashknife Barrier
+158 U Leonin Relic-Warder
+159 C Lieutenants of the Guard
+160 U Lightform
+161 C Lightwalker
+162 U Lingering Souls
+163 C Lone Missionary
+164 C Lonesome Unicorn
+165 C Looming Altisaur
+166 C Lotus-Eye Mystics
+167 C Loxodon Partisan
+168 C Loyal Sentry
+169 C Lunarch Mantle
+170 R Magus of the Moat
+171 C Mana Tithe
+172 C Mardu Hordechief
+173 C Marked by Honor
+174 R Martyr's Bond
+175 U Martyr's Cause
+176 C Meditation Puzzle
+177 C Midnight Guard
+178 R Mirran Crusader
+179 R Mirror Entity
+180 C Momentary Blink
+181 C Moonlit Strider
+182 C Mortal's Ardor
+183 U Mother of Runes
+184 C Ninth Bridge Patrol
+185 U Nyx-Fleece Ram
+186 R Odric, Lunarch Marshal
+187 C Ondu Greathorn
+188 C Ondu War Cleric
+189 C Oreskos Swiftclaw
+190 U Oust
+191 C Pacifism
+192 U Palace Jailer
+193 C Palace Sentinels
+194 C Paladin of the Bloodstained
+195 C Path of Peace
+196 U Path to Exile
+197 U Peace of Mind
+198 C Pegasus Courser
+199 C Pentarch Ward
+200 C Pitfall Trap
+201 C Pressure Point
+202 U Promise of Bunrei
+203 C Prowling Caracal
+204 C Rally the Peasants
+205 C Raptor Companion
+206 R Recruiter of the Guard
+207 U Refurbish
+208 C Renewed Faith
+209 C Resurrection
+210 U Retreat to Emeria
+211 C Reviving Dose
+212 C Rhet-Crop Spearmaster
+213 U Righteous Cause
+214 C Rootborn Defenses
+215 C Sacred Cat
+216 C Sanctum Gargoyle
+217 C Sandstorm Charger
+218 C Savannah Lions
+219 C Seal of Cleansing
+220 C Searing Light
+221 U Seeker of the Way
+222 C Sensor Splicer
+223 U Seraph of the Suns
+224 C Serra Disciple
+225 U Serra's Embrace
+226 C Sheer Drop
+227 C Shining Aerosaur
+228 C Shining Armor
+229 C Shoulder to Shoulder
+230 C Siegecraft
+231 C Silverchase Fox
+232 C Skyhunter Skirmisher
+233 U Skymarcher Aspirant
+234 C Skyspear Cavalry
+235 C Slash of Talons
+236 C Snubhorn Sentry
+237 C Soulmender
+238 C Soul Parry
+239 C Soul-Strike Technique
+240 C Soul Summons
+241 C Soul Warden
+242 C Sparring Mummy
+243 C Spectral Gateguards
+244 C Stalwart Aven
+245 C Star-Crowned Stag
+246 C Stave Off
+247 C Steadfast Sentinel
+248 C Stone Haven Medic
+249 C Sunlance
+250 C Sunrise Seeker
+251 C Suppression Bonds
+252 C Survive the Night
+253 U Swords to Plowshares
+254 C Take Vengeance
+255 C Tandem Tactics
+256 R Teferi's Protection
+257 C Terashi's Grasp
+258 C Territorial Hammerskull
+259 R Thalia's Lancers
+260 C Thraben Inspector
+261 C Thraben Standard Bearer
+262 U Timely Reinforcements
+263 C Topan Freeblade
+264 C Unwavering Initiate
+265 C Veteran Swordsmith
+266 C Village Bell-Ringer
+267 C Voice of the Provinces
+268 U Volunteer Reserves
+269 C Wake the Reflections
+270 U Wall of Omens
+271 C Wall of One Thousand Cuts
+272 C Wandering Champion
+273 C War Behemoth
+274 R Weathered Wayfarer
+275 C Wild Griffin
+276 U Windborne Charge
+277 C Winged Shepherd
+278 U Wing Shards
+279 C Youthful Knight
+280 C Zealous Strike
+281 C Academy Journeymage
+282 C Aethersnipe
+283 C Aether Tradewinds
+284 C Amass the Components
+285 R Aminatou's Augury
+286 C Amphin Pathmage
+287 C Anticipate
+288 C Arcane Denial
+289 C Archaeomancer
+290 U Archetype of Imagination
+291 C Artificer's Assistant
+292 C Augur of Bolas
+293 C Augury Owl
+294 C Bastion Inventor
+295 C Befuddle
+296 C Benthic Giant
+297 C Benthic Infiltrator
+298 C Bewilder
+299 U Blue Elemental Blast
+300 C Borrowing 100,000 Arrows
+301 C Brainstorm
+302 C Brilliant Spectrum
+303 U Brine Elemental
+304 C Calculated Dismissal
+305 C Caller of Gales
+306 C Call to Heel
+307 C Cancel
+308 C Capture Sphere
+309 C Cartouche of Knowledge
+310 C Castaway's Despair
+311 C Catalog
+312 U Chart a Course
+313 R Chasm Skulker
+314 C Chillbringer
+315 C Choking Tethers
+316 C Chronostutter
+317 U Circular Logic
+318 U Citywatch Sphinx
+319 C Claustrophobia
+320 C Clear the Mind
+321 C Cloak of Mists
+322 C Cloud Elemental
+323 C Cloudkin Seer
+324 C Cloudreader Sphinx
+325 C Clutch of Currents
+326 C Compelling Argument
+327 U Concentrate
+328 U Condescend
+329 C Containment Membrane
+330 C Contingency Plan
+331 C Contradict
+332 C Convolute
+333 C Coralhelm Guide
+334 C Coral Trickster
+335 U Corrupted Conscience
+336 C Counterspell
+337 C Court Hussar
+338 C Crashing Tide
+339 C Crush Dissent
+340 U Curiosity
+341 C Curio Vendor
+342 C Daze
+343 C Dazzling Lights
+344 C Decision Paralysis
+345 C Deep Analysis
+346 C Deep Freeze
+347 R Deepglow Skate
+348 C Diminish
+349 C Dirgur Nemesis
+350 C Dispel
+351 C Displace
+352 U Distortion Strike
+353 C Divination
+354 R Djinn of Wishes
+355 C Doorkeeper
+356 U Dragon's Eye Savants
+357 C Drag Under
+358 C Dreadwaters
+359 C Dream Cache
+360 C Dream Twist
+361 C Eel Umbra
+362 C Embodiment of Spring
+363 R Energy Field
+364 C Enlightened Maniac
+365 U Ensoul Artifact
+366 C Errant Ephemeron
+367 C Essence Scatter
+368 U Everdream
+369 U Exclude
+370 M Expropriate
+371 U Fact or Fiction
+372 C Faerie Invaders
+373 C Faerie Mechanist
+374 C Failed Inspection
+375 U Fascination
+376 C Fathom Seer
+377 R Fblthp, the Lost
+378 U Flashfreeze
+379 U Fledgling Mawcor
+380 C Fleeting Distraction
+381 U Floodgate
+382 U Fog Bank
+383 C Fogwalker
+384 C Foil
+385 C Forbidden Alchemy
+386 C Frantic Search
+387 C Frilled Sea Serpent
+388 C Frost Lynx
+389 C Gaseous Form
+390 C Ghost Ship
+391 C Glacial Crasher
+392 C Glint
+393 C Gone Missing
+394 C Grasp of Phantoms
+395 U Guard Gomazoa
+396 C Gurmag Drowner
+397 C Gush
+398 U Hedron Crab
+399 C Hieroglyphic Illumination
+400 C Hightide Hermit
+401 C Hinterland Drake
+402 C Horseshoe Crab
+403 C Humongulus
+404 C Impulse
+405 C Inkfathom Divers
+406 C Invisibility
+407 C Ior Ruin Expedition
+408 C Jace's Phantasm
+409 C Jeering Homunculus
+410 C Jeskai Sage
+411 R Jushi Apprentice // Tomoya the Revealer
+412 C Jwar Isle Avenger
+413 C Kiora's Dambreaker
+414 C Laboratory Brute
+415 U Laboratory Maniac
+416 U Labyrinth Guardian
+417 U Lay Claim
+418 C Leapfrog
+419 U Mahamoti Djinn
+420 C Mana Leak
+421 C Man-o'-War
+422 R Master Transmuter
+423 C Maximize Altitude
+424 R Memory Erosion
+425 C Memory Lapse
+426 U Merfolk Looter
+427 C Messenger Jays
+428 C Metallic Rebuke
+429 C Mind Sculpt
+430 R Mind Spring
+431 R The Mirari Conjecture
+432 R Misdirection
+433 U Mistform Shrieker
+434 C Mist Raven
+435 C Mnemonic Wall
+436 C Monastery Loremaster
+437 C Mulldrifter
+438 U Murder of Crows
+439 C Mystical Teachings
+440 R Mystic Confluence
+441 C Mystic of the Hidden Way
+442 C Nagging Thoughts
+443 C Negate
+444 C Niblis of Dusk
+445 C Nine-Tail White Fox
+446 C Ninja of the Deep Hours
+447 C Ojutai Interceptor
+448 C Ojutai's Breath
+449 C Omenspeaker
+450 U Opportunity
+451 C Opt
+452 C Peel from Reality
+453 C Phantasmal Bear
+454 U Phantasmal Dragon
+455 U Phyrexian Ingester
+456 R Phyrexian Metamorph
+457 C Pondering Mage
+458 C Portent
+459 U Predict
+460 C Preordain
+461 U Prodigal Sorcerer
+462 U Propaganda
+463 C Prosperous Pirates
+464 C Purple-Crystal Crab
+465 C Refocus
+466 C Repulse
+467 C Retraction Helix
+468 C Rhystic Study
+469 U Riftwing Cloudskate
+470 C Ringwarden Owl
+471 U Rishadan Footpad
+472 C River Darter
+473 C River Serpent
+474 C Riverwheel Aerialists
+475 U Sage of Lat-Nam
+476 C Sailor of Means
+477 R Sakashima the Impostor
+478 C Sapphire Charm
+479 C Scroll Thief
+480 C Sea Gate Oracle
+481 U Sealock Monster
+482 C Secrets of the Golden City
+483 C Send to Sleep
+484 R Serendib Efreet
+485 C Shaper Parasite
+486 C Shimmerscale Drake
+487 C Shipwreck Looter
+488 U Sigiled Starfish
+489 C Silent Observer
+490 U Silvergill Adept
+491 C Singing Bell Strike
+492 U Skaab Goliath
+493 C Skitter Eel
+494 C Skittering Crustacean
+495 U Sleep
+496 C Slipstream Eel
+497 C Slither Blade
+498 C Snap
+499 C Snapping Drake
+500 C Somber Hoverguard
+501 U Soothsaying
+502 U Sphinx's Tutelage
+503 C Spire Monitor
+504 C Steady Progress
+505 C Stitched Drake
+506 C Storm Sculptor
+507 C Strategic Planning
+508 C Stream of Thought
+509 R Stunt Double
+510 C Surrakar Banisher
+511 C Syncopate
+512 U Syr Elenora, the Discerning
+513 R Talrand, Sky Summoner
+514 C Tandem Lookout
+515 M Teferi, Temporal Archmage
+516 C Temporal Fissure
+517 M Temporal Mastery
+518 U Thieving Magpie
+519 C Thornwind Faeries
+520 C Thoughtcast
+521 C Thought Collapse
+522 C Thought Scour
+523 U Thrummingbird
+524 C Thunder Drake
+525 C Tidal Warrior
+526 C Tidal Wave
+527 U Tinker
+528 C Totally Lost
+529 U Trail of Evidence
+530 C Treasure Cruise
+531 C Treasure Hunt
+532 U Treasure Mage
+533 C Trinket Mage
+534 U Triton Tactics
+535 C Turn Aside
+536 C Uncomfortable Chill
+537 C Vapor Snag
+538 C Vigean Graftmage
+539 U Wall of Frost
+540 C Warden of Evos Isle
+541 C Watercourser
+542 C Wave-Wing Elemental
+543 C Weldfast Wingsmith
+544 C Welkin Tern
+545 R Whelming Wave
+546 C Whiplash Trap
+547 R Whir of Invention
+548 C Windcaller Aven
+549 C Wind Drake
+550 U Wind-Kin Raiders
+551 C Windrider Eel
+552 C Wind Strider
+553 C Wishcoin Crab
+554 C Wishful Merfolk
+555 C Wretched Gryff
+556 C Write into Being
+557 U Youthful Scholar
+558 C Absorb Vis
+559 C Accursed Spirit
+560 C Aid the Fallen
+561 C Alesha's Vanguard
+562 C Alley Strangler
+563 C Altar's Reap
+564 C Ambitious Aetherborn
+565 C Ancestral Vengeance
+566 U Animate Dead
+567 U Annihilate
+568 C Bala Ged Scorpion
+569 U Baleful Ammit
+570 C Balustrade Spy
+571 C Bartizan Bats
+572 C Bitter Revelation
+573 C Black Cat
+574 U Black Knight
+575 R Black Market
+576 C Bladebrand
+577 C Blessing of Belzenlok
+578 C Blighted Bat
+579 C Blightsoil Druid
+580 C Blistergrub
+581 U Blood Artist
+582 C Bloodrite Invoker
+583 C Bone Splinters
+584 C Boon of Emrakul
+585 U Breeding Pit
+586 C Butcher's Glee
+587 U Cabal Therapy
+588 C Cackling Imp
+589 C Cadaver Imp
+590 R Cairn Wanderer
+591 C Caligo Skin-Witch
+592 U Carrion Feeder
+593 C Carrion Imp
+594 C Catacomb Crocodile
+595 C Catacomb Slug
+596 U Caustic Tar
+597 C Certain Death
+598 C Child of Night
+599 C Coat with Venom
+600 R Collective Brutality
+601 U Corpsehatch
+602 C Costly Plunder
+603 C Covenant of Blood
+604 C Cower in Fear
+605 C Crippling Blight
+606 C Crow of Dark Tidings
+607 C Cursed Minotaur
+608 C Daring Demolition
+609 U Darkblast
+610 C Dark Dabbling
+611 C Dark Ritual
+612 C Dark Withering
+613 U Dauthi Mindripper
+614 C Deadbridge Shaman
+615 C Deadeye Tormentor
+616 R Deadly Tempest
+617 C Dead Reveler
+618 C Death Denied
+619 C Defeat
+620 U Demonic Tutor
+621 C Demonic Vigor
+622 C Demon's Grasp
+623 C Desperate Castaways
+624 C Diabolic Edict
+625 R Dictate of Erebos
+626 C Die Young
+627 C Dinosaur Hunter
+628 C Dirge of Dread
+629 U Dismember
+630 C Disowned Ancestor
+631 C Doomed Dissenter
+632 C Douse in Gloom
+633 R Drana, Kalastria Bloodchief
+634 C Dreadbringer Lampads
+635 C Dread Drone
+636 U Dread Return
+637 C Dregscape Zombie
+638 C Driver of the Dead
+639 C Drudge Sentinel
+640 C Dukhara Scavenger
+641 C Dune Beetle
+642 C Duress
+643 C Dusk Charger
+644 C Dusk Legion Zealot
+645 U The Eldest Reborn
+646 C Epicure of Blood
+647 C Erg Raiders
+648 C Eternal Thirst
+649 C Evincar's Justice
+650 C Executioner's Capsule
+651 U Exsanguinate
+652 C Eyeblight's Ending
+653 U Fallen Angel
+654 C Farbog Revenant
+655 U Fatal Push
+656 C Fen Hauler
+657 C Feral Abomination
+658 C Festercreep
+659 C Festering Newt
+660 C Fetid Imp
+661 C Fill with Fright
+662 C First-Sphere Gargantua
+663 C Flesh to Dust
+664 U Fretwork Colony
+665 C Fungal Infection
+666 U Genju of the Fens
+667 C Ghostly Changeling
+668 C Ghoulcaller's Accomplice
+669 U Gifted Aetherborn
+670 U Go for the Throat
+671 R Gonti, Lord of Luxury
+672 C Grasping Scoundrel
+673 R Gravecrawler
+674 C Gravedigger
+675 C Gravepurge
+676 M Grave Titan
+677 C Gray Merchant of Asphodel
+678 C Grim Affliction
+679 C Grim Discovery
+680 C Grixis Slavedriver
+681 C Grotesque Mutation
+682 C Gruesome Fate
+683 C Gurmag Angler
+684 R Haakon, Stromgald Scourge
+685 C Hideous End
+686 C Hired Blade
+687 C Hound of the Farbogs
+688 U Hunter of Eyeblights
+689 R Hypnotic Specter
+690 C Induce Despair
+691 C Infernal Scarring
+692 U Infest
+693 C Innocent Blood
+694 U Inquisition of Kozilek
+695 C Instill Infection
+696 C Kalastria Nightwatch
+697 C Krumar Bond-Kin
+698 C Lawless Broker
+699 C Lazotep Behemoth
+700 C Lethal Sting
+701 M Liliana, Death's Majesty
+702 R Living Death
+703 U Lord of the Accursed
+704 C Macabre Waltz
+705 C Marauding Boneslasher
+706 C March of the Drowned
+707 C Mark of the Vampire
+708 C Marsh Hulk
+709 C Mephitic Vapors
+710 C Merciless Resolve
+711 C Miasmic Mummy
+712 C Mind Rake
+713 C Mind Rot
+714 R Mind Shatter
+715 C Mire's Malice
+716 C Moment of Craving
+717 C Murder
+718 C Murderous Compulsion
+719 C Nameless Inversion
+720 C Nantuko Husk
+721 C Never Happened
+722 R Nighthowler
+723 C Night's Whisper
+724 C Nirkana Assassin
+725 U Noxious Dragon
+726 C Okiba-Gang Shinobi
+727 C Painful Lesson
+728 U Perish
+729 C Pestilence
+730 R Phyrexian Arena
+731 R Phyrexian Plaguelord
+732 C Phyrexian Rager
+733 U Phyrexian Reclamation
+734 C Pit Keeper
+735 U Plaguecrafter
+736 C Plagued Rusalka
+737 C Plague Wight
+738 C Prakhata Club Security
+739 C Prowling Pangolin
+740 C Queen's Agent
+741 U Quest for the Gravelord
+742 C Rabid Bloodsucker
+743 C Rakdos Drake
+744 C Rakshasa's Secret
+745 U Ravenous Chupacabra
+746 C Read the Bones
+747 C Reaper of Night // Harvest Fear
+748 U Reassembling Skeleton
+749 C Reckless Imp
+750 U Reckless Spite
+751 C Recover
+752 C Renegade Demon
+753 C Renegade's Getaway
+754 C Returned Centaur
+755 R Revel in Riches
+756 U Revenant
+757 C Rite of the Serpent
+758 C Rotfeaster Maggot
+759 C Ruin Rat
+760 R Rune-Scarred Demon
+761 U Sadistic Hypnotist
+762 C Scarab Feast
+763 C Scrounger of Souls
+764 C Scuttling Death
+765 C Seal of Doom
+766 U Sengir Vampire
+767 R Sewer Nemesis
+768 C Shadowcloak Vampire
+769 C Shambling Attendants
+770 C Shambling Goblin
+771 U Shriekmaw
+772 U Shrouded Lore
+773 C Silumgar Butcher
+774 U Skeletal Scrying
+775 C Skeleton Archer
+776 C Skulking Ghost
+777 U Smiting Helix
+778 M Sorin Markov
+779 C Spreading Rot
+780 U Stab Wound
+781 C Stallion of Ashmouth
+782 C Stinkweed Imp
+783 U Street Wraith
+784 C Stromkirk Patrol
+785 C Subtle Strike
+786 C Sultai Runemark
+787 C Tar Snare
+788 U Tavern Swindler
+789 C Tendrils of Corruption
+790 C Thallid Omnivore
+791 C Thornbow Archer
+792 C Thorn of the Black Rose
+793 C Thraben Foulbloods
+794 C Tidy Conclusion
+795 R Torment of Hailfire
+796 C Torment of Venom
+797 C Touch of Moonglove
+798 R Toxin Sliver
+799 C Tragic Slip
+800 C Trespasser's Curse
+801 U Trial of Ambition
+802 C Twins of Maurer Estate
+803 C Typhoid Rats
+804 C Unburden
+805 C Undercity's Embrace
+806 C Untamed Hunger
+807 C Unyielding Krumar
+808 C Urborg Uprising
+809 C Vampire Champion
+810 C Vampire Envoy
+811 U Vampire Hexmage
+812 C Vampire Lacerator
+813 U Vampire Nighthawk
+814 C Vessel of Malignity
+815 C Virulent Swipe
+816 C Voracious Null
+817 C Vraska's Finisher
+818 C Wake of Vultures
+819 C Walking Corpse
+820 U Walk the Plank
+821 C Wander in Death
+822 C Warteye Witch
+823 C Weight of the Underworld
+824 C Weirded Vampire
+825 U Wight of Precinct Six
+826 U Will-o'-the-Wisp
+827 C Windgrace Acolyte
+828 C Wrench Mind
+829 U Yargle, Glutton of Urborg
+830 C Zulaport Chainmage
+831 C Act of Treason
+832 U Act on Impulse
+833 U Ahn-Crop Crasher
+834 C Ainok Tracker
+835 C Akroan Sergeant
+836 C Alchemist's Greeting
+837 R Alesha, Who Smiles at Death
+838 U Ancient Grudge
+839 U Anger
+840 R Anger of the Gods
+841 U Arc Trail
+842 C Arrow Storm
+843 C Atarka Efreet
+844 U Avalanche Riders
+845 C Avarax
+846 C Azra Bladeseeker
+847 C Balduvian Horde
+848 C Barging Sergeant
+849 C Barrage of Boulders
+850 C Battle Rampart
+851 C Battle-Rattle Shaman
+852 U Beetleback Chief
+853 C Bellows Lizard
+854 C Blades of Velis Vel
+855 C Blastfire Bolt
+856 C Blazing Volley
+857 C Blindblast
+858 C Bloodfire Expert
+859 C Bloodlust Inciter
+860 C Bloodmad Vampire
+861 C Blood Ogre
+862 C Bloodstone Goblin
+863 C Blow Your House Down
+864 C Blur of Blades
+865 C Boggart Brute
+866 C Boiling Earth
+867 C Bombard
+868 C Bomber Corps
+869 C Borrowed Hostility
+870 C Boulder Salvo
+871 C Brazen Buccaneers
+872 C Brazen Wolves
+873 R Brimstone Dragon
+874 U Brimstone Mage
+875 C Bring Low
+876 U Browbeat
+877 C Brute Strength
+878 C Built to Smash
+879 C Burst Lightning
+880 C Canyon Lurkers
+881 C Cartouche of Zeal
+882 C Cathartic Reunion
+883 C Chandra's Pyrohelix
+884 C Chandra's Revolution
+885 R Chaos Warp
+886 U Charging Monstrosaur
+887 C Chartooth Cougar
+888 C Cinder Hellion
+889 C Cleansing Screech
+890 C Cobblebrute
+891 C Cosmotronic Wave
+892 R Cragganwick Cremator
+893 C Crash Through
+894 C Crowd's Favor
+895 C Crown-Hunter Hireling
+896 U Curse of Opulence
+897 U Curse of the Nightly Hunt
+898 M Daretti, Scrap Savant
+899 U Death by Dragons
+900 C Defiant Ogre
+901 C Demolish
+902 C Desert Cerodon
+903 U Desperate Ravings
+904 C Destructive Tampering
+905 C Direct Current
+906 C Distemper of the Blood
+907 U Dragon Breath
+908 C Dragon Egg
+909 C Dragon Fodder
+910 C Dragonsoul Knight
+911 U Dragon Whelp
+912 C Dual Shot
+913 C Dynacharge
+914 C Earth Elemental
+915 C Emrakul's Hatcher
+916 U Enthralling Victor
+917 C Erratic Explosion
+918 C Expedite
+919 C Faithless Looting
+920 C Falkenrath Reaver
+921 C Fall of the Hammer
+922 C Fervent Strike
+923 C Fierce Invocation
+924 C Fiery Hellhound
+925 C Fiery Temper
+926 U Fireball
+927 C Firebolt
+928 C Firebrand Archer
+929 C Fire Elemental
+930 U Flame Jab
+931 U Flameshot
+932 U Flametongue Kavu
+933 U Flamewave Invoker
+934 C Fling
+935 C Forge Devil
+936 C Foundry Street Denizen
+937 C Frenzied Raptor
+938 C Frilled Deathspitter
+939 C Frontline Devastator
+940 C Frontline Rebel
+941 C Furnace Whelp
+942 C Fury Charm
+943 C Galvanic Blast
+944 C Generator Servant
+945 U Genju of the Spires
+946 C Geomancer's Gambit
+947 C Ghitu Lavarunner
+948 U Ghitu War Cry
+949 C Giant Spectacle
+950 U Goblin Assault
+951 C Goblin Balloon Brigade
+952 U Goblin Bombardment
+953 C Goblin Fireslinger
+954 R Goblin Game
+955 C Goblin Locksmith
+956 U Goblin Matron
+957 C Goblin Motivator
+958 U Goblin Oriflamme
+959 R Goblin Piledriver
+960 C Goblin Roughrider
+961 U Goblin Warchief
+962 C Goblin War Paint
+963 C Gorehorn Minotaurs
+964 C Gore Swine
+965 C Granitic Titan
+966 C Grapeshot
+967 C Gravitic Punch
+968 R Greater Gargadon
+969 C Gut Shot
+970 U Guttersnipe
+971 C Hammerhand
+972 C Hanweir Lancer
+973 C Hardened Berserker
+974 C Hijack
+975 C Hulking Devil
+976 C Hyena Pack
+977 C Ill-Tempered Cyclops
+978 C Impact Tremors
+979 R Impending Disaster
+980 U Incorrigible Youths
+981 C Inferno Fist
+982 U Inferno Jet
+983 C Ingot Chewer
+984 C Insolent Neonate
+985 C Jackal Pup
+986 C Kaervek's Torch
+987 M Kargan Dragonlord
+988 C Keldon Halberdier
+989 C Keldon Overseer
+990 C Khenra Scrapper
+991 M Kiki-Jiki, Mirror Breaker
+992 C Kiln Fiend
+993 C Kird Ape
+994 R Knollspine Dragon
+995 C Kolaghan Stormsinger
+996 R Krenko, Mob Boss
+997 C Krenko's Command
+998 C Krenko's Enforcer
+999 C Leaping Master
+1000 C Leopard-Spotted Jiao
+1001 U Lightning Bolt
+1002 C Lightning Javelin
+1003 C Lightning Shrieker
+1004 C Lightning Talons
+1005 C Madcap Skills
+1006 C Magma Spray
+1007 C Makindi Sliderunner
+1008 C Mardu Warshrieker
+1009 U Mark of Mutiny
+1010 C Maximize Velocity
+1011 C Miner's Bane
+1012 R Mizzix's Mastery
+1013 U Mogg Fanatic
+1014 C Mogg Flunkies
+1015 C Mogg War Marshal
+1016 U Molten Rain
+1017 U Monastery Swiftspear
+1018 C Mutiny
+1019 C Nimble-Blade Khenra
+1020 C Ondu Champion
+1021 C Orcish Cannonade
+1022 C Orcish Oriflamme
+1023 C Outnumber
+1024 C Pillage
+1025 R Preyseizer Dragon
+1026 U Price of Progress
+1027 C Prickleboar
+1028 C Prophetic Ravings
+1029 M Purphoros, God of the Forge
+1030 U Pyrotechnics
+1031 C Quakefoot Cyclops
+1032 R Rage Reflection
+1033 C Rampaging Cyclops
+1034 R Reality Scramble
+1035 C Reckless Fireweaver
+1036 C Reckless Wurm
+1037 U Recoup
+1038 U Release the Ants
+1039 R Release the Gremlins
+1040 C Renegade Tactics
+1041 U Rivals' Duel
+1042 U Roast
+1043 U Rolling Thunder
+1044 C Rubblebelt Maaka
+1045 C Ruinous Gremlin
+1046 C Rummaging Goblin
+1047 C Run Amok
+1048 C Rush of Adrenaline
+1049 C Salivating Gremlins
+1050 C Samut's Sprint
+1051 C Sarkhan's Rage
+1052 C Screamreach Brawler
+1053 C Seismic Shift
+1054 C Seismic Stomp
+1055 C Shatter
+1056 U Shattering Spree
+1057 C Shenanigans
+1058 C Shock
+1059 C Skirk Commando
+1060 C Skirk Prospector
+1061 C Smash to Smithereens
+1062 C Smelt
+1063 C Sparkmage Apprentice
+1064 C Sparkspitter
+1065 C Sparktongue Dragon
+1066 U Spikeshot Goblin
+1067 U Staggershock
+1068 M Star of Extinction
+1069 R Steamflogger Boss
+1070 U Stormblood Berserker
+1071 R Sudden Demise
+1072 U Sulfurous Blast
+1073 C Summit Prowler
+1074 C Sun-Crowned Hunters
+1075 C Swashbuckling
+1076 C Sweatworks Brawler
+1077 C Swift Kick
+1078 C Tarfire
+1079 R Taurean Mauler
+1080 U Tectonic Rift
+1081 C Temur Battle Rage
+1082 C Thresher Lizard
+1083 C Thrill of Possibility
+1084 U Tibalt's Rager
+1085 C Torch Courier
+1086 R Two-Headed Giant
+1087 C Uncaged Fury
+1088 C Undying Rage
+1089 R Urza's Rage
+1090 C Valakut Invoker
+1091 C Valakut Predator
+1092 C Valley Dasher
+1093 C Vandalize
+1094 C Vent Sentinel
+1095 C Vessel of Volatility
+1096 U Viashino Sandstalker
+1097 U Volcanic Dragon
+1098 C Volcanic Rush
+1099 C Voldaren Duelist
+1100 C Wall of Fire
+1101 C Wayward Giant
+1102 R Wheel of Fate
+1103 C Wildfire Emissary
+1104 C Wojek Bodyguard
+1105 U Young Pyromancer
+1106 C Zada's Commando
+1107 C Zealot of the God-Pharaoh
+1108 C Abundant Growth
+1109 U Acidic Slime
+1110 C Adventurous Impulse
+1111 C Aerie Bowmasters
+1112 U Affectionate Indrik
+1113 C Aggressive Instinct
+1114 C Aggressive Urge
+1115 U Ainok Survivalist
+1116 C Alpine Grizzly
+1117 C Ambassador Oak
+1118 U Ana Sanctuary
+1119 U Ancestral Mask
+1120 C Ancient Brontodon
+1121 U Ancient Stirrings
+1122 C Arachnus Web
+1123 C Arbor Armament
+1124 C Arbor Elf
+1125 R Asceticism
+1126 C Aura Gnarlid
+1127 C Avacyn's Pilgrim
+1128 C Backwoods Survivalists
+1129 C Baloth Gorger
+1130 C Basking Rootwalla
+1131 C Bear Cub
+1132 U Beastbreaker of Bala Ged
+1133 R Beastmaster Ascension
+1134 U Beast Within
+1135 U Become Immense
+1136 C Beneath the Sands
+1137 U Bestial Menace
+1138 R Birds of Paradise
+1139 C Bitterblade Warrior
+1140 C Bitterbow Sharpshooters
+1141 U Blanchwood Armor
+1142 C Blastoderm
+1143 R Bloom Tender
+1144 C Blossom Dryad
+1145 C Borderland Explorer
+1146 C Borderland Ranger
+1147 R Bow of Nylea
+1148 U Briarhorn
+1149 C Bristling Boar
+1150 C Broken Bond
+1151 C Broodhunter Wurm
+1152 C Byway Courier
+1153 C Call the Scions
+1154 C Canopy Spider
+1155 C Carnivorous Moss-Beast
+1156 U Carpet of Flowers
+1157 C Caustic Caterpillar
+1158 C Centaur Courser
+1159 U Centaur Glade
+1160 C Charging Rhino
+1161 C Chatter of the Squirrel
+1162 C Citanul Woodreaders
+1163 C Clip Wings
+1164 C Colossal Dreadmaw
+1165 C Combo Attack
+1166 C Commune with Nature
+1167 C Commune with the Gods
+1168 C Conifer Strider
+1169 R Courser of Kruphix
+1170 U Creeping Mold
+1171 C Crop Rotation
+1172 C Crossroads Consecrator
+1173 U The Crowd Goes Wild
+1174 C Crowned Ceratok
+1175 C Crushing Canopy
+1176 C Cultivate
+1177 C Daggerback Basilisk
+1178 C Dawn's Reflection
+1179 C Death-Hood Cobra
+1180 R Defense of the Heart
+1181 U Desert Twister
+1182 U Destructor Dragon
+1183 C Dissenter's Deliverance
+1184 U Domesticated Hydra
+1185 C Dragonscale Boon
+1186 C Dragon-Scarred Bear
+1187 R Dungrove Elder
+1188 C Durkwood Baloth
+1189 C Earthen Arms
+1190 R Eldritch Evolution
+1191 C Elemental Uprising
+1192 C Elephant Guide
+1193 C Elves of Deep Shadow
+1194 C Elvish Fury
+1195 C Elvish Visionary
+1196 C Elvish Warrior
+1197 C Ember Weaver
+1198 C Epic Confrontation
+1199 C Essence Warden
+1200 U Eternal Witness
+1201 U Experiment One
+1202 C Explore
+1203 U Explosive Vegetation
+1204 C Ezuri's Archers
+1205 C Fade into Antiquity
+1206 C Farseek
+1207 C Feed the Clan
+1208 C Feral Krushok
+1209 C Feral Prowler
+1210 C Ferocious Zheng
+1211 C Fertile Ground
+1212 C Fierce Empath
+1213 C Fog
+1214 C Formless Nurturing
+1215 C Frontier Mastodon
+1216 U Gaea's Blessing
+1217 C Gaea's Protector
+1218 C Giant Growth
+1219 C Giant Spider
+1220 C Gift of Growth
+1221 C Gift of Paradise
+1222 C Glade Watcher
+1223 C Gnarlid Pack
+1224 C Grapple with the Past
+1225 C Grazing Gladehart
+1226 C Greater Basilisk
+1227 C Greater Sandwurm
+1228 R Greenbelt Rampager
+1229 C Greenwood Sentinel
+1230 C Groundswell
+1231 C Guardian Shield-Bearer
+1232 U Hamlet Captain
+1233 C Hardy Veteran
+1234 U Harmonize
+1235 C Harrow
+1236 C Hooded Brawler
+1237 C Hooting Mandrills
+1238 R Hornet Nest
+1239 C Hunter's Ambush
+1240 C Hunt the Weak
+1241 R Hurricane
+1242 U Imperious Perfect
+1243 U Invigorate
+1244 C Ivy Lane Denizen
+1245 C Jungle Delver
+1246 C Jungle Wayfinder
+1247 C Kavu Climber
+1248 C Kavu Primarch
+1249 C Khalni Heart Expedition
+1250 C Kin-Tree Warden
+1251 C Kozilek's Predator
+1252 C Kraul Foragers
+1253 C Kraul Warrior
+1254 C Krosan Druid
+1255 C Krosan Tusker
+1256 C Larger Than Life
+1257 C Lay of the Land
+1258 C Lead by Example
+1259 C Lead the Stampede
+1260 C Lifespring Druid
+1261 C Lignify
+1262 C Llanowar Elves
+1263 C Llanowar Empath
+1264 C Longshot Squad
+1265 U Lure
+1266 U Manglehorn
+1267 C Mantle of Webs
+1268 C Map the Wastes
+1269 R Meandering Towershell
+1270 C Might of the Masses
+1271 C Mulch
+1272 R Mycoloth
+1273 C Natural Connection
+1274 C Naturalize
+1275 C Nature's Claim
+1276 C Nature's Lore
+1277 C Nest Invader
+1278 C Nettle Sentinel
+1279 C New Horizons
+1280 C Nimble Mongoose
+1281 M Nissa, Voice of Zendikar
+1282 C Oakgnarl Warrior
+1283 C Ondu Giant
+1284 C Oran-Rief Invoker
+1285 C Overgrown Armasaur
+1286 U Overgrown Battlement
+1287 U Overrun
+1288 C Pack's Favor
+1289 C Peema Outrider
+1290 U Pelakka Wurm
+1291 C Penumbra Spider
+1292 U Phantom Centaur
+1293 C Pierce the Sky
+1294 C Pinion Feast
+1295 C Plummet
+1296 C Pouncing Cheetah
+1297 C Prey's Vengeance
+1298 C Prey Upon
+1299 C Priest of Titania
+1300 C Pulse of Murasa
+1301 C Quiet Disrepair
+1302 U Rain of Thorns
+1303 C Rampant Growth
+1304 U Rancor
+1305 C Ranger's Guile
+1306 C Ravenous Leucrocota
+1307 C Reclaim
+1308 C Reclaiming Vines
+1309 U Regrowth
+1310 C Relic Crush
+1311 C Return to the Earth
+1312 C Revive
+1313 C Rhox Maulers
+1314 C Riparian Tiger
+1315 U River Boa
+1316 U Roar of the Wurm
+1317 C Root Out
+1318 C Roots
+1319 C Rosethorn Halberd
+1320 C Runeclaw Bear
+1321 C Sagu Archer
+1322 C Sakura-Tribe Elder
+1323 C Saproling Migration
+1324 C Savage Punch
+1325 C Scatter the Seeds
+1326 C Seal of Strength
+1327 C Search for Tomorrow
+1328 U Seek the Horizon
+1329 C Seek the Wilds
+1330 M Selvala, Heart of the Wilds
+1331 R Shamanic Revelation
+1332 C Shape the Sands
+1333 C Siege Wurm
+1334 C Silhana Ledgewalker
+1335 C Silkweaver Elite
+1336 C Snake Umbra
+1337 U Snapping Sailback
+1338 R Spawning Grounds
+1339 U Spider Spawning
+1340 R Squirrel Wrangler
+1341 C Stalking Tiger
+1342 C Stoic Builder
+1343 C Strength in Numbers
+1344 C Sylvan Bounty
+1345 U Sylvan Scrying
+1346 C Tajuru Pathwarden
+1347 U Tajuru Warcaller
+1348 C Take Down
+1349 C Talons of Wildwood
+1350 R Tempt with Discovery
+1351 C Terrain Elemental
+1352 C Territorial Baloth
+1353 C Thornhide Wolves
+1354 U Thornscape Battlemage
+1355 C Thornweald Archer
+1356 U Thrashing Brontodon
+1357 C Thrive
+1358 M Thrun, the Last Troll
+1359 U Timberwatch Elf
+1360 C Time to Feed
+1361 R Tireless Tracker
+1362 C Titanic Growth
+1363 U Triumph of the Hordes
+1364 C Tukatongue Thallid
+1365 U Turntimber Basilisk
+1366 C Vastwood Gorger
+1367 U Venom Sliver
+1368 R Vigor
+1369 C Watcher in the Web
+1370 C Wellwisher
+1371 C Wild Growth
+1372 C Wild Mongrel
+1373 C Wild Nacatl
+1374 C Wildsize
+1375 C Wolfkin Bond
+1376 U Woodborn Behemoth
+1377 C Woolly Loxodon
+1378 U Wren's Run Vanquisher
+1379 C Yavimaya Elder
+1380 C Yavimaya Sapherd
+1381 C Yeva's Forcemage
+1382 U Zendikar's Roil
+1383 U Abzan Charm
+1384 C Abzan Guide
+1385 C Agony Warp
+1386 U Akroan Hoplite
+1387 M Animar, Soul of Elements
+1388 U Armadillo Cloak
+1389 U Armament Corps
+1390 R Assemble the Legion
+1391 M Athreos, God of Passage
+1392 U Aura Shards
+1393 U Azorius Charm
+1394 U Azra Oddsmaker
+1395 U Baleful Strix
+1396 U Baloth Null
+1397 U Bear's Companion
+1398 U Belligerent Brontodon
+1399 U Bituminous Blast
+1400 U Bladewing the Risen
+1401 U Blightning
+1402 U Bloodbraid Elf
+1403 U Boros Challenger
+1404 U Bounding Krasis
+1405 U Call of the Nightwing
+1406 U Campaign of Vengeance
+1407 U Cauldron Dance
+1408 U Citadel Castellan
+1409 C Coiling Oracle
+1410 U Contraband Kingpin
+1411 U Corpsejack Menace
+1412 U Crosis's Charm
+1413 U Cunning Breezedancer
+1414 U Deathreap Ritual
+1415 C Deny Reality
+1416 U Draconic Disciple
+1417 M Dragon Broodmother
+1418 M Dragonlord Ojutai
+1419 U Drana's Emissary
+1420 U Engineered Might
+1421 U Esper Charm
+1422 U Ethercaste Knight
+1423 C Ethereal Ambush
+1424 U Extract from Darkness
+1425 U Fires of Yavimaya
+1426 U Flame-Kin Zealot
+1427 U Fusion Elemental
+1428 U Gelectrode
+1429 U Ghor-Clan Rampager
+1430 M The Gitrog Monster
+1431 C Goblin Deathraiders
+1432 C Grim Contest
+1433 R Guided Passage
+1434 C Hammer Dropper
+1435 U Hidden Stockpile
+1436 U Highspire Mantis
+1437 C Hypothesizzle
+1438 U Iroas's Champion
+1439 U Join Shields
+1440 U Jungle Barrier
+1441 U Kathari Remnant
+1442 U Kin-Tree Invocation
+1443 U Kiora's Follower
+1444 U Kiss of the Amesha
+1445 R Kolaghan's Command
+1446 M Kruphix, God of Horizons
+1447 C Lawmage's Binding
+1448 U Lightning Helix
+1449 M Maelstrom Archangel
+1450 U Mardu Roughrider
+1451 C Martial Glory
+1452 U Maverick Thopterist
+1453 R Meddling Mage
+1454 U Mercurial Geists
+1455 M Meren of Clan Nel Toth
+1456 U Migratory Route
+1457 U Mortify
+1458 U Naya Charm
+1459 R Nemesis of Reason
+1460 R Nin, the Pain Artist
+1461 U Obelisk Spider
+1462 U Ochran Assassin
+1463 U Pillory of the Sleepless
+1464 U Plaxcaster Frogling
+1465 U Pollenbright Wings
+1466 U Putrefy
+1467 C Qasali Pridemage
+1468 M Queen Marchesa
+1469 R Questing Phelddagrif
+1470 U Raff Capashen, Ship's Mage
+1471 U Raging Swordtooth
+1472 U Reclusive Artificer
+1473 U Reflector Mage
+1474 U Rhox War Monk
+1475 C Riptide Crab
+1476 R Rith, the Awakener
+1477 U River Hoopoe
+1478 C Rosemane Centaur
+1479 U Satyr Enchanter
+1480 R Savage Knuckleblade
+1481 U Savage Twister
+1482 U Sedraxis Specter
+1483 U Shambling Remains
+1484 U Shardless Agent
+1485 U Shipwreck Singer
+1486 U Skyward Eye Prophets
+1487 M Sliver Hivelord
+1488 U Soul Manipulation
+1489 U Sprouting Thrinax
+1490 U Stormchaser Chimera
+1491 U Sultai Charm
+1492 U Sultai Soothsayer
+1493 R Supreme Verdict
+1494 U Tatyova, Benthic Druid
+1495 C Terminate
+1496 U Thought Erasure
+1497 R Time Sieve
+1498 C Tithe Drinker
+1499 U Tower Gargoyle
+1500 U Treacherous Terrain
+1501 U Underworld Coinsmith
+1502 U Unflinching Courage
+1503 U Unlicensed Disintegration
+1504 U Urban Evolution
+1505 U Vengeful Rebirth
+1506 R Violent Ultimatum
+1507 U Warden of the Eye
+1508 R Wargate
+1509 U Wayfaring Temple
+1510 U Weapons Trainer
+1511 U Wee Dragonauts
+1512 U Winding Constrictor
+1513 U Woolly Thoctar
+1514 R Yavimaya's Embrace
+1515 R Yuriko, the Tiger's Shadow
+1516 U Zealous Persecution
+1517 C Zhur-Taa Druid
+1518 R Boros Reckoner
+1519 R Debtors' Knell
+1520 R Dominus of Fealty
+1521 R Doomgape
+1522 R Enchanted Evening
+1523 C Giantbaiting
+1524 C Gift of Orzhova
+1525 U Gwyllion Hedge-Mage
+1526 C Manamorphose
+1527 U Mistmeadow Witch
+1528 U Nucklavee
+1529 R Oracle of Nectars
+1530 R Rhys the Redeemed
+1531 U Rosheen Meanderer
+1532 U Selesnya Guildmage
+1533 U Shrewd Hatchling
+1534 U Slave of Bolas
+1535 U Thopter Foundry
+1536 U Claim // Fame
+1537 R Commit // Memory
+1538 C Fire // Ice
+1539 R Aetherflux Reservoir
+1540 C Aether Spellbomb
+1541 R Akroan Horse
+1542 C Alchemist's Vial
+1543 M Alhammarret's Archive
+1544 C Alloy Myr
+1545 C Armillary Sphere
+1546 U Ashnod's Altar
+1547 R Basilisk Collar
+1548 R Belbe's Portal
+1549 C Blinding Souleater
+1550 U Bomat Bazaar Barge
+1551 C Bone Saw
+1552 C Bonesplitter
+1553 R Boompile
+1554 U Bottle Gnomes
+1555 U Burnished Hart
+1556 R Caged Sun
+1557 U Cathodion
+1558 R Cauldron of Souls
+1559 R Chromatic Lantern
+1560 C Chromatic Star
+1561 R Coat of Arms
+1562 U Coldsteel Heart
+1563 U Consulate Dreadnought
+1564 U Contagion Clasp
+1565 C Copper Carapace
+1566 R Coveted Jewel
+1567 U Crenellated Wall
+1568 U Crystal Ball
+1569 U Crystal Chimes
+1570 U Crystal Shard
+1571 R Darksteel Garrison
+1572 U Diamond Mare
+1573 R Dolmen Gate
+1574 R Draco
+1575 U Dragon Mask
+1576 R Eater of Days
+1577 M Eldrazi Monument
+1578 U Elixir of Immortality
+1579 U Emmessi Tome
+1580 U Etched Oracle
+1581 U Farmstead Gleaner
+1582 U Filigree Familiar
+1583 C Flayer Husk
+1584 R Font of Mythos
+1585 U Foundry Inspector
+1586 U Fountain of Renewal
+1587 C Frogmite
+1588 R Goblin Charbelcher
+1589 C Gruul Signet
+1590 C Guardians of Meletis
+1591 U Heavy Arbalest
+1592 U Helm of Awakening
+1593 U Herald's Horn
+1594 C Hexplate Golem
+1595 U Hot Soup
+1596 U Icy Manipulator
+1597 C Implement of Malice
+1598 C Irontread Crusher
+1599 U Juggernaut
+1600 U Lightning Greaves
+1601 C Lotus Petal
+1602 U Loxodon Warhammer
+1603 M Mana Crypt
+1604 U Mask of Memory
+1605 U Meteorite
+1606 U Millikin
+1607 U Millstone
+1608 R Mimic Vat
+1609 C Mind Stone
+1610 U Mishra's Bauble
+1611 C Moonglove Extract
+1612 U Mortarpod
+1613 U Myr Retriever
+1614 C Myr Sire
+1615 C Ornithopter
+1616 U Palladium Myr
+1617 C Peace Strider
+1618 U Perilous Myr
+1619 R Phyrexian Soulgorger
+1620 U Pilgrim's Eye
+1621 R Precursor Golem
+1622 C Prophetic Prism
+1623 C Renegade Map
+1624 U Rhonas's Monument
+1625 U Sandstone Oracle
+1626 C Serrated Arrows
+1627 C Short Sword
+1628 U Sigil of Valor
+1629 C Simic Locket
+1630 U Skullclamp
+1631 C Skyscanner
+1632 R Solemn Simulacrum
+1633 U Sol Ring
+1634 U Sorcerer's Broom
+1635 U Spy Kit
+1636 U Sunset Pyramid
+1637 U Suspicious Bookcase
+1638 R Sword of the Animist
+1639 C Thought Vessel
+1640 U Thran Dynamo
+1641 U Thran Golem
+1642 U Tormod's Crypt
+1643 R Tower of Eons
+1644 R Trading Post
+1645 U Trepanation Blade
+1646 U Umbral Mantle
+1647 C Universal Automaton
+1648 C Universal Solvent
+1649 U Whispersilk Cloak
+1650 U Aether Hub
+1651 U Akoum Refuge
+1652 C Ancient Den
+1653 U Ancient Ziggurat
+1654 U Arcane Sanctum
+1655 R Arch of Orazca
+1656 C Ash Barrens
+1657 U Blasted Landscape
+1658 U Blighted Fen
+1659 C Blossoming Sands
+1660 C Bojuka Bog
+1661 U Crumbling Necropolis
+1662 C Darksteel Citadel
+1663 C Dismal Backwater
+1664 U Dreadship Reef
+1665 C Evolving Wilds
+1666 U Faerie Conclave
+1667 U Field of Ruin
+1668 C Forgotten Cave
+1669 U Frontier Bivouac
+1670 C Gateway Plaza
+1671 U Ghost Quarter
+1672 R Gilt-Leaf Palace
+1673 U Goblin Burrows
+1674 U Graypelt Refuge
+1675 C Great Furnace
+1676 C Jungle Hollow
+1677 U Jungle Shrine
+1678 U Kazandu Refuge
+1679 U Krosan Verge
+1680 U Mishra's Factory
+1681 U New Benalia
+1682 C Orzhov Basilica
+1683 U Reliquary Tower
+1684 U Rogue's Passage
+1685 U Sandsteppe Citadel
+1686 C Scoured Barrens
+1687 U Sejiri Refuge
+1688 U Skarrg, the Rage Pits
+1689 C Swiftwater Cliffs
+1690 U Tectonic Edge
+1691 U Temple of the False God
+1692 C Thornwood Falls
+1693 U Unclaimed Territory
+1694 U Wirewood Lodge
diff --git a/forge-gui/res/editions/Secret Lair Drop Series.txt b/forge-gui/res/editions/Secret Lair Drop Series.txt
index add00a13e0f..473a0ef4d5e 100644
--- a/forge-gui/res/editions/Secret Lair Drop Series.txt
+++ b/forge-gui/res/editions/Secret Lair Drop Series.txt
@@ -44,7 +44,11 @@ Type=Other
52 M Meren of Clan Nel Toth
53 M Narset, Enlightened Master
54 M Oona, Queen of the Fae
-55 M Saskia, the Unyielding
+55 M Saskia the Unyielding
+59 R Squire
+60 R Storm Crow
+61 R Goblin Snowman
+62 R Mudhole
68 M Heliod, God of the Sun
69 M Karametra, God of Harvests
70 M Iroas, God of Victory
diff --git a/forge-gui/res/editions/Secret Lair Ultimate Edition.txt b/forge-gui/res/editions/Secret Lair Ultimate Edition.txt
new file mode 100644
index 00000000000..33fc84e11c6
--- /dev/null
+++ b/forge-gui/res/editions/Secret Lair Ultimate Edition.txt
@@ -0,0 +1,12 @@
+[metadata]
+Code=SLU
+Date=2020-05-29
+Name=Secret Lair: Ultimate Edition
+Type=Reprint
+
+[cards]
+1 R Marsh Flats
+2 R Scalding Tarn
+3 R Verdant Catacombs
+4 R Arid Mesa
+5 R Misty Rainforest
diff --git a/forge-gui/res/editions/Signature Spellbook Chandra.txt b/forge-gui/res/editions/Signature Spellbook Chandra.txt
new file mode 100644
index 00000000000..e9d17ca1d3f
--- /dev/null
+++ b/forge-gui/res/editions/Signature Spellbook Chandra.txt
@@ -0,0 +1,9 @@
+[metadata]
+Code=SS3
+Date=2020-06-26
+Name=Signature Spellbook: Chandra
+Type=Reprint
+
+[cards]
+1 M Chandra, Torch of Defiance
+4 M Past in Flames
diff --git a/forge-gui/res/editions/Theros Beyond Death.txt b/forge-gui/res/editions/Theros Beyond Death.txt
index 65fd9f6ae80..6d8b9f91a2d 100644
--- a/forge-gui/res/editions/Theros Beyond Death.txt
+++ b/forge-gui/res/editions/Theros Beyond Death.txt
@@ -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
diff --git a/forge-gui/res/editions/Throne of Eldraine.txt b/forge-gui/res/editions/Throne of Eldraine.txt
index 1c567deab3e..b08eab5cb35 100644
--- a/forge-gui/res/editions/Throne of Eldraine.txt
+++ b/forge-gui/res/editions/Throne of Eldraine.txt
@@ -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
diff --git a/forge-gui/res/editions/Unstable.txt b/forge-gui/res/editions/Unstable.txt
index 636eead1d2a..efa45ccd3cb 100644
--- a/forge-gui/res/editions/Unstable.txt
+++ b/forge-gui/res/editions/Unstable.txt
@@ -20,6 +20,8 @@ w_2_2_cat
w_0_1_goat
w_1_1_spirit_flying
w_1_1_spirit_flying
+w_4_4_angel_flying
+w_4_4_angel_flying
w_1_1_soldier
u_1_1_faerie_spy_flying_haste_draw
u_8_8_octopus
@@ -45,6 +47,7 @@ g_1_1_squirrel
g_6_6_wurm
gw_x_x_elemental_total_creatures
gw_x_x_elemental_total_creatures
+wubrg_4_4_dragon
c_a_clue_draw
c_a_clue_draw
c_x_x_a_construct
diff --git a/forge-gui/res/editions/Weatherlight.txt b/forge-gui/res/editions/Weatherlight.txt
index 3689f4a2a66..1ef49097fbf 100644
--- a/forge-gui/res/editions/Weatherlight.txt
+++ b/forge-gui/res/editions/Weatherlight.txt
@@ -178,5 +178,5 @@ R Winding Canyons
R Xanthic Statue
C Zombie Scavengers
-[token]
+[tokens]
g_1_1_squirrel
\ No newline at end of file
diff --git a/forge-gui/res/formats/Casual/Brawl.txt b/forge-gui/res/formats/Casual/Brawl.txt
index 804db4799d5..63db92dec93 100644
--- a/forge-gui/res/formats/Casual/Brawl.txt
+++ b/forge-gui/res/formats/Casual/Brawl.txt
@@ -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
diff --git a/forge-gui/res/formats/Digital/Historic.txt b/forge-gui/res/formats/Digital/Historic.txt
new file mode 100644
index 00000000000..0a1157f164f
--- /dev/null
+++ b/forge-gui/res/formats/Digital/Historic.txt
@@ -0,0 +1,8 @@
+[format]
+Name:Historic
+Type:Digital
+Subtype:Arena
+Effective:2019-11-21
+Order:142
+Sets:XLN, RIX, DOM, M19, GRN, G18, RNA, WAR, M20, ELD, HA1, THB, HA2
+Banned:Oko, Thief of Crowns; Once Upon a Time; Veil of Summer
\ No newline at end of file
diff --git a/forge-gui/res/formats/Sanctioned/Legacy.txt b/forge-gui/res/formats/Sanctioned/Legacy.txt
index 404a575f8cd..5d6162ad978 100644
--- a/forge-gui/res/formats/Sanctioned/Legacy.txt
+++ b/forge-gui/res/formats/Sanctioned/Legacy.txt
@@ -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
diff --git a/forge-gui/res/formats/Sanctioned/Modern.txt b/forge-gui/res/formats/Sanctioned/Modern.txt
index 5f00bc45197..78faab16590 100644
--- a/forge-gui/res/formats/Sanctioned/Modern.txt
+++ b/forge-gui/res/formats/Sanctioned/Modern.txt
@@ -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
diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties
index 48007ba7e84..c336f991d72 100644
--- a/forge-gui/res/languages/de-DE.properties
+++ b/forge-gui/res/languages/de-DE.properties
@@ -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.
diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties
index 503298d25a2..3041bb1895d 100644
--- a/forge-gui/res/languages/en-US.properties
+++ b/forge-gui/res/languages/en-US.properties
@@ -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)
@@ -2006,6 +2010,9 @@ lblDamagepreventionHas=Damage prevention: {0}
lblIsExtraTurn=Extra Turn: Yes
lblExtraTurnCountHas=Extra Turn Count: {0}
lblAntedHas=Ante''d: {0}
+lblAdditionalVotes=You get {0} additional votes.
+lblOptionalAdditionalVotes=You may vote {0} additional times.
+lblControlsVote=You choose how each player votes.
#VStack.java
lblAlwaysYes=Always Yes
lblAlwaysNo=Always No
diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties
index 9c4ddf6ed92..29524d91269 100644
--- a/forge-gui/res/languages/es-ES.properties
+++ b/forge-gui/res/languages/es-ES.properties
@@ -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)
diff --git a/forge-gui/res/languages/it-IT.properties b/forge-gui/res/languages/it-IT.properties
index 5b059135cd4..c260b57b87c 100644
--- a/forge-gui/res/languages/it-IT.properties
+++ b/forge-gui/res/languages/it-IT.properties
@@ -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)
diff --git a/forge-gui/res/languages/zh-CN.properties b/forge-gui/res/languages/zh-CN.properties
index 409c129d0e7..c72c559cbac 100644
--- a/forge-gui/res/languages/zh-CN.properties
+++ b/forge-gui/res/languages/zh-CN.properties
@@ -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=下载每张牌的图片(每张牌出现一次)
diff --git a/forge-gui/res/lists/TypeLists.txt b/forge-gui/res/lists/TypeLists.txt
index c4a5c83097f..602bac23b05 100644
--- a/forge-gui/res/lists/TypeLists.txt
+++ b/forge-gui/res/lists/TypeLists.txt
@@ -168,6 +168,7 @@ Ooze:Oozes
Orb:Orbs
Orc:Orcs
Orgg:Orggs
+Otter:Otters
Ouphe:Ouphes
Ox:Oxen
Oyster:Oysters
@@ -209,6 +210,7 @@ Servo:Servos
Shade:Shades
Shaman:Shamans
Shapeshifter:Shapeshifters
+Shark:Sharks
Sheep:Sheep
Siren:Sirens
Skeleton:Skeletons
@@ -309,6 +311,7 @@ Kaya
Kiora
Koth
Liliana
+Lukka
Nahiri
Narset
Nissa
diff --git a/forge-gui/res/puzzle/PS_THB6.pzl b/forge-gui/res/puzzle/PS_THB6.pzl
new file mode 100644
index 00000000000..0fb3f36c66e
--- /dev/null
+++ b/forge-gui/res/puzzle/PS_THB6.pzl
@@ -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
diff --git a/forge-gui/res/puzzle/PS_THB7.pzl b/forge-gui/res/puzzle/PS_THB7.pzl
new file mode 100644
index 00000000000..bd9f0a62678
--- /dev/null
+++ b/forge-gui/res/puzzle/PS_THB7.pzl
@@ -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
diff --git a/forge-gui/res/puzzle/PS_THB8.pzl b/forge-gui/res/puzzle/PS_THB8.pzl
new file mode 100644
index 00000000000..e3649191a1f
--- /dev/null
+++ b/forge-gui/res/puzzle/PS_THB8.pzl
@@ -0,0 +1,16 @@
+[metadata]
+Name:Possibility Storm - Theros Beyond Death #08
+URL:https://i2.wp.com/www.possibilitystorm.com/wp-content/uploads/2020/03/151.-THB8-scaled.jpg
+Goal:Win
+Turns:1
+Difficulty:Uncommon
+Description:Win this turn.
+[state]
+humanlife=20
+ailife=23
+turn=1
+activeplayer=human
+activephase=MAIN1
+humanhand=Bone Splinters;Gray Merchant of Asphodel;Mogis's Favor;Kaya's Ghostform;Massacre Girl
+humanbattlefield=Nightmare Shepherd;Nightmare Shepherd;Nyx Lotus;Swamp;Swamp;Swamp;Swamp;Swamp;Swamp
+aibattlefield=Bishop of Wings;Angelic Guardian;Sunblade Angel
diff --git a/forge-gui/res/puzzle/PS_THB9.pzl b/forge-gui/res/puzzle/PS_THB9.pzl
new file mode 100644
index 00000000000..0524bae6e5a
--- /dev/null
+++ b/forge-gui/res/puzzle/PS_THB9.pzl
@@ -0,0 +1,19 @@
+[metadata]
+Name:Possibility Storm - Theros Beyond Death #09
+URL:https://i0.wp.com/www.possibilitystorm.com/wp-content/uploads/2020/03/152.-THB9-scaled.jpg
+Goal:Win
+Turns:1
+Difficulty:Mythic
+Description:Win this turn. Assume your opponent has no mana available and no cards in hand. Assume both players have over 30 cards left in their library, and that any drawn are irrelevant to the puzzle.
+[state]
+humanlife=2
+ailife=94
+turn=1
+activeplayer=human
+activephase=MAIN1
+humanhand=Terror of Mount Velus;Awaken the Erstwhile;Dragon Mage;Corpse Knight;Fling
+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;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt
+humanbattlefield=The Royal Scions|Counters:LOYALTY=8;Purphoros, Bronze-Blooded;Smothering Tithe;Mace of the Valiant;Bag of Holding;Bag of Holding;Temple of Malice|NoETBTrigs;Temple of Malice|NoETBTrigs;Temple of Malice|NoETBTrigs;Temple of Malice|NoETBTrigs;Temple of Triumph|NoETBTrigs;Temple of Triumph|NoETBTrigs;Temple of Triumph|NoETBTrigs;Temple of Triumph|NoETBTrigs
+ailibrary=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;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt
+aibattlefield=Knight of Autumn;Vindictive Vampire;Ajani's Pridemate|Counters:P1P1=15;Ajani, Strength of the Pride|Counters:LOYALTY=2;Sorin, Vengeful Bloodlord|Counters:LOYALTY=2
+humanprecast=Smothering Tithe:TrigToken;Smothering Tithe:TrigToken;Smothering Tithe:TrigToken;Smothering Tithe:TrigToken
diff --git a/forge-gui/res/sealed/MTGACube.sealed b/forge-gui/res/sealed/MTGACube.sealed
new file mode 100644
index 00000000000..07a36bb9909
--- /dev/null
+++ b/forge-gui/res/sealed/MTGACube.sealed
@@ -0,0 +1,8 @@
+Name:MTGACube
+DeckFile:MTGACube
+IgnoreRarity:True
+LandSetCode:THB
+Singleton:True
+
+Booster: 5 Common, 5 Uncommon, 5 Rare, 1 Mythic
+NumPacks:3
\ No newline at end of file
diff --git a/forge-gui/res/skins/darkred/bg_match.jpg b/forge-gui/res/skins/darkred/bg_match.jpg
index f926fee0812..fb7c0298072 100644
Binary files a/forge-gui/res/skins/darkred/bg_match.jpg and b/forge-gui/res/skins/darkred/bg_match.jpg differ
diff --git a/forge-gui/res/skins/default/font1.ttf b/forge-gui/res/skins/default/font1.ttf
index 4b4ecc66671..8723f9e2252 100644
Binary files a/forge-gui/res/skins/default/font1.ttf and b/forge-gui/res/skins/default/font1.ttf differ
diff --git a/forge-gui/res/skins/default/sprite_border.png b/forge-gui/res/skins/default/sprite_border.png
index c6be80f099a..4fe259b55c7 100644
Binary files a/forge-gui/res/skins/default/sprite_border.png and b/forge-gui/res/skins/default/sprite_border.png differ
diff --git a/forge-gui/res/skins/default/sprite_icons.png b/forge-gui/res/skins/default/sprite_icons.png
index 7cff5a518d1..384ae908ccf 100644
Binary files a/forge-gui/res/skins/default/sprite_icons.png and b/forge-gui/res/skins/default/sprite_icons.png differ
diff --git a/forge-gui/res/skins/default/sprite_manaicons.png b/forge-gui/res/skins/default/sprite_manaicons.png
index 3b8f734e939..8cbbda13212 100644
Binary files a/forge-gui/res/skins/default/sprite_manaicons.png and b/forge-gui/res/skins/default/sprite_manaicons.png differ
diff --git a/forge-gui/res/tokenscripts/c_0_4_wall_defender.txt b/forge-gui/res/tokenscripts/c_0_4_a_wall_defender.txt
similarity index 67%
rename from forge-gui/res/tokenscripts/c_0_4_wall_defender.txt
rename to forge-gui/res/tokenscripts/c_0_4_a_wall_defender.txt
index 03a29952252..2b5be99e3e2 100644
--- a/forge-gui/res/tokenscripts/c_0_4_wall_defender.txt
+++ b/forge-gui/res/tokenscripts/c_0_4_a_wall_defender.txt
@@ -1,6 +1,6 @@
Name:Wall
ManaCost:no cost
-Types:Creature Wall
+Types:Artifact Creature Wall
PT:0/4
K:Defender
Oracle:Defender
diff --git a/forge-gui/res/tokenscripts/c_a_feather_sac.txt b/forge-gui/res/tokenscripts/c_a_feather_sac.txt
new file mode 100644
index 00000000000..21ff81b91b4
--- /dev/null
+++ b/forge-gui/res/tokenscripts/c_a_feather_sac.txt
@@ -0,0 +1,5 @@
+Name:Feather
+ManaCost:no cost
+Types:Artifact
+A:AB$ ChangeZone | Cost$ 1 T Sac<1/CARDNAME> | TgtPrompt$ Choose target Phoenix card in your graveyard | ValidTgts$ Phoenix.YouOwn | Origin$ Graveyard | Destination$ Battlefield | Tapped$ True | SpellDescription$ Return target Phoenix card from your graveyard to the battlefield tapped.
+Oracle:{1}, {T}, Sacrifice this artifact: Return target Phoenix card from your graveyard to the battlefield tapped.
diff --git a/forge-gui/res/tokenscripts/g_x_x_dinosaur_beast_trample.txt b/forge-gui/res/tokenscripts/g_x_x_dinosaur_beast_trample.txt
new file mode 100644
index 00000000000..967d820125a
--- /dev/null
+++ b/forge-gui/res/tokenscripts/g_x_x_dinosaur_beast_trample.txt
@@ -0,0 +1,7 @@
+Name:Dinosaur Beast
+Colors:green
+ManaCost:no cost
+PT:*/*
+Types:Creature Dinosaur Beast
+K:Trample
+Oracle:Trample
diff --git a/forge-gui/res/tokenscripts/r_1_1_dinosaur_haste.txt b/forge-gui/res/tokenscripts/r_1_1_dinosaur_haste.txt
new file mode 100644
index 00000000000..6c3201e848c
--- /dev/null
+++ b/forge-gui/res/tokenscripts/r_1_1_dinosaur_haste.txt
@@ -0,0 +1,7 @@
+Name:Dinosaur
+ManaCost:no cost
+Types:Creature Dinosaur
+Colors:red
+PT:1/1
+K:Haste
+Oracle:Haste
\ No newline at end of file
diff --git a/forge-gui/res/tokenscripts/u_8_8_kraken.txt b/forge-gui/res/tokenscripts/u_8_8_kraken.txt
new file mode 100644
index 00000000000..3df1f45a889
--- /dev/null
+++ b/forge-gui/res/tokenscripts/u_8_8_kraken.txt
@@ -0,0 +1,6 @@
+Name:Kraken
+ManaCost:no cost
+Types:Creature Kraken
+Colors:blue
+PT:8/8
+Oracle:
\ No newline at end of file
diff --git a/forge-gui/res/tokenscripts/u_x_x_shark_flying.txt b/forge-gui/res/tokenscripts/u_x_x_shark_flying.txt
new file mode 100644
index 00000000000..8da980b6426
--- /dev/null
+++ b/forge-gui/res/tokenscripts/u_x_x_shark_flying.txt
@@ -0,0 +1,7 @@
+Name:Shark
+ManaCost:no cost
+Types:Creature Shark
+Colors:blue
+PT:*/*
+K:Flying
+Oracle:Flying
diff --git a/forge-gui/res/tokenscripts/w_1_1_cat_bird_flying.txt b/forge-gui/res/tokenscripts/w_1_1_cat_bird_flying.txt
new file mode 100644
index 00000000000..2b9d9e2852a
--- /dev/null
+++ b/forge-gui/res/tokenscripts/w_1_1_cat_bird_flying.txt
@@ -0,0 +1,7 @@
+Name:Cat Bird
+ManaCost:no cost
+Types:Creature Cat Bird
+Colors:white
+PT:1/1
+K:Flying
+Oracle:Flying
\ No newline at end of file
diff --git a/forge-gui/src/main/config/forge.command b/forge-gui/src/main/config/forge.command
deleted file mode 100644
index e6068616734..00000000000
--- a/forge-gui/src/main/config/forge.command
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-cd "`dirname \"$0\"`"
-java -Xmx1024m -jar $project.build.finalName$
\ No newline at end of file
diff --git a/forge-gui/src/main/config/forge.sh b/forge-gui/src/main/config/forge.sh
deleted file mode 100644
index e6068616734..00000000000
--- a/forge-gui/src/main/config/forge.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-cd "`dirname \"$0\"`"
-java -Xmx1024m -jar $project.build.finalName$
\ No newline at end of file
diff --git a/forge-gui/src/main/java/forge/GuiBase.java b/forge-gui/src/main/java/forge/GuiBase.java
index aee6e20dd49..52d487a467c 100644
--- a/forge-gui/src/main/java/forge/GuiBase.java
+++ b/forge-gui/src/main/java/forge/GuiBase.java
@@ -6,23 +6,21 @@ public class GuiBase {
private static IGuiBase guiInterface;
private static boolean propertyConfig = true;
private static boolean networkplay = false;
+ private static boolean isAndroidport = false;
+ private static boolean interrupted = false;
- public static IGuiBase getInterface() {
- return guiInterface;
- }
- public static void setInterface(IGuiBase i0) {
- guiInterface = i0;
- }
- public static void enablePropertyConfig(boolean value) {
- propertyConfig = value;
- }
- public static boolean isNetworkplay() {
- return networkplay;
- }
- public static void setNetworkplay(boolean value) {
- networkplay = value;
- }
- public static boolean hasPropertyConfig() {
- return propertyConfig;
- }
+ public static IGuiBase getInterface() { return guiInterface; }
+ public static void setInterface(IGuiBase i0) { guiInterface = i0; }
+
+ public static void setIsAndroid(boolean value) { isAndroidport = value; }
+ public static boolean isAndroid() { return isAndroidport; }
+
+ public static boolean isNetworkplay() { return networkplay; }
+ public static void setNetworkplay(boolean value) { networkplay = value; }
+
+ public static boolean hasPropertyConfig() { return propertyConfig; }
+ public static void enablePropertyConfig(boolean value) { propertyConfig = value; }
+
+ public static void setInterrupted(boolean value) { interrupted = value; }
+ public static boolean isInterrupted() { return interrupted; }
}
diff --git a/forge-gui/src/main/java/forge/assets/FSkinProp.java b/forge-gui/src/main/java/forge/assets/FSkinProp.java
index 5468c1e2914..38edff0ea32 100644
--- a/forge-gui/src/main/java/forge/assets/FSkinProp.java
+++ b/forge-gui/src/main/java/forge/assets/FSkinProp.java
@@ -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),
diff --git a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java
index 2ccc98d7e08..575aa7031b8 100644
--- a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java
+++ b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java
@@ -156,6 +156,9 @@ public class DeckgenUtil {
//}
}
+ //remove any cards not valid in format
+ selectedCards = Lists.newArrayList(Iterables.filter(selectedCards, format.getFilterPrinted()));
+
List toRemove = new ArrayList<>();
//randomly remove cards
@@ -252,6 +255,9 @@ public class DeckgenUtil {
//}
}
+ //remove any cards not valid in format
+ selectedCards = Lists.newArrayList(Iterables.filter(selectedCards, format.getFilterPrinted()));
+
List toRemove = new ArrayList<>();
//randomly remove cards
diff --git a/forge-gui/src/main/java/forge/download/AutoUpdater.java b/forge-gui/src/main/java/forge/download/AutoUpdater.java
new file mode 100644
index 00000000000..8d050efecef
--- /dev/null
+++ b/forge-gui/src/main/java/forge/download/AutoUpdater.java
@@ -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 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("(.*)");
+ 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 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 callback = new WaitCallback() {
+ @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);
+ }
+ }
+}
diff --git a/forge-gui/src/main/java/forge/download/GuiDownloadZipService.java b/forge-gui/src/main/java/forge/download/GuiDownloadZipService.java
index f1d1dc9b8d4..dd142b04cf4 100644
--- a/forge-gui/src/main/java/forge/download/GuiDownloadZipService.java
+++ b/forge-gui/src/main/java/forge/download/GuiDownloadZipService.java
@@ -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;
diff --git a/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java b/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java
index 547b185b681..e6ba852fce2 100644
--- a/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java
+++ b/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java
@@ -6,6 +6,7 @@ import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.ability.ApiType;
import forge.game.card.Card;
+import forge.game.card.CardPredicates;
import forge.game.card.CardView;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -16,6 +17,7 @@ import forge.player.PlayerZoneUpdate;
import forge.player.PlayerZoneUpdates;
import forge.properties.ForgeConstants;
import forge.properties.ForgePreferences;
+import forge.util.Aggregates;
import forge.util.ITriggerEvent;
import forge.util.TextUtil;
@@ -148,6 +150,9 @@ public final class InputSelectTargets extends InputSyncronizedBase {
return false;
}
+ // TODO should use sa.canTarget(card) instead?
+ // it doesn't have messages
+
//If the card is not a valid target
if (!card.canBeTargetedBy(sa)) {
showMessage(sa.getHostCard() + " - Cannot target this card (Shroud? Protection? Restrictions).");
@@ -170,6 +175,20 @@ public final class InputSelectTargets extends InputSyncronizedBase {
return false;
}
+ if (sa.hasParam("MaxTotalTargetCMC")) {
+ int maxTotalCMC = tgt.getMaxTotalCMC(sa.getHostCard(), sa);
+ if (maxTotalCMC > 0) {
+ int soFar = Aggregates.sum(sa.getTargets().getTargetCards(), CardPredicates.Accessors.fnGetCmc);
+ if (!sa.isTargeting(card)) {
+ soFar += card.getCMC();
+ }
+ if (soFar > maxTotalCMC) {
+ showMessage(sa.getHostCard() + " - Cannot target this card (CMC limit exceeded)");
+ return false;
+ }
+ }
+ }
+
// If all cards must have different controllers
if (tgt.isDifferentControllers()) {
final List targetedControllers = new ArrayList<>();
@@ -186,12 +205,7 @@ public final class InputSelectTargets extends InputSyncronizedBase {
}
if (!choices.contains(card)) {
- if (card.isPlaneswalker() && sa.getApi() == ApiType.DealDamage) {
- showMessage(sa.getHostCard() + " - To deal an opposing Planeswalker direct damage, target its controller and then redirect the damage on resolution.");
- }
- else {
- showMessage(sa.getHostCard() + " - The selected card is not a valid choice to be targeted.");
- }
+ showMessage(sa.getHostCard() + " - The selected card is not a valid choice to be targeted.");
return false;
}
diff --git a/forge-gui/src/main/java/forge/model/FModel.java b/forge-gui/src/main/java/forge/model/FModel.java
index d0c182e890c..fe3dc41129f 100644
--- a/forge-gui/src/main/java/forge/model/FModel.java
+++ b/forge-gui/src/main/java/forge/model/FModel.java
@@ -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,11 +143,16 @@ 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,
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
- magicDb = new StaticData(reader, tokenReader, ForgeConstants.EDITIONS_DIR, ForgeConstants.BLOCK_DATA_DIR);
+ magicDb = new StaticData(reader, tokenReader, ForgeConstants.EDITIONS_DIR, ForgeConstants.BLOCK_DATA_DIR, FModel.getPreferences().getPrefBoolean(FPref.UI_LOAD_UNKNOWN_CARDS));
CardTranslation.preloadTranslation(preferences.getPref(FPref.UI_LANGUAGE), ForgeConstants.LANG_DIR);
//create profile dirs if they don't already exist
@@ -220,8 +225,6 @@ public final class FModel {
achievements.put(GameType.Quest, new QuestAchievements());
achievements.put(GameType.PlanarConquest, new PlanarConquestAchievements());
achievements.put(GameType.Puzzle, new PuzzleAchievements());
-
-
//preload AI profiles
AiProfileUtil.loadAllProfiles(ForgeConstants.AI_PROFILE_DIR);
diff --git a/forge-gui/src/main/java/forge/net/CObjectInputStream.java b/forge-gui/src/main/java/forge/net/CObjectInputStream.java
new file mode 100644
index 00000000000..347879e6570
--- /dev/null
+++ b/forge-gui/src/main/java/forge/net/CObjectInputStream.java
@@ -0,0 +1,49 @@
+package forge.net;
+
+import io.netty.handler.codec.serialization.ClassResolver;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.io.StreamCorruptedException;
+
+public class CObjectInputStream extends ObjectInputStream {
+ private final ClassResolver classResolver;
+
+ CObjectInputStream(InputStream in, ClassResolver classResolver) throws IOException {
+ super(in);
+ this.classResolver = classResolver;
+ }
+
+ @Override
+ protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
+ int type = read();
+ if (type < 0) {
+ throw new EOFException();
+ } else {
+ switch(type) {
+ case 0:
+ return super.readClassDescriptor();
+ case 1:
+ String className = readUTF();
+ Class> clazz = classResolver.resolve(className);
+ return ObjectStreamClass.lookupAny(clazz);
+ default:
+ throw new StreamCorruptedException("Unexpected class descriptor type: " + type);
+ }
+ }
+ }
+
+ @Override
+ protected Class> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+ Class> clazz;
+ try {
+ clazz = classResolver.resolve(desc.getName());
+ } catch (ClassNotFoundException ignored) {
+ clazz = super.resolveClass(desc);
+ }
+ return clazz;
+ }
+}
diff --git a/forge-gui/src/main/java/forge/net/CObjectOutputStream.java b/forge-gui/src/main/java/forge/net/CObjectOutputStream.java
new file mode 100644
index 00000000000..e0c408aa923
--- /dev/null
+++ b/forge-gui/src/main/java/forge/net/CObjectOutputStream.java
@@ -0,0 +1,21 @@
+package forge.net;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OutputStream;
+
+public class CObjectOutputStream extends ObjectOutputStream {
+ static final int TYPE_THIN_DESCRIPTOR = 1;
+
+ CObjectOutputStream(OutputStream out) throws IOException {
+ super(out);
+ }
+
+ @Override
+ protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException {
+ //we only pass this and the decoder will lookup in the stream (faster method both mobile and desktop)
+ write(TYPE_THIN_DESCRIPTOR);
+ writeUTF(desc.getName());
+ }
+}
diff --git a/forge-gui/src/main/java/forge/net/CompatibleObjectDecoder.java b/forge-gui/src/main/java/forge/net/CompatibleObjectDecoder.java
new file mode 100644
index 00000000000..c288eace00b
--- /dev/null
+++ b/forge-gui/src/main/java/forge/net/CompatibleObjectDecoder.java
@@ -0,0 +1,48 @@
+package forge.net;
+
+import forge.GuiBase;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufInputStream;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.serialization.ClassResolver;
+import net.jpountz.lz4.LZ4BlockInputStream;
+
+import java.io.ObjectInputStream;
+import java.io.StreamCorruptedException;
+
+public class CompatibleObjectDecoder extends LengthFieldBasedFrameDecoder {
+ private final ClassResolver classResolver;
+
+ public CompatibleObjectDecoder(ClassResolver classResolver) {
+ this(1048576, classResolver);
+ }
+
+ public CompatibleObjectDecoder(int maxObjectSize, ClassResolver classResolver) {
+ super(maxObjectSize, 0, 4, 0, 4);
+ this.classResolver = classResolver;
+ }
+
+ @Override
+ protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
+ ByteBuf frame = (ByteBuf)super.decode(ctx, in);
+ if (frame == null) {
+ return null;
+ } else {
+ ObjectInputStream ois = GuiBase.hasPropertyConfig() ?
+ new ObjectInputStream(new LZ4BlockInputStream(new ByteBufInputStream(frame, true))):
+ new CObjectInputStream(new LZ4BlockInputStream(new ByteBufInputStream(frame, true)),this.classResolver);
+
+ Object var5 = null;
+ try {
+ var5 = ois.readObject();
+ } catch (StreamCorruptedException e) {
+ System.err.println(String.format("Version Mismatch: %s", e.getMessage()));
+ } finally {
+ ois.close();
+ }
+
+ return var5;
+ }
+ }
+}
diff --git a/forge-gui/src/main/java/forge/net/CompatibleObjectEncoder.java b/forge-gui/src/main/java/forge/net/CompatibleObjectEncoder.java
new file mode 100644
index 00000000000..59709288d41
--- /dev/null
+++ b/forge-gui/src/main/java/forge/net/CompatibleObjectEncoder.java
@@ -0,0 +1,38 @@
+package forge.net;
+
+import forge.GuiBase;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufOutputStream;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+import net.jpountz.lz4.LZ4BlockOutputStream;
+
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+public class CompatibleObjectEncoder extends MessageToByteEncoder {
+ private static final byte[] LENGTH_PLACEHOLDER = new byte[4];
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
+ int startIdx = out.writerIndex();
+ ByteBufOutputStream bout = new ByteBufOutputStream(out);
+ ObjectOutputStream oout = null;
+
+ try {
+ bout.write(LENGTH_PLACEHOLDER);
+ oout = GuiBase.hasPropertyConfig() ? new ObjectOutputStream(new LZ4BlockOutputStream(bout)) : new CObjectOutputStream(new LZ4BlockOutputStream(bout));
+ oout.writeObject(msg);
+ oout.flush();
+ } finally {
+ if (oout != null) {
+ oout.close();
+ } else {
+ bout.close();
+ }
+ }
+
+ int endIdx = out.writerIndex();
+ out.setInt(startIdx, endIdx - startIdx - 4);
+ }
+}
diff --git a/forge-gui/src/main/java/forge/net/CustomObjectDecoder.java b/forge-gui/src/main/java/forge/net/CustomObjectDecoder.java
deleted file mode 100644
index 47d898769d4..00000000000
--- a/forge-gui/src/main/java/forge/net/CustomObjectDecoder.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package forge.net;
-
-import forge.GuiBase;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufInputStream;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
-import io.netty.handler.codec.serialization.ClassResolver;
-import org.mapdb.elsa.ElsaObjectInputStream;
-
-import java.io.ObjectInputStream;
-
-public class CustomObjectDecoder extends LengthFieldBasedFrameDecoder {
- private final ClassResolver classResolver;
-
- public CustomObjectDecoder(ClassResolver classResolver) {
- this(1048576, classResolver);
- }
-
- public CustomObjectDecoder(int maxObjectSize, ClassResolver classResolver) {
- super(maxObjectSize, 0, 4, 0, 4);
- this.classResolver = classResolver;
- }
-
- protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
- ByteBuf frame = (ByteBuf) super.decode(ctx, in);
- if (frame == null) {
- return null;
- } else {
- if (GuiBase.hasPropertyConfig()){
- ElsaObjectInputStream ois = new ElsaObjectInputStream(new ByteBufInputStream(frame, true));
-
- Object var5;
- try {
- var5 = ois.readObject();
- } finally {
- ois.close();
- }
-
- return var5;
- }
- else {
- ObjectInputStream ois = new ObjectInputStream(new ByteBufInputStream(frame, true));
-
- Object var5;
- try {
- var5 = ois.readObject();
- } finally {
- ois.close();
- }
-
- return var5;
- }
- }
- }
-
- public static int maxObjectsize = 10000000; //10megabyte???
-}
diff --git a/forge-gui/src/main/java/forge/net/CustomObjectEncoder.java b/forge-gui/src/main/java/forge/net/CustomObjectEncoder.java
deleted file mode 100644
index 58dc82a58fb..00000000000
--- a/forge-gui/src/main/java/forge/net/CustomObjectEncoder.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package forge.net;
-
-import forge.GuiBase;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufOutputStream;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.MessageToByteEncoder;
-import org.mapdb.elsa.ElsaObjectOutputStream;
-
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-
-public class CustomObjectEncoder extends MessageToByteEncoder {
- private static final byte[] LENGTH_PLACEHOLDER = new byte[4];
-
- public CustomObjectEncoder() {
- }
-
- protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
- int startIdx = out.writerIndex();
- ByteBufOutputStream bout = new ByteBufOutputStream(out);
-
- if (GuiBase.hasPropertyConfig()){
- ElsaObjectOutputStream oout = null;
- try {
- bout.write(LENGTH_PLACEHOLDER);
- oout = new ElsaObjectOutputStream(bout);
- oout.writeObject(msg);
- oout.flush();
- } finally {
- if (oout != null) {
- oout.close();
- } else {
- bout.close();
- }
- }
- } else {
- ObjectOutputStream oout = null;
- try {
- bout.write(LENGTH_PLACEHOLDER);
- oout = new ObjectOutputStream(bout);
- oout.writeObject(msg);
- oout.flush();
- } finally {
- if (oout != null) {
- oout.close();
- } else {
- bout.close();
- }
- }
- }
-
- int endIdx = out.writerIndex();
- out.setInt(startIdx, endIdx - startIdx - 4);
- }
-}
diff --git a/forge-gui/src/main/java/forge/net/GameProtocolHandler.java b/forge-gui/src/main/java/forge/net/GameProtocolHandler.java
index face68c68ac..d22fc8bdf33 100644
--- a/forge-gui/src/main/java/forge/net/GameProtocolHandler.java
+++ b/forge-gui/src/main/java/forge/net/GameProtocolHandler.java
@@ -1,8 +1,10 @@
package forge.net;
import forge.FThreads;
+import forge.assets.FSkinProp;
import forge.net.event.GuiGameEvent;
import forge.net.event.ReplyEvent;
+import forge.util.gui.SOptionPane;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
@@ -25,6 +27,7 @@ public abstract class GameProtocolHandler extends ChannelInboundHandlerAdapte
@Override
public final void channelRead(final ChannelHandlerContext ctx, final Object msg) {
+ final String[] catchedError = {""};
System.out.println("Received: " + msg);
if (msg instanceof ReplyEvent) {
final ReplyEvent event = (ReplyEvent) msg;
@@ -36,7 +39,9 @@ public abstract class GameProtocolHandler extends ChannelInboundHandlerAdapte
final Method method = protocolMethod.getMethod();
if (method == null) {
- throw new IllegalStateException(String.format("Method %s not found", protocolMethod.name()));
+ //throw new IllegalStateException(String.format("Method %s not found", protocolMethod.name()));
+ catchedError[0] += String.format("IllegalStateException: Method %s not found (GameProtocolHandler.java Line 43)\n", protocolMethod.name());
+ System.err.println(String.format("Method %s not found", protocolMethod.name()));
}
final Object[] args = event.getObjects();
@@ -56,7 +61,9 @@ public abstract class GameProtocolHandler extends ChannelInboundHandlerAdapte
} catch (final IllegalAccessException | IllegalArgumentException e) {
System.err.println(String.format("Unknown protocol method %s with %d args", methodName, args == null ? 0 : args.length));
} catch (final InvocationTargetException e) {
- throw new RuntimeException(e.getTargetException());
+ //throw new RuntimeException(e.getTargetException());
+ catchedError[0] += (String.format("RuntimeException: %s (GameProtocolHandler.java Line 65)\n", e.getTargetException().toString()));
+ System.err.println(e.getTargetException().toString());
}
} else {
Serializable reply = null;
@@ -70,8 +77,11 @@ public abstract class GameProtocolHandler extends ChannelInboundHandlerAdapte
}
} catch (final IllegalAccessException | IllegalArgumentException e) {
System.err.println(String.format("Unknown protocol method %s with %d args, replying with null", methodName, args == null ? 0 : args.length));
- } catch (final InvocationTargetException e) {
- throw new RuntimeException(e.getTargetException());
+ } catch (final NullPointerException | InvocationTargetException e) {
+ //throw new RuntimeException(e.getTargetException());
+ catchedError[0] += e.toString();
+ SOptionPane.showMessageDialog(catchedError[0], "Error", FSkinProp.ICO_WARNING);
+ System.err.println(e.toString());
}
getRemote(ctx).send(new ReplyEvent(event.getId(), reply));
}
diff --git a/forge-gui/src/main/java/forge/net/IOnlineLobby.java b/forge-gui/src/main/java/forge/net/IOnlineLobby.java
index 29d75d91634..be7a2bcefd9 100644
--- a/forge-gui/src/main/java/forge/net/IOnlineLobby.java
+++ b/forge-gui/src/main/java/forge/net/IOnlineLobby.java
@@ -7,4 +7,5 @@ import forge.net.client.FGameClient;
public interface IOnlineLobby {
ILobbyView setLobby(GameLobby lobby);
void setClient(FGameClient client);
+ void closeConn(String msg);
}
diff --git a/forge-gui/src/main/java/forge/net/NetConnectUtil.java b/forge-gui/src/main/java/forge/net/NetConnectUtil.java
index 9de56210721..86d32841074 100644
--- a/forge-gui/src/main/java/forge/net/NetConnectUtil.java
+++ b/forge-gui/src/main/java/forge/net/NetConnectUtil.java
@@ -1,10 +1,10 @@
package forge.net;
import forge.match.LobbySlotType;
+import forge.properties.ForgeConstants;
import org.apache.commons.lang3.StringUtils;
import forge.GuiBase;
-import forge.assets.FSkinProp;
import forge.interfaces.IGuiGame;
import forge.interfaces.ILobbyListener;
import forge.interfaces.ILobbyView;
@@ -145,8 +145,8 @@ public class NetConnectUtil {
}
@Override
public final void close() {
- SOptionPane.showMessageDialog("Your connection to the host (" + url + ") was interrupted.", "Error", FSkinProp.ICO_WARNING);
- onlineLobby.setClient(null);
+ GuiBase.setInterrupted(true);
+ onlineLobby.closeConn("Your connection to the host (" + url + ") was interrupted.");
}
@Override
public ClientGameLobby getLobby() {
@@ -178,7 +178,8 @@ public class NetConnectUtil {
client.connect(hostname, port);
}
catch (Exception ex) {
- return null;
+ //return a message to close the connection so we will not crash...
+ return new ChatMessage(null, ForgeConstants.CLOSE_CONN_COMMAND);
}
return new ChatMessage(null, String.format("Connected to %s:%d", hostname, port));
diff --git a/forge-gui/src/main/java/forge/net/ProtocolMethod.java b/forge-gui/src/main/java/forge/net/ProtocolMethod.java
index 4497b5cf0f9..058c6c260fb 100644
--- a/forge-gui/src/main/java/forge/net/ProtocolMethod.java
+++ b/forge-gui/src/main/java/forge/net/ProtocolMethod.java
@@ -18,12 +18,9 @@ import forge.player.PlayerZoneUpdates;
import forge.trackable.TrackableCollection;
import forge.util.ITriggerEvent;
import forge.util.ReflectionUtil;
-import org.apache.commons.lang3.SerializationUtils;
-import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
-import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Map;
@@ -68,14 +65,14 @@ public enum ProtocolMethod {
order (Mode.SERVER, List.class, String.class, String.class, Integer.TYPE, Integer.TYPE, List.class, List.class, CardView.class, Boolean.TYPE),
sideboard (Mode.SERVER, List.class, CardPool.class, CardPool.class, String.class),
chooseSingleEntityForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, DelayedReveal.class, Boolean.TYPE),
- chooseEntitiesForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, Integer.TYPE, Integer.TYPE, DelayedReveal.class),
+ chooseEntitiesForEffect(Mode.SERVER, List.class, String.class, List.class, Integer.TYPE, Integer.TYPE, DelayedReveal.class),
manipulateCardList (Mode.SERVER, List.class, String.class, Iterable.class, Iterable.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE),
setCard (Mode.SERVER, Void.TYPE, CardView.class),
setSelectables (Mode.SERVER, Void.TYPE, Iterable/*CardView*/.class),
clearSelectables (Mode.SERVER),
refreshField (Mode.SERVER),
// TODO case "setPlayerAvatar":
- openZones (Mode.SERVER, PlayerZoneUpdates.class, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
+ openZones (Mode.SERVER, PlayerZoneUpdates.class, PlayerView.class, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
restoreOldZones (Mode.SERVER, Void.TYPE, PlayerView.class, PlayerZoneUpdates.class),
isUiSetToSkipPhase (Mode.SERVER, Boolean.TYPE, PlayerView.class, PhaseType.class),
setRememberedActions(Mode.SERVER, Void.TYPE),
@@ -159,36 +156,15 @@ public enum ProtocolMethod {
}
public void checkArgs(final Object[] args) {
- if (GuiBase.hasPropertyConfig())
- return; //uses custom serializer for Android 8+..
+ if(!GuiBase.hasPropertyConfig())
+ return; //if the experimental network option is enabled, then check the args, else let the default decoder handle it
+
for (int iArg = 0; iArg < args.length; iArg++) {
- Object arg = null;
- Class> type = null;
- try {
- arg = args[iArg];
- if (this.args.length > iArg)
- type = this.args[iArg];
- }
- catch (ArrayIndexOutOfBoundsException ex){ ex.printStackTrace(); }
- catch(ConcurrentModificationException ex) { ex.printStackTrace(); }
- if (arg != null)
- if (type != null)
- if (!ReflectionUtil.isInstance(arg, type)) {
- throw new InternalError(String.format("Protocol method %s: illegal argument (%d) of type %s, %s expected", name(), iArg, arg.getClass().getName(), type.getName()));
- }
- if (arg != null) {
- // attempt to Serialize each argument, this will throw an exception if it can't.
- try {
- byte[] serialized = SerializationUtils.serialize((Serializable) arg);
- SerializationUtils.deserialize(serialized);
- } catch (ArrayIndexOutOfBoundsException ex) {
- // not sure why this one would be thrown, but it is
- // it also doesn't prevent things from working, so, log for now, pending full network rewrite
- ex.printStackTrace();
- } catch(ConcurrentModificationException ex) {
- // can't seem to avoid this from periodically happening
- ex.printStackTrace();
- }
+ final Object arg = args[iArg];
+ final Class> type = this.args[iArg];
+ if (!ReflectionUtil.isInstance(arg, type)) {
+ //throw new InternalError(String.format("Protocol method %s: illegal argument (%d) of type %s, %s expected", name(), iArg, arg.getClass().getName(), type.getName()));
+ System.err.println(String.format("InternalError: Protocol method %s: illegal argument (%d) of type %s, %s expected (ProtocolMethod.java)", name(), iArg, arg.getClass().getName(), type.getName()));
}
}
}
@@ -199,7 +175,8 @@ public enum ProtocolMethod {
return;
}
if (!ReflectionUtil.isInstance(value, returnType)) {
- throw new IllegalStateException(String.format("Protocol method %s: illegal return object type %s returned by client, expected %s", name(), value.getClass().getName(), getReturnType().getName()));
+ //throw new IllegalStateException(String.format("Protocol method %s: illegal return object type %s returned by client, expected %s", name(), value.getClass().getName(), getReturnType().getName()));
+ System.err.println(String.format("IllegalStateException: Protocol method %s: illegal return object type %s returned by client, expected %s (ProtocolMethod.java)", name(), value.getClass().getName(), getReturnType().getName()));
}
}
}
diff --git a/forge-gui/src/main/java/forge/net/client/FGameClient.java b/forge-gui/src/main/java/forge/net/client/FGameClient.java
index 148118df536..75547b95677 100644
--- a/forge-gui/src/main/java/forge/net/client/FGameClient.java
+++ b/forge-gui/src/main/java/forge/net/client/FGameClient.java
@@ -1,7 +1,7 @@
package forge.net.client;
-import forge.net.CustomObjectDecoder;
-import forge.net.CustomObjectEncoder;
+import forge.net.CompatibleObjectDecoder;
+import forge.net.CompatibleObjectEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
@@ -58,8 +58,8 @@ public class FGameClient implements IToServer {
public void initChannel(final SocketChannel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(
- new CustomObjectEncoder(),
- new CustomObjectDecoder(CustomObjectDecoder.maxObjectsize, ClassResolvers.cacheDisabled(null)),
+ new CompatibleObjectEncoder(),
+ new CompatibleObjectDecoder(9766*1024, ClassResolvers.cacheDisabled(null)),
new MessageHandler(),
new LobbyUpdateHandler(),
new GameClientHandler(FGameClient.this));
@@ -86,7 +86,8 @@ public class FGameClient implements IToServer {
}
public void close() {
- channel.close();
+ if (channel != null)
+ channel.close();
}
@Override
diff --git a/forge-gui/src/main/java/forge/net/client/NetGameController.java b/forge-gui/src/main/java/forge/net/client/NetGameController.java
index 826f5901161..85fe12feed5 100644
--- a/forge-gui/src/main/java/forge/net/client/NetGameController.java
+++ b/forge-gui/src/main/java/forge/net/client/NetGameController.java
@@ -40,12 +40,12 @@ public class NetGameController implements IGameController {
@Override
public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) {
- send(ProtocolMethod.selectPlayer, playerView, triggerEvent);
+ send(ProtocolMethod.selectPlayer, playerView, null/*triggerEvent*/); //some platform don't have mousetriggerevent class or it will not allow them to click/tap
}
@Override
public boolean selectCard(final CardView cardView, final List otherCardViewsToSelect, final ITriggerEvent triggerEvent) {
- send(ProtocolMethod.selectCard, cardView, otherCardViewsToSelect, triggerEvent);
+ send(ProtocolMethod.selectCard, cardView, otherCardViewsToSelect, null/*triggerEvent*/); //some platform don't have mousetriggerevent class or it will not allow them to click/tap
// Difference from local games! Always consider a card as successfully selected,
// to avoid blocks where server and client wait for each other to respond.
// Some cost in functionality but a huge gain in stability & speed.
diff --git a/forge-gui/src/main/java/forge/net/server/FServerManager.java b/forge-gui/src/main/java/forge/net/server/FServerManager.java
index beb3887d4c2..8e4942ce6b4 100644
--- a/forge-gui/src/main/java/forge/net/server/FServerManager.java
+++ b/forge-gui/src/main/java/forge/net/server/FServerManager.java
@@ -6,8 +6,8 @@ import forge.interfaces.IGuiGame;
import forge.interfaces.ILobbyListener;
import forge.match.LobbySlot;
import forge.match.LobbySlotType;
-import forge.net.CustomObjectDecoder;
-import forge.net.CustomObjectEncoder;
+import forge.net.CompatibleObjectDecoder;
+import forge.net.CompatibleObjectEncoder;
import forge.net.event.LobbyUpdateEvent;
import forge.net.event.LoginEvent;
import forge.net.event.LogoutEvent;
@@ -99,8 +99,8 @@ public final class FServerManager {
public final void initChannel(final SocketChannel ch) throws Exception {
final ChannelPipeline p = ch.pipeline();
p.addLast(
- new CustomObjectEncoder(),
- new CustomObjectDecoder(CustomObjectDecoder.maxObjectsize, ClassResolvers.cacheDisabled(null)),
+ new CompatibleObjectEncoder(),
+ new CompatibleObjectDecoder(9766*1024, ClassResolvers.cacheDisabled(null)),
new MessageHandler(),
new RegisterClientHandler(),
new LobbyInputHandler(),
@@ -180,6 +180,15 @@ public final class FServerManager {
this.localLobby = lobby;
}
+ public void unsetReady() {
+ if (this.localLobby != null) {
+ if (this.localLobby.getSlot(0) != null) {
+ this.localLobby.getSlot(0).setIsReady(false);
+ updateLobbyState();
+ }
+ }
+ }
+
public boolean isMatchActive() {
return this.localLobby != null && this.localLobby.isMatchActive();
}
diff --git a/forge-gui/src/main/java/forge/net/server/NetGuiGame.java b/forge-gui/src/main/java/forge/net/server/NetGuiGame.java
index 33e6f727b07..c835d079f03 100644
--- a/forge-gui/src/main/java/forge/net/server/NetGuiGame.java
+++ b/forge-gui/src/main/java/forge/net/server/NetGuiGame.java
@@ -41,7 +41,7 @@ public class NetGuiGame extends AbstractGuiGame {
return sender.sendAndWait(method, args);
}
- private void updateGameView() {
+ public void updateGameView() {
send(ProtocolMethod.setGameView, getGameView());
}
@@ -191,7 +191,7 @@ public class NetGuiGame extends AbstractGuiGame {
@Override
public SpellAbilityView getAbilityToPlay(final CardView hostCard, final List abilities, final ITriggerEvent triggerEvent) {
- return sendAndWait(ProtocolMethod.getAbilityToPlay, hostCard, abilities, triggerEvent);
+ return sendAndWait(ProtocolMethod.getAbilityToPlay, hostCard, abilities, null/*triggerEvent*/); //someplatform don't have mousetriggerevent class or it will not allow them to click/tap
}
@Override
diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java
index a8cf3e0ac8d..b65361d9835 100644
--- a/forge-gui/src/main/java/forge/player/HumanPlay.java
+++ b/forge-gui/src/main/java/forge/player/HumanPlay.java
@@ -748,10 +748,10 @@ public class HumanPlay {
if (mc.getAmountOfX() > 0 && !"Count$xPaid".equals(xInCard)) { // announce X will overwrite whatever was in card script
int xPaid = AbilityUtils.calculateAmount(source, "X", ability);
toPay.setXManaCostPaid(xPaid, ability.getParam("XColor"));
- source.setXManaCostPaid(xPaid);
+ ability.setXManaCostPaid(xPaid);
}
- else if (source.getXManaCostPaid() > 0) { //ensure pre-announced X value retained
- toPay.setXManaCostPaid(source.getXManaCostPaid(), ability.getParam("XColor"));
+ else if (ability.getXManaCostPaid() != null) { //ensure pre-announced X value retained
+ toPay.setXManaCostPaid(ability.getXManaCostPaid(), ability.getParam("XColor"));
}
int timesMultikicked = source.getKickerMagnitude();
diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java
index a337db37099..5a863164800 100644
--- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java
+++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java
@@ -105,10 +105,7 @@ public class HumanPlaySpellAbility {
if (ability.isSpell() && !ability.isCastFaceDown() && fromState == CardStateName.FaceDown) {
c.turnFaceUp();
}
- c.setCastSA(ability);
- ability.setLastStateBattlefield(game.getLastStateBattlefield());
- ability.setLastStateGraveyard(game.getLastStateGraveyard());
- ability.setHostCard(game.getAction().moveToStack(c, null));
+ ability.setHostCard(game.getAction().moveToStack(c, ability));
}
if (!ability.isCopied()) {
@@ -317,10 +314,10 @@ public class HumanPlaySpellAbility {
if (value == null) {
return false;
}
- card.setXManaCostPaid(value);
+ ability.setXManaCostPaid(value);
}
} else if (manaCost.getMana().isZero() && ability.isSpell()) {
- card.setXManaCostPaid(0);
+ ability.setXManaCostPaid(0);
}
}
return true;
diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java
index a2a08af75ec..c083f38348a 100644
--- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java
+++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java
@@ -777,7 +777,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
tempShowCards(topN);
if ( FModel.getPreferences().getPrefBoolean(FPref.UI_SELECT_FROM_CARD_DISPLAYS) &&
- (!GuiBase.getInterface().isLibgdxPort()) ) {
+ (!GuiBase.getInterface().isLibgdxPort()) && (!GuiBase.isNetworkplay())) { //prevent crash for desktop vs mobile port it will crash the netplay since mobile doesnt have manipulatecardlist, send the alternate below
CardCollectionView cardList = player.getCardsIn(ZoneType.Library);
ImmutablePair result =
arrangeForMove(localizer.getMessage("lblMoveCardstoToporBbottomofLibrary"), cardList, topN, true, true);
@@ -1181,7 +1181,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
@Override
public Object vote(final SpellAbility sa, final String prompt, final List options,
- final ListMultimap votes) {
+ final ListMultimap votes, Player forPlayer) {
return getGui().one(prompt, options);
}
diff --git a/forge-gui/src/main/java/forge/properties/ForgeConstants.java b/forge-gui/src/main/java/forge/properties/ForgeConstants.java
index 471e6acbbf0..12e9b309a5d 100644
--- a/forge-gui/src/main/java/forge/properties/ForgeConstants.java
+++ b/forge-gui/src/main/java/forge/properties/ForgeConstants.java
@@ -237,6 +237,7 @@ public final class ForgeConstants {
public static final String QUEST_PREFS_FILE = USER_PREFS_DIR + "quest.preferences";
public static final String CONQUEST_PREFS_FILE = USER_PREFS_DIR + "conquest.preferences";
public static final String ITEM_VIEW_PREFS_FILE = USER_PREFS_DIR + "item_view.preferences";
+ public static final String CLOSE_CONN_COMMAND = "<<_EM_ESOLC_<<";
// data that has defaults in the program dir but overrides/additions in the user dir
private static final String _DEFAULTS_DIR = RES_DIR + "defaults" + PATH_SEPARATOR;
diff --git a/forge-gui/src/main/java/forge/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/properties/ForgePreferences.java
index f9b7c3329a4..8ba539ec1ba 100644
--- a/forge-gui/src/main/java/forge/properties/ForgePreferences.java
+++ b/forge-gui/src/main/java/forge/properties/ForgePreferences.java
@@ -139,6 +139,8 @@ public class ForgePreferences extends PreferencesStore {
UI_ENABLE_PRELOAD_EXTENDED_ART("false"),
UI_ENABLE_BORDER_MASKING("false"),
UI_SHOW_FPS("false"),
+ UI_NETPLAY_COMPAT("false"),
+ UI_LOAD_UNKNOWN_CARDS("true"),
UI_ALLOW_ORDER_GRAVEYARD_WHEN_NEEDED ("Never"),
UI_DEFAULT_FONT_SIZE("12"),
UI_SELECT_FROM_CARD_DISPLAYS("true"),
@@ -153,6 +155,7 @@ public class ForgePreferences extends PreferencesStore {
//TODO This should be removed after the update that requires Java 8.
DISABLE_DISPLAY_JAVA_8_UPDATE_WARNING("false"),
+ AUTO_UPDATE("none"),
USE_SENTRY("false"), // this controls whether automated bug reporting is done or not
MATCH_HOT_SEAT_MODE("false"), //this only applies to mobile game
diff --git a/forge-gui/src/main/java/forge/sound/EventVisualizer.java b/forge-gui/src/main/java/forge/sound/EventVisualizer.java
index 69ce7a8fba1..734fd807df2 100644
--- a/forge-gui/src/main/java/forge/sound/EventVisualizer.java
+++ b/forge-gui/src/main/java/forge/sound/EventVisualizer.java
@@ -22,6 +22,7 @@ import forge.game.event.GameEventCardSacrificed;
import forge.game.event.GameEventCardTapped;
import forge.game.event.GameEventFlipCoin;
import forge.game.event.GameEventGameOutcome;
+import forge.game.event.GameEventGameStarted;
import forge.game.event.GameEventLandPlayed;
import forge.game.event.GameEventManaBurn;
import forge.game.event.GameEventPlayerLivesChanged;
@@ -47,6 +48,11 @@ public class EventVisualizer extends IGameEventVisitor.Base imp
this.player = lobbyPlayer;
}
+ @Override
+ public SoundEffectType visit(GameEventGameStarted event) {
+ return SoundEffectType.StartDuel;
+ }
+
@Override
public SoundEffectType visit(final GameEventCardDamaged event) { return SoundEffectType.Damage; }
@Override
diff --git a/forge-gui/src/main/java/forge/sound/SoundEffectType.java b/forge-gui/src/main/java/forge/sound/SoundEffectType.java
index 14ea9684c53..002e93360a0 100644
--- a/forge-gui/src/main/java/forge/sound/SoundEffectType.java
+++ b/forge-gui/src/main/java/forge/sound/SoundEffectType.java
@@ -83,6 +83,7 @@ public enum SoundEffectType {
ScriptedEffect("", false), // Plays the effect defined by SVar:SoundEffect
Shuffle("shuffle.wav", false),
Sorcery("sorcery.wav", false),
+ StartDuel("start_duel.wav",false),
Tap("tap.wav", false),
Token("token.wav", true),
Untap("untap.wav", true),
diff --git a/forge-gui/src/main/java/forge/util/ImageFetcher.java b/forge-gui/src/main/java/forge/util/ImageFetcher.java
index cf219331dc8..ef83a64f414 100644
--- a/forge-gui/src/main/java/forge/util/ImageFetcher.java
+++ b/forge-gui/src/main/java/forge/util/ImageFetcher.java
@@ -6,6 +6,8 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import forge.FThreads;
import forge.ImageKeys;
@@ -45,21 +47,28 @@ public abstract class ImageFetcher {
final String filename = ImageUtil.getImageKey(paperCard, backFace, true);
destFile = new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + filename + ".jpg");
- // First try to download the LQ Set URL, then fetch from scryfall/magiccards.info
+ // First try to download the LQ Set URL, then fetch from scryfall
StringBuilder setDownload = new StringBuilder(ForgeConstants.URL_PIC_DOWNLOAD);
setDownload.append(ImageUtil.getDownloadUrl(paperCard, backFace));
downloadUrls.add(setDownload.toString());
+ int artIndex = 1;
+ final Pattern pattern = Pattern.compile(
+ "^.:([^|]*\\|){2}(\\d+).*$"
+ );
+ Matcher matcher = pattern.matcher(imageKey);
+ if (matcher.matches()) {
+ artIndex = Integer.parseInt(matcher.group(2));
+ }
final StaticData data = StaticData.instance();
- final int cardNum = data.getCommonCards().getCardCollectorNumber(paperCard.getName(), paperCard.getEdition());
- if (cardNum != -1) {
+ final String cardNum = data.getCommonCards().getCardCollectorNumber(paperCard.getName(), paperCard.getEdition(), artIndex);
+ if (cardNum != null) {
String suffix = "";
if (paperCard.getRules().getOtherPart() != null) {
suffix = (backFace ? "b" : "a");
}
final String editionMciCode = data.getEditions().getMciCodeByCode(paperCard.getEdition());
- downloadUrls.add(String.format("https://img.scryfall.com/cards/normal/en/%s/%d%s.jpg", editionMciCode, cardNum, suffix));
- downloadUrls.add(String.format("https://magiccards.info/scans/en/%s/%d%s.jpg", editionMciCode, cardNum, suffix));
+ downloadUrls.add(String.format("https://img.scryfall.com/cards/normal/en/%s/%s%s.jpg", editionMciCode, cardNum, suffix));
}
} else if (prefix.equals(ImageKeys.TOKEN_PREFIX)) {
if (tokenImages == null) {
diff --git a/forge-gui/tools/EditionTracking.py b/forge-gui/tools/EditionTracking.py
index b6cc10d5884..f4d9677ceef 100644
--- a/forge-gui/tools/EditionTracking.py
+++ b/forge-gui/tools/EditionTracking.py
@@ -46,8 +46,11 @@ def initializeEditions():
metadata = True
continue
+ if line.startswith("#"):
+ continue
+
if line:
- hasSetNumbers = line.split(" ", 1)[0].isdigit()
+ hasSetNumbers = line[0].isdigit()
card = line.split(" ", 2 if hasSetNumbers else 1)[-1].rstrip()
if card not in mtgDataCards:
diff --git a/pom.xml b/pom.xml
index 3d50a4419b4..22f1a350b04 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
forgepomForge Parent
- 1.6.33-SNAPSHOT
+ 1.6.34-SNAPSHOT
Forge lets you play the card game Magic: The Gathering against a computer opponent using all of the rules.