Merge remote-tracking branch 'upstream/master'

This commit is contained in:
CCTV-1
2020-03-14 12:18:50 +08:00
43 changed files with 364 additions and 107 deletions

View File

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

View File

@@ -113,7 +113,11 @@ public final class ImageKeys {
} }
//try fullborder... //try fullborder...
if (filename.contains(".full")) { if (filename.contains(".full")) {
file = findFile(dir, TextUtil.fastReplace(filename, ".full", ".fullborder")); String fullborderFile = TextUtil.fastReplace(filename, ".full", ".fullborder");
file = findFile(dir, fullborderFile);
if (file != null) { return file; }
// if there's an art variant try without it
file = findFile(dir, TextUtil.fastReplace(fullborderFile, "1.fullborder", ".fullborder"));
if (file != null) { return file; } if (file != null) { return file; }
} }
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder //if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder

View File

@@ -192,6 +192,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
public boolean getSmallSetOverride() { return smallSetOverride; } public boolean getSmallSetOverride() { return smallSetOverride; }
public String getBoosterMustContain() { return boosterMustContain; } public String getBoosterMustContain() { return boosterMustContain; }
public CardInSet[] getCards() { return cards; } public CardInSet[] getCards() { return cards; }
public boolean isModern() { return getDate().after(parseDate("2003-07-27")); } //8ED and above are modern except some promo cards and others
public Map<String, Integer> getTokens() { return tokenNormalized; } public Map<String, Integer> getTokens() { return tokenNormalized; }

View File

@@ -222,7 +222,12 @@ public final class CardRules implements ICardCharacteristics {
public boolean canBeBrawlCommander() { public boolean canBeBrawlCommander() {
CardType type = mainPart.getType(); CardType type = mainPart.getType();
return (type.isLegendary() && type.isCreature()) || type.isPlaneswalker(); return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
}
public boolean canBeTinyLeadersCommander() {
CardType type = mainPart.getType();
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
} }
public String getMeldWith() { public String getMeldWith() {

View File

@@ -594,8 +594,10 @@ public final class CardRulesPredicates {
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard); public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy); public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land); public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER, public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY)); Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
public static final Predicate<CardRules> CAN_BE_TINY_LEADERS_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
/** The Constant IS_NON_CREATURE_SPELL. **/ /** The Constant IS_NON_CREATURE_SPELL. **/
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = com.google.common.base.Predicates public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = com.google.common.base.Predicates

View File

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

View File

@@ -5,6 +5,8 @@ import forge.item.PaperCard;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ImmutableSortedMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -17,6 +19,22 @@ import java.util.Map.Entry;
*/ */
public class TextUtil { public class TextUtil {
static ImmutableSortedMap<Integer,String> romanMap = ImmutableSortedMap.<Integer,String>naturalOrder()
.put(1000, "M").put(900, "CM")
.put(500, "D").put(400, "CD")
.put(100, "C").put(90, "XC")
.put(50, "L").put(40, "XL")
.put(10, "X").put(9, "IX")
.put(5, "V").put(4, "IV").put(1, "I").build();
public final static String toRoman(int number) {
if (number <= 0) {
return "";
}
int l = romanMap.floorKey(number);
return romanMap.get(l) + toRoman(number-l);
}
/** /**
* Safely converts an object to a String. * Safely converts an object to a String.
* *

View File

@@ -547,6 +547,13 @@ public class GameAction {
c.setCastSA(null); c.setCastSA(null);
} else if (zoneTo.is(ZoneType.Stack)) { } else if (zoneTo.is(ZoneType.Stack)) {
c.setCastFrom(zoneFrom.getZoneType()); c.setCastFrom(zoneFrom.getZoneType());
if (cause != null && cause.isSpell() && c.equals(cause.getHostCard()) && !c.isCopiedSpell()) {
cause.setLastStateBattlefield(game.getLastStateBattlefield());
cause.setLastStateGraveyard(game.getLastStateGraveyard());
c.setCastSA(cause);
} else {
c.setCastSA(null);
}
} else if (!(zoneTo.is(ZoneType.Battlefield) && zoneFrom.is(ZoneType.Stack))) { } else if (!(zoneTo.is(ZoneType.Battlefield) && zoneFrom.is(ZoneType.Stack))) {
c.setCastFrom(null); c.setCastFrom(null);
c.setCastSA(null); c.setCastSA(null);

View File

@@ -22,8 +22,10 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import forge.card.MagicColor;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser; import forge.card.mana.ManaCostParser;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.card.*; import forge.game.card.*;
@@ -32,9 +34,15 @@ import forge.game.cost.Cost;
import forge.game.keyword.KeywordInterface; import forge.game.keyword.KeywordInterface;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerController; import forge.game.player.PlayerController;
import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementHandler;
import forge.game.replacement.ReplacementLayer;
import forge.game.spellability.*; import forge.game.spellability.*;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.Lang;
import forge.util.TextUtil; import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -363,10 +371,11 @@ public final class GameActionUtil {
} }
SpellAbility result = null; SpellAbility result = null;
final Card host = sa.getHostCard(); final Card host = sa.getHostCard();
final Game game = host.getGame();
final Player activator = sa.getActivatingPlayer(); final Player activator = sa.getActivatingPlayer();
final PlayerController pc = activator.getController(); final PlayerController pc = activator.getController();
host.getGame().getAction().checkStaticAbilities(false); game.getAction().checkStaticAbilities(false);
boolean reset = false; boolean reset = false;
@@ -429,7 +438,60 @@ public final class GameActionUtil {
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE); int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
if (v > 0) { if (v > 0) {
host.addReplacementEffect(CardFactoryUtil.makeEtbCounter("etbCounter:P1P1:" + v, host, false));
final Card eff = new Card(game.nextCardId(), game);
eff.setTimestamp(game.getNextTimestamp());
eff.setName(c.getName() + "'s Effect");
eff.addType("Effect");
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
eff.setOwner(activator);
eff.setImageKey(c.getImageKey());
eff.setColor(MagicColor.COLORLESS);
eff.setImmutable(true);
// try to get the SpellAbility from the mana ability
//eff.setEffectSource((SpellAbility)null);
eff.addRemembered(host);
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ P1P1 | ETB$ True | CounterNum$ " + v;
SpellAbility saAb = AbilityFactory.getAbility(abStr, c);
CardFactoryUtil.setupETBReplacementAbility(saAb);
String desc = "It enters the battlefield with ";
desc += Lang.nounWithNumeral(v, CounterType.P1P1.getName() + " counter");
desc += " on it.";
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
re.setLayer(ReplacementLayer.Other);
re.setOverridingAbility(saAb);
eff.addReplacementEffect(re);
// Forgot Trigger
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
saForget.setSubAbility(saExile);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
parsedTrigger.setOverridingAbility(saForget);
eff.addTrigger(parsedTrigger);
eff.updateStateForView();
// TODO: Add targeting to the effect so it knows who it's dealing with
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
game.getAction().moveTo(ZoneType.Command, eff, null);
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
if (result == null) { if (result == null) {
result = sa.copy(); result = sa.copy();
} }

View File

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

View File

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

View File

@@ -882,6 +882,10 @@ public class Combat {
return true; // is blocking something at the moment return true; // is blocking something at the moment
} }
if (!blocker.isLKI()) {
return false;
}
CombatLki lki = lkiCache.get(blocker); CombatLki lki = lkiCache.get(blocker);
return null != lki && !lki.isAttacker; // was blocking something anyway return null != lki && !lki.isAttacker; // was blocking something anyway
} }
@@ -893,6 +897,10 @@ public class Combat {
return true; // is blocking the attacker's band at the moment return true; // is blocking the attacker's band at the moment
} }
if (!blocker.isLKI()) {
return false;
}
CombatLki lki = lkiCache.get(blocker); CombatLki lki = lkiCache.get(blocker);
return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band
} }

View File

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

View File

@@ -19,9 +19,11 @@ package forge.game.spellability;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import forge.card.ColorSet; import forge.card.ColorSet;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.mana.ManaAtom; import forge.card.mana.ManaAtom;
import forge.game.Game;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
@@ -34,6 +36,8 @@ import forge.game.replacement.*;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler; import forge.game.trigger.TriggerHandler;
import forge.game.trigger.TriggerType; import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.Lang;
import forge.util.TextUtil; import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -227,10 +231,26 @@ public class AbilityManaPart implements java.io.Serializable {
/** /**
* createETBCounters * createETBCounters
*/ */
public void createETBCounters(Card c) { public void createETBCounters(Card c, Player controller) {
String[] parse = this.addsCounters.split("_"); String[] parse = this.addsCounters.split("_");
// Convert random SVars if there are other cards with this effect // Convert random SVars if there are other cards with this effect
if (c.isValid(parse[0], c.getController(), c, null)) { if (c.isValid(parse[0], c.getController(), c, null)) {
final Game game = this.sourceCard.getGame();
final Card eff = new Card(game.nextCardId(), game);
eff.setTimestamp(game.getNextTimestamp());
eff.setName(sourceCard.getName() + "'s Effect");
eff.addType("Effect");
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
eff.setOwner(controller);
eff.setImageKey(sourceCard.getImageKey());
eff.setColor(MagicColor.COLORLESS);
eff.setImmutable(true);
// try to get the SpellAbility from the mana ability
//eff.setEffectSource((SpellAbility)null);
eff.addRemembered(c);
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ " + parse[1] String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ " + parse[1]
+ " | ETB$ True | CounterNum$ " + parse[2]; + " | ETB$ True | CounterNum$ " + parse[2];
@@ -240,15 +260,37 @@ public class AbilityManaPart implements java.io.Serializable {
} }
CardFactoryUtil.setupETBReplacementAbility(sa); CardFactoryUtil.setupETBReplacementAbility(sa);
String repeffstr = "Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield " String desc = "It enters the battlefield with ";
+ " | Secondary$ True | Description$ CARDNAME" desc += Lang.nounWithNumeral(parse[2], CounterType.valueOf(parse[1]).getName() + " counter");
+ " enters the battlefield with " + CounterType.valueOf(parse[1]).getName() + " counters."; desc += " on it.";
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, c, false); String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
re.setLayer(ReplacementLayer.Other); re.setLayer(ReplacementLayer.Other);
re.setOverridingAbility(sa); re.setOverridingAbility(sa);
c.addReplacementEffect(re); eff.addReplacementEffect(re);
// Forgot Trigger
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
saForget.setSubAbility(saExile);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
parsedTrigger.setOverridingAbility(saForget);
eff.addTrigger(parsedTrigger);
eff.updateStateForView();
// TODO: Add targeting to the effect so it knows who it's dealing with
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
game.getAction().moveTo(ZoneType.Command, eff, null);
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
} }
} }

View File

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

View File

@@ -6,7 +6,7 @@
<uses-sdk <uses-sdk
android:minSdkVersion="19" android:minSdkVersion="19"
android:targetSdkVersion="21" /> android:targetSdkVersion="26" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- This one needs Android Runtime Permission for Android 6+ --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- This one needs Android Runtime Permission for Android 6+ -->
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>

View File

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

View File

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

View File

@@ -103,7 +103,7 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
case TinyLeaders: case TinyLeaders:
allSections.add(DeckSection.Commander); allSections.add(DeckSection.Commander);
commanderFilter = CardRulesPredicates.Presets.CAN_BE_COMMANDER; commanderFilter = CardRulesPredicates.Presets.CAN_BE_TINY_LEADERS_COMMANDER;
commanderPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(Predicates.compose(commanderFilter, PaperCard.FN_GET_RULES)), PaperCard.class); commanderPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(Predicates.compose(commanderFilter, PaperCard.FN_GET_RULES)), PaperCard.class);
normalPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(), PaperCard.class); normalPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(), PaperCard.class);

View File

@@ -35,6 +35,9 @@ import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static forge.card.CardRenderer.CROP_MULTIPLIER;
import static forge.card.CardRenderer.isModernFrame;
public class CardImageRenderer { public class CardImageRenderer {
private static final float BASE_IMAGE_WIDTH = 360; private static final float BASE_IMAGE_WIDTH = 360;
private static final float BASE_IMAGE_HEIGHT = 504; private static final float BASE_IMAGE_HEIGHT = 504;
@@ -357,13 +360,19 @@ public class CardImageRenderer {
float new_yRotate = (dispH - new_w) /2; float new_yRotate = (dispH - new_w) /2;
boolean rotateSplit = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS); boolean rotateSplit = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS);
boolean rotatePlane = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON); boolean rotatePlane = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON);
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
float minusxy = isModernFrame(card) ? 0.0f : 0.13f*radius;
if (card.getCurrentState().getSetCode().equals("LEA")||card.getCurrentState().getSetCode().equals("LEB")) {
croppedArea = 0.975f;
minusxy = 0.135f*radius;
}
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) { if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) {
if (Forge.enableUIMask){ if (Forge.enableUIMask){
if (ImageCache.isExtendedArt(card)) if (ImageCache.isExtendedArt(card))
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90); g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
else { else {
g.drawRotatedImage(FSkin.getBorders().get(0), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90); g.drawRotatedImage(FSkin.getBorders().get(0), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x+radius/2, new_y+radius/2, new_w*0.96f, new_h*0.96f, (new_x+radius/2) + (new_w*0.96f) / 2, (new_y+radius/2) + (new_h*0.96f) / 2, -90); g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x+radius/2-minusxy, new_y+radius/2-minusxy, new_w*croppedArea, new_h*croppedArea, (new_x+radius/2-minusxy) + (new_w*croppedArea) / 2, (new_y+radius/2-minusxy) + (new_h*croppedArea) / 2, -90);
} }
} else } else
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90); g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
@@ -374,7 +383,7 @@ public class CardImageRenderer {
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90); g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
else { else {
g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90); g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x + radius / 2, new_y + radius / 2, new_w * 0.96f, new_h * 0.96f, (new_x + radius / 2) + (new_w * 0.96f) / 2, (new_y + radius / 2) + (new_h * 0.96f) / 2, isAftermath ? 90 : -90); g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x + radius / 2-minusxy, new_y + radius / 2-minusxy, new_w * croppedArea, new_h * croppedArea, (new_x + radius / 2-minusxy) + (new_w * croppedArea) / 2, (new_y + radius / 2-minusxy) + (new_h * croppedArea) / 2, isAftermath ? 90 : -90);
} }
} else } else
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90); g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
@@ -384,7 +393,7 @@ public class CardImageRenderer {
g.drawImage(image, x, y, w, h); g.drawImage(image, x, y, w, h);
else { else {
g.drawImage(ImageCache.getBorderImage(card, canshow), x, y, w, h); g.drawImage(ImageCache.getBorderImage(card, canshow), x, y, w, h);
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f); g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
} }
} else { } else {
if (canshow && !ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE).equals(card.getState(altState).getImageKey())) if (canshow && !ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE).equals(card.getState(altState).getImageKey()))

View File

@@ -105,6 +105,7 @@ public class CardRenderer {
private static final float NAME_COST_THRESHOLD = Utils.scale(200); private static final float NAME_COST_THRESHOLD = Utils.scale(200);
private static final float BORDER_THICKNESS = Utils.scale(1); private static final float BORDER_THICKNESS = Utils.scale(1);
public static final float PADDING_MULTIPLIER = 0.021f; public static final float PADDING_MULTIPLIER = 0.021f;
public static final float CROP_MULTIPLIER = 0.96f;
private static Map<Integer, BitmapFont> counterFonts = new HashMap<>(); private static Map<Integer, BitmapFont> counterFonts = new HashMap<>();
private static final Color counterBackgroundColor = new Color(0f, 0f, 0f, 0.9f); private static final Color counterBackgroundColor = new Color(0f, 0f, 0f, 0.9f);
@@ -142,6 +143,49 @@ public class CardRenderer {
} }
} }
public static boolean isModernFrame(IPaperCard c) {
if (c == null)
return false;
CardEdition ed = FModel.getMagicDb().getEditions().get(c.getEdition());
if (ed != null) {
switch (ed.getCode()) {
case "MED":
case "ME2":
case "ME3":
case "ME4":
case "TSB":
return false;
default:
return ed.isModern();
}
}
return false;
}
public static boolean isModernFrame(CardView c) {
if (c == null)
return false;
CardView.CardStateView state = c.getCurrentState();
CardEdition ed = FModel.getMagicDb().getEditions().get(state.getSetCode());
if (ed != null) {
switch (ed.getCode()) {
case "MED":
case "ME2":
case "ME3":
case "ME4":
case "TSB":
return false;
default:
return ed.isModern();
}
}
return false;
}
public static float getCardListItemHeight(boolean compactMode) { public static float getCardListItemHeight(boolean compactMode) {
if (compactMode) { if (compactMode) {
return MANA_SYMBOL_SIZE + 2 * FList.PADDING; return MANA_SYMBOL_SIZE + 2 * FList.PADDING;
@@ -402,7 +446,12 @@ public class CardRenderer {
public static void drawCard(Graphics g, IPaperCard pc, float x, float y, float w, float h, CardStackPosition pos) { public static void drawCard(Graphics g, IPaperCard pc, float x, float y, float w, float h, CardStackPosition pos) {
Texture image = new RendererCachedCardImage(pc, false).getImage(); Texture image = new RendererCachedCardImage(pc, false).getImage();
float radius = (h - w)/8; float radius = (h - w)/8;
float croppedArea = isModernFrame(pc) ? CROP_MULTIPLIER : 0.97f;
float minusxy = isModernFrame(pc) ? 0.0f : 0.13f*radius;
if (pc.getEdition().equals("LEA")||pc.getEdition().equals("LEB")) {
croppedArea = 0.975f;
minusxy = 0.135f*radius;
}
if (image != null) { if (image != null) {
if (image == ImageCache.defaultImage) { if (image == ImageCache.defaultImage) {
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos); CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
@@ -413,7 +462,7 @@ public class CardRenderer {
g.drawImage(image, x, y, w, h); g.drawImage(image, x, y, w, h);
else { else {
g.drawImage(ImageCache.getBorderImage(pc), x, y, w, h); g.drawImage(ImageCache.getBorderImage(pc), x, y, w, h);
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f); g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
} }
} else } else
g.drawImage(image, x, y, w, h); g.drawImage(image, x, y, w, h);
@@ -437,7 +486,12 @@ public class CardRenderer {
Texture image = new RendererCachedCardImage(card, false).getImage(); Texture image = new RendererCachedCardImage(card, false).getImage();
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner()); FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
float radius = (h - w)/8; float radius = (h - w)/8;
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
float minusxy = isModernFrame(card) ? 0.0f : 0.13f*radius;
if (card.getCurrentState().getSetCode().equals("LEA")||card.getCurrentState().getSetCode().equals("LEB")) {
croppedArea = 0.975f;
minusxy = 0.135f*radius;
}
if (image != null) { if (image != null) {
if (image == ImageCache.defaultImage) { if (image == ImageCache.defaultImage) {
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos); CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
@@ -450,7 +504,7 @@ public class CardRenderer {
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90); g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
else { else {
g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90); g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90);
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), x+radius/2.3f, y+radius/2, w*0.96f, h*0.96f, (x+radius/2.3f) + (w*0.96f) / 2, (y+radius/2) + (h*0.96f) / 2, -90); g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), x+radius/2.3f-minusxy, y+radius/2-minusxy, w*croppedArea, h*croppedArea, (x+radius/2.3f-minusxy) + (w*croppedArea) / 2, (y+radius/2-minusxy) + (h*croppedArea) / 2, -90);
} }
} else } else
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90); g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
@@ -461,7 +515,7 @@ public class CardRenderer {
else { else {
boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors(); boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
g.drawBorderImage(ImageCache.getBorderImage(card, canshow), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors g.drawBorderImage(ImageCache.getBorderImage(card, canshow), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f); g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
} }
} else { } else {
if (canshow) if (canshow)
@@ -1119,8 +1173,14 @@ public class CardRenderer {
public static void drawFoilEffect(Graphics g, CardView card, float x, float y, float w, float h, boolean inZoomer) { public static void drawFoilEffect(Graphics g, CardView card, float x, float y, float w, float h, boolean inZoomer) {
float new_x = x; float new_y = y; float new_w = w; float new_h = h; float radius = (h - w)/8; float new_x = x; float new_y = y; float new_w = w; float new_h = h; float radius = (h - w)/8;
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
float minusxy = isModernFrame(card) ? 0.0f : 0.13f*radius;
if (card.getCurrentState().getSetCode().equals("LEA")||card.getCurrentState().getSetCode().equals("LEB")) {
croppedArea = 0.975f;
minusxy = 0.135f*radius;
}
if (Forge.enableUIMask) { if (Forge.enableUIMask) {
new_x += radius/2.4f; new_y += radius/2; new_w = w * 0.96f; new_h = h * 0.96f; new_x += radius/2.4f-minusxy; new_y += radius/2-minusxy; new_w = w * croppedArea; new_h = h * croppedArea;
} }
if (isPreferenceEnabled(FPref.UI_OVERLAY_FOIL_EFFECT) && MatchController.instance.mayView(card)) { if (isPreferenceEnabled(FPref.UI_OVERLAY_FOIL_EFFECT) && MatchController.instance.mayView(card)) {
boolean rotateSplit = isPreferenceEnabled(FPref.UI_ROTATE_SPLIT_CARDS) && card.isSplitCard() && inZoomer; boolean rotateSplit = isPreferenceEnabled(FPref.UI_ROTATE_SPLIT_CARDS) && card.isSplitCard() && inZoomer;

View File

@@ -219,7 +219,22 @@ public class CardZoom extends FOverlay {
float w = getWidth(); float w = getWidth();
float h = getHeight(); float h = getHeight();
float messageHeight = FDialog.MSG_HEIGHT; float messageHeight = FDialog.MSG_HEIGHT;
float maxCardHeight = h - 2 * messageHeight; float AspectRatioMultiplier = 2;
switch (Forge.extrawide) {
case "default":
AspectRatioMultiplier = 3; //good for tablets with 16:10 or similar
break;
case "wide":
AspectRatioMultiplier = 2.5f;
break;
case "extrawide":
AspectRatioMultiplier = 2; //good for tall phones with 21:9 or similar
break;
default:
AspectRatioMultiplier = 3;
break;
}
float maxCardHeight = h - AspectRatioMultiplier * messageHeight; //maxheight of currently zoomed card
float cardWidth, cardHeight, y; float cardWidth, cardHeight, y;

View File

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

View File

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

View File

@@ -4,8 +4,9 @@ Types:Creature Elemental Knight
PT:4/6 PT:4/6
K:Vigilance K:Vigilance
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroy | TriggerDescription$ When CARDNAME enters the battlefield, destroy up to one target nonland permanent. Its controller creates a 3/3 colorless Golem artifact creature token. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroy | TriggerDescription$ When CARDNAME enters the battlefield, destroy up to one target nonland permanent. Its controller creates a 3/3 colorless Golem artifact creature token.
SVar:TrigDestroy:DB$ Destroy | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | SubAbility$ DBToken SVar:TrigDestroy:DB$ Destroy | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | RememberLKI$ True | SubAbility$ DBToken
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_3_3_a_golem | TokenOwner$ TargetedController | LegacyImage$ c 3 3 a golem m20 SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_3_3_a_golem | TokenOwner$ RememberedController | LegacyImage$ c 3 3 a golem m20 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, return target artifact or enchantment card from your graveyard to your hand. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, return target artifact or enchantment card from your graveyard to your hand.
SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Artifact.YouCtrl,Enchantment.YouCtrl SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Artifact.YouCtrl,Enchantment.YouCtrl
Oracle:Vigilance\nWhen Cavalier of Dawn enters the battlefield, destroy up to one target nonland permanent. Its controller creates a 3/3 colorless Golem artifact creature token.\nWhen Cavalier of Dawn dies, return target artifact or enchantment card from your graveyard to your hand. Oracle:Vigilance\nWhen Cavalier of Dawn enters the battlefield, destroy up to one target nonland permanent. Its controller creates a 3/3 colorless Golem artifact creature token.\nWhen Cavalier of Dawn dies, return target artifact or enchantment card from your graveyard to your hand.

View File

@@ -1,7 +1,7 @@
Name:Ghastly Demise Name:Ghastly Demise
ManaCost:B ManaCost:B
Types:Instant Types:Instant
A:SP$ Destroy | Cost$ B | ValidTgts$ Creature.nonBlack+toughnessLEX | TgtPrompt$ Select target nonblack creature with toughness less than or equal to the number of cards in your graveyard. | References$ X | SpellDescription$ Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard. A:SP$ Destroy | Cost$ B | ValidTgts$ Creature.nonBlack | TgtPrompt$ Select target nonblack creature | ConditionCheckSVar$ Y | ConditionSVarCompare$ LEX | References$ X,Y | StackDescription$ SpellDescription | SpellDescription$ Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard.
SVar:Y:Targeted$CardToughness
SVar:X:Count$InYourYard SVar:X:Count$InYourYard
SVar:Picture:http://www.wizards.com/global/images/magic/general/ghastly_demise.jpg
Oracle:Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard. Oracle:Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard.

View File

@@ -1,9 +1,10 @@
Name:Nissa's Pilgrimage Name:Nissa's Pilgrimage
ManaCost:2 G ManaCost:2 G
Types:Sorcery Types:Sorcery
A:SP$ ChangeZone | Cost$ 2 G | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic+Forest | ChangeNum$ 1 | SubAbility$ DBChangeZone1 | NoShuffle$ True | SpellDescription$ Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library. Spell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two. A:SP$ ChangeZone | Cost$ 2 G | Origin$ Library | Destination$ Library | ChangeType$ Land.Basic+Forest | ChangeNum$ X | References$ X,Y | RememberChanged$ True | SubAbility$ DBBattlefield | Shuffle$ False | StackDescription$ SpellDescription | SpellDescription$ Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library. Spell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two.
SVar:DBChangeZone1:DB$ChangeZone | Origin$ Library | Destination$ Hand | SubAbility$ DBChangeZone2 | ChangeType$ Land.Basic+Forest | ChangeNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ LT2 | References$ X SVar:DBBattlefield:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | Tapped$ True | SubAbility$ DBHand | ChangeType$ Card.IsRemembered | ChangeNum$ 1 | Mandatory$ True | NoLooking$ True | SelectPrompt$ Select a card to go to the battlefield | Shuffle$ False | StackDescription$ None
SVar:DBChangeZone2:DB$ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Basic+Forest | ChangeNum$ 2 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE2 | References$ X SVar:DBHand:DB$ ChangeZone | Origin$ Library | Destination$ Hand | Defined$ Remembered | NoLooking$ True | StackDescription$ None | SubAbility$ DBCleanup
SVar:X:Count$ValidGraveyard Instant.YouOwn,Sorcery.YouOwn SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/nissas_pilgrimage.jpg SVar:X:Count$Compare Y GE2.3.2
SVar:Y:Count$ValidGraveyard Instant.YouOwn,Sorcery.YouOwn
Oracle:Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library.\nSpell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two. Oracle:Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library.\nSpell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two.

View File

@@ -2,7 +2,7 @@ Name:Settle the Wreckage
ManaCost:2 W W ManaCost:2 W W
Types:Instant Types:Instant
A:SP$ ChangeZoneAll | Cost$ 2 W W | ValidTgts$ Player | ChangeType$ Creature.attacking | TgtPrompt$ Select target player | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBGetLands | SpellDescription$ Exile all attacking creatures target player controls. That player may search their library for that many basic lands, put those cards onto the battlefield tapped, then shuffle their library. A:SP$ ChangeZoneAll | Cost$ 2 W W | ValidTgts$ Player | ChangeType$ Creature.attacking | TgtPrompt$ Select target player | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBGetLands | SpellDescription$ Exile all attacking creatures target player controls. That player may search their library for that many basic lands, put those cards onto the battlefield tapped, then shuffle their library.
SVar:DBGetLands:DB$ ChangeZone | Optional$ True | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ X | References$ X | DefinedPlayer$ RememberedController | ShuffleNonMandatory$ True | SubAbility$ DBCleanup SVar:DBGetLands:DB$ ChangeZone | Optional$ True | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ X | References$ X | DefinedPlayer$ TargetedPlayer | ShuffleNonMandatory$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:Count$RememberedSize SVar:X:Count$RememberedSize
SVar:Picture:http://www.wizards.com/global/images/magic/general/settle_the_wreckage.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/settle_the_wreckage.jpg

View File

@@ -3,7 +3,7 @@ ManaCost:1 W
Types:Enchantment Saga Types:Enchantment Saga
K:Saga:3:TrigChange,TrigToken,TrigGainLife K:Saga:3:TrigChange,TrigToken,TrigGainLife
SVar:TrigChange:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Plains+Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library. SVar:TrigChange:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Plains+Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_0_4_wall_defender | TokenOwner$ You | LegacyImage$ c 0 4 wall defender thb | SpellDescription$ Create a 0/4 colorless Wall artifact creature token with defender. SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_0_4_a_wall_defender | TokenOwner$ You | LegacyImage$ c 0 4 wall defender thb | SpellDescription$ Create a 0/4 colorless Wall artifact creature token with defender.
SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 | SpellDescription$ You gain 2 life. SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 | SpellDescription$ You gain 2 life.
DeckHas:Ability$LifeGain & Ability$Token DeckHas:Ability$LifeGain & Ability$Token
Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI - Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library.\nII - Create a 0/4 colorless Wall artifact creature token with defender.\nIII - You gain 2 life. Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI - Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library.\nII - Create a 0/4 colorless Wall artifact creature token with defender.\nIII - You gain 2 life.

View File

@@ -1,7 +1,7 @@
Name:The Triumph of Anax Name:The Triumph of Anax
ManaCost:2 R ManaCost:2 R
Types:Enchantment Saga Types:Enchantment Saga
K:Saga:3:DBPump,DBPump,DBPump,DBPick K:Saga:4:DBPump,DBPump,DBPump,DBPick
SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +X | References$ X | KW$ Trample | SpellDescription$ Until end of turn, target creature gains trample and gets +X/+0, where X is the number of lore counters on CARDNAME. SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +X | References$ X | KW$ Trample | SpellDescription$ Until end of turn, target creature gains trample and gets +X/+0, where X is the number of lore counters on CARDNAME.
SVar:X:Count$CardCounters.LORE SVar:X:Count$CardCounters.LORE
SVar:PlayMain1:TRUE SVar:PlayMain1:TRUE

View File

@@ -377,7 +377,7 @@ Prerelease=6 Boosters, 1 RareMythic+
[tokens] [tokens]
b_2_2_zombie b_2_2_zombie
c_0_4_wall_defender c_0_4_a_wall_defender
g_1_2_spider_reach g_1_2_spider_reach
g_2_2_wolf g_2_2_wolf
r_x_1_elemental_trample_haste r_x_1_elemental_trample_haste

View File

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

View File

@@ -3,4 +3,4 @@ Name:Legacy
Order:105 Order:105
Subtype:Legacy Subtype:Legacy
Type:Sanctioned Type:Sanctioned
Banned:Adriana's Valor; Advantageous Proclamation; Assemble the Rank and Vile; Backup Plan; Brago's Favor; Deathrite Shaman; Double Stroke; Echoing Boon; Emissary's Ploy; Gitaxian Probe; Hired Heist; Hold the Perimeter; Hymn of the Wilds; Immediate Action; Incendiary Dissent; Iterative Analysis; Muzzio's Preparations; Natural Unity; Power Play; Secret Summoning; Secrets of Paradise; Sentinel Dispatch; Sovereign's Realm; Summoner's Bond; Unexpected Potential; Weight Advantage; Worldknit; Amulet of Quoz; Bronze Tablet; Contract from Below; Darkpact; Demonic Attorney; Jeweled Bird; Rebirth; Tempest Efreet; Timmerian Fiends; Ancestral Recall; Balance; Bazaar of Baghdad; Black Lotus; Channel; Chaos Orb; Demonic Consultation; Demonic Tutor; Dig Through Time; Earthcraft; Falling Star; Fastbond; Flash; Frantic Search; Goblin Recruiter; Gush; Hermit Druid; Imperial Seal; Library of Alexandria; Mana Crypt; Mana Drain; Mana Vault; Memory Jar; Mental Misstep; Mind Twist; Mind's Desire; Mishra's Workshop; Mox Emerald; Mox Jet; Mox Pearl; Mox Ruby; Mox Sapphire; Mystical Tutor; Necropotence; Oath of Druids; Sensei's Divining Top; Shahrazad; Skullclamp; Sol Ring; Strip Mine; Survival of the Fittest; Time Vault; Time Walk; Timetwister; Tinker; Tolarian Academy; Treasure Cruise; Vampiric Tutor; Wheel of Fortune; Windfall; Wrenn and Six; Yawgmoth's Bargain; Yawgmoth's Will Banned:Adriana's Valor; Advantageous Proclamation; Assemble the Rank and Vile; Backup Plan; Brago's Favor; Deathrite Shaman; Double Stroke; Echoing Boon; Emissary's Ploy; Gitaxian Probe; Hired Heist; Hold the Perimeter; Hymn of the Wilds; Immediate Action; Incendiary Dissent; Iterative Analysis; Muzzio's Preparations; Natural Unity; Power Play; Secret Summoning; Secrets of Paradise; Sentinel Dispatch; Sovereign's Realm; Summoner's Bond; Underworld Breach; Unexpected Potential; Weight Advantage; Worldknit; Amulet of Quoz; Bronze Tablet; Contract from Below; Darkpact; Demonic Attorney; Jeweled Bird; Rebirth; Tempest Efreet; Timmerian Fiends; Ancestral Recall; Balance; Bazaar of Baghdad; Black Lotus; Channel; Chaos Orb; Demonic Consultation; Demonic Tutor; Dig Through Time; Earthcraft; Falling Star; Fastbond; Flash; Frantic Search; Goblin Recruiter; Gush; Hermit Druid; Imperial Seal; Library of Alexandria; Mana Crypt; Mana Drain; Mana Vault; Memory Jar; Mental Misstep; Mind Twist; Mind's Desire; Mishra's Workshop; Mox Emerald; Mox Jet; Mox Pearl; Mox Ruby; Mox Sapphire; Mystical Tutor; Necropotence; Oath of Druids; Sensei's Divining Top; Shahrazad; Skullclamp; Sol Ring; Strip Mine; Survival of the Fittest; Time Vault; Time Walk; Timetwister; Tinker; Tolarian Academy; Treasure Cruise; Vampiric Tutor; Wheel of Fortune; Windfall; Wrenn and Six; Yawgmoth's Bargain; Yawgmoth's Will

View File

@@ -4,4 +4,4 @@ Order:103
Subtype:Modern Subtype:Modern
Type:Sanctioned Type:Sanctioned
Sets:8ED, MRD, DST, 5DN, CHK, BOK, SOK, 9ED, RAV, GPT, DIS, CSP, TSP, TSB, PLC, FUT, 10E, LRW, EVE, SHM, MOR, ALA, CFX, ARB, M10, ZEN, WWK, ROE, M11, SOM, MBS, NPH, M12, ISD, DKA, AVR, M13, RTR, GTC, DGM, M14, THS, BNG, JOU, M15, KTK, FRF, DTK, MM2, ORI, BFZ, OGW, SOI, EMN, KLD, AER, AKH, W17, HOU, XLN, RIX, DOM, M19, G18, GRN, RNA, WAR, MH1, M20, ELD, THB Sets:8ED, MRD, DST, 5DN, CHK, BOK, SOK, 9ED, RAV, GPT, DIS, CSP, TSP, TSB, PLC, FUT, 10E, LRW, EVE, SHM, MOR, ALA, CFX, ARB, M10, ZEN, WWK, ROE, M11, SOM, MBS, NPH, M12, ISD, DKA, AVR, M13, RTR, GTC, DGM, M14, THS, BNG, JOU, M15, KTK, FRF, DTK, MM2, ORI, BFZ, OGW, SOI, EMN, KLD, AER, AKH, W17, HOU, XLN, RIX, DOM, M19, G18, GRN, RNA, WAR, MH1, M20, ELD, THB
Banned:Ancient Den; Birthing Pod; Blazing Shoal; Bridge from Below; Chrome Mox; Cloudpost; Dark Depths; Deathrite Shaman; Dig Through Time; Dread Return; Eye of Ugin; Faithless Looting; Gitaxian Probe; Glimpse of Nature; Golgari Grave-Troll; Great Furnace; Green Sun's Zenith; Hogaak, Arisen Necropolis; Hypergenesis; Krark-Clan Ironworks; Mental Misstep; Mox Opal; Mycosynth Lattice; Oko, Thief of Crowns; Ponder; Preordain; Punishing Fire; Rite of Flame; Seat of the Synod; Second Sunrise; Seething Song; Sensei's Divining Top; Skullclamp; Splinter Twin; Summer Bloom; Treasure Cruise; Tree of Tales; Umezawa's Jitte; Vault of Whispers Banned:Ancient Den; Birthing Pod; Blazing Shoal; Bridge from Below; Chrome Mox; Cloudpost; Dark Depths; Deathrite Shaman; Dig Through Time; Dread Return; Eye of Ugin; Faithless Looting; Gitaxian Probe; Glimpse of Nature; Golgari Grave-Troll; Great Furnace; Green Sun's Zenith; Hogaak, Arisen Necropolis; Hypergenesis; Krark-Clan Ironworks; Mental Misstep; Mox Opal; Mycosynth Lattice; Oko, Thief of Crowns; Once Upon A Time; Ponder; Preordain; Punishing Fire; Rite of Flame; Seat of the Synod; Second Sunrise; Seething Song; Sensei's Divining Top; Skullclamp; Splinter Twin; Summer Bloom; Treasure Cruise; Tree of Tales; Umezawa's Jitte; Vault of Whispers

View File

@@ -52,8 +52,8 @@ btnResetJavaFutureCompatibilityWarnings=Restablecer las advertencias de compatib
btnClearImageCache=Limpiar Caché de Imágenes btnClearImageCache=Limpiar Caché de Imágenes
btnTokenPreviewer=Previsualizador de Fichas (Token) btnTokenPreviewer=Previsualizador de Fichas (Token)
btnCopyToClipboard=Copiar al portapapeles btnCopyToClipboard=Copiar al portapapeles
cbpAutoUpdater=Auto updater cbpAutoUpdater=Actualizar Forge
nlAutoUpdater=Select the release channel to use for updating Forge nlAutoUpdater=Selecciona la versión a utilizar para actualizar Forge
cbpSelectLanguage=Idioma cbpSelectLanguage=Idioma
nlSelectLanguage=Seleccionar idioma (excepto partida). Todavía un trabajo en progreso) (Es necesario reiniciar Forge) nlSelectLanguage=Seleccionar idioma (excepto partida). Todavía un trabajo en progreso) (Es necesario reiniciar Forge)
cbRemoveSmall=Eliminar Pequeñas Criaturas cbRemoveSmall=Eliminar Pequeñas Criaturas
@@ -184,7 +184,7 @@ KeyboardShortcuts=Atajos de teclado
#VSubmenuAchievements.java #VSubmenuAchievements.java
lblAchievements=Logros lblAchievements=Logros
#VSubmenuDownloaders.java #VSubmenuDownloaders.java
btnCheckForUpdates=Check for Updates btnCheckForUpdates=Comprobar Actualizaciones
btnDownloadSetPics=Descargar todas las Ediciones de Cartas btnDownloadSetPics=Descargar todas las Ediciones de Cartas
btnDownloadPics=Descargar todas las Cartas btnDownloadPics=Descargar todas las Cartas
btnDownloadPicsHQ=Descargar todas las Cartas en calidad alta (Muy lento!) btnDownloadPicsHQ=Descargar todas las Cartas en calidad alta (Muy lento!)
@@ -197,7 +197,7 @@ btnImportPictures=Importar Datos
btnHowToPlay=Cómo jugar (Inglés) btnHowToPlay=Cómo jugar (Inglés)
btnDownloadPrices=Descargar los precios de las cartas btnDownloadPrices=Descargar los precios de las cartas
btnLicensing=Detalles de la licencia btnLicensing=Detalles de la licencia
lblCheckForUpdates=Check Forge server to see if there''s a more recent release lblCheckForUpdates=Comprueba si en el servidor de Forge existe alguna versión más reciente
lblDownloadPics=Descargar la imagen de la carta por defecto para cada carta. lblDownloadPics=Descargar la imagen de la carta por defecto para cada carta.
lblDownloadPicsHQ=Descargar la imagen en calidad alta de la carta por defecto para cada carta. lblDownloadPicsHQ=Descargar la imagen en calidad alta de la carta por defecto para cada carta.
lblDownloadSetPics=Descargue todas las imágenes de cada carta (una por cada edición donde apareció la carta) lblDownloadSetPics=Descargue todas las imágenes de cada carta (una por cada edición donde apareció la carta)

View File

@@ -52,6 +52,8 @@ btnResetJavaFutureCompatibilityWarnings=重置Java兼容性警告
btnClearImageCache=清除图片缓存 btnClearImageCache=清除图片缓存
btnTokenPreviewer=衍生物预览器 btnTokenPreviewer=衍生物预览器
btnCopyToClipboard=复制到剪切板 btnCopyToClipboard=复制到剪切板
cbpAutoUpdater=Auto updater
nlAutoUpdater=Select the release channel to use for updating Forge
cbpSelectLanguage=语言 cbpSelectLanguage=语言
cbpAutoUpdater=自动更新 cbpAutoUpdater=自动更新
nlAutoUpdater=选择用于更新Forge的发布渠道 nlAutoUpdater=选择用于更新Forge的发布渠道
@@ -197,7 +199,7 @@ btnImportPictures=导入数据
btnHowToPlay=如何玩 btnHowToPlay=如何玩
btnDownloadPrices=下载卡牌价格 btnDownloadPrices=下载卡牌价格
btnLicensing=许可证详情 btnLicensing=许可证详情
lblCheckForUpdates=检查Forge服务器查看是否有新的版本 lblCheckForUpdates=查看是否有新的版本
lblDownloadPics=下载缺省牌的图片 lblDownloadPics=下载缺省牌的图片
lblDownloadPicsHQ=下载缺省牌的高清图片 lblDownloadPicsHQ=下载缺省牌的高清图片
lblDownloadSetPics=下载每张牌的图片(每张牌出现一次) lblDownloadSetPics=下载每张牌的图片(每张牌出现一次)

View File

@@ -12,6 +12,5 @@ turn=1
activeplayer=ai activeplayer=ai
activephase=MAIN1 activephase=MAIN1
humanhand=Lazotep Plating;Slaying Fire;So Tiny;Shock;Gideon's Triumph;Aspect of Manticore humanhand=Lazotep Plating;Slaying Fire;So Tiny;Shock;Gideon's Triumph;Aspect of Manticore
humanbattlefield=The Akroan War|Counters:LORE=2|Id:2;Blood Aspirant;Flux Channeler;Naiad of Hidden Coves;Temple of Enlightenment|NoETBTrigs;Temple of Enlightenment|NoETBTrigs;Sacred Foundry|NoETBTrigs;Sacred Foundry|NoETBTrigs 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 aibattlefield=Underworld Dreams;Underworld Dreams;Underworld Dreams;Ferocity of the Wilds;Goblin Assault Team;Temple Thief;Mire Triton;Dreadhorde Butcher|Id:1
humanprecast=2:DBGainControl->1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

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

View File

@@ -105,10 +105,7 @@ public class HumanPlaySpellAbility {
if (ability.isSpell() && !ability.isCastFaceDown() && fromState == CardStateName.FaceDown) { if (ability.isSpell() && !ability.isCastFaceDown() && fromState == CardStateName.FaceDown) {
c.turnFaceUp(); c.turnFaceUp();
} }
c.setCastSA(ability); ability.setHostCard(game.getAction().moveToStack(c, ability));
ability.setLastStateBattlefield(game.getLastStateBattlefield());
ability.setLastStateGraveyard(game.getLastStateGraveyard());
ability.setHostCard(game.getAction().moveToStack(c, null));
} }
if (!ability.isCopied()) { if (!ability.isCopied()) {

View File

@@ -6,6 +6,8 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import forge.FThreads; import forge.FThreads;
import forge.ImageKeys; import forge.ImageKeys;
@@ -50,7 +52,14 @@ public abstract class ImageFetcher {
setDownload.append(ImageUtil.getDownloadUrl(paperCard, backFace)); setDownload.append(ImageUtil.getDownloadUrl(paperCard, backFace));
downloadUrls.add(setDownload.toString()); downloadUrls.add(setDownload.toString());
int artIndex = Integer.parseInt(imageKey.split("\\|")[2]); 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 StaticData data = StaticData.instance();
final String cardNum = data.getCommonCards().getCardCollectorNumber(paperCard.getName(), paperCard.getEdition(), artIndex); final String cardNum = data.getCommonCards().getCardCollectorNumber(paperCard.getName(), paperCard.getEdition(), artIndex);
if (cardNum != null) { if (cardNum != null) {