mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
@@ -37,6 +37,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public final class CardDb implements ICardDatabase, IDeckGenPool {
|
public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||||
public final static String foilSuffix = "+";
|
public final static String foilSuffix = "+";
|
||||||
@@ -272,7 +273,22 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
artIds.put(key, artIdx);
|
artIds.put(key, artIdx);
|
||||||
addCard(new PaperCard(cr, e.getCode(), cis.rarity, artIdx, false, cis.collectorNumber, cis.artistName));
|
addCard(new PaperCard(cr, e.getCode(), cis.rarity, artIdx, false, cis.collectorNumber, cis.artistName, cis.functionalVariantName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean addFromSetByName(String cardName, CardEdition ed, CardRules cr) {
|
||||||
|
List<CardInSet> cardsInSet = ed.getCardInSet(cardName); // empty collection if not present
|
||||||
|
if (cr.hasFunctionalVariants()) {
|
||||||
|
cardsInSet = cardsInSet.stream().filter(c -> StringUtils.isEmpty(c.functionalVariantName)
|
||||||
|
|| cr.getSupportedFunctionalVariants().contains(c.functionalVariantName)
|
||||||
|
).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
if (cardsInSet.isEmpty())
|
||||||
|
return false;
|
||||||
|
for (CardInSet cis : cardsInSet) {
|
||||||
|
addSetCard(ed, cis, cr);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadCard(String cardName, String setCode, CardRules cr) {
|
public void loadCard(String cardName, String setCode, CardRules cr) {
|
||||||
@@ -285,18 +301,10 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
if (ed == null || ed.equals(CardEdition.UNKNOWN)) {
|
if (ed == null || ed.equals(CardEdition.UNKNOWN)) {
|
||||||
// look for all possible editions
|
// look for all possible editions
|
||||||
for (CardEdition e : editions) {
|
for (CardEdition e : editions) {
|
||||||
List<CardInSet> cardsInSet = e.getCardInSet(cardName); // empty collection if not present
|
reIndexNecessary |= addFromSetByName(cardName, e, cr);
|
||||||
for (CardInSet cis : cardsInSet) {
|
|
||||||
addSetCard(e, cis, cr);
|
|
||||||
reIndexNecessary = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
List<CardInSet> cardsInSet = ed.getCardInSet(cardName); // empty collection if not present
|
reIndexNecessary |= addFromSetByName(cardName, ed, cr);
|
||||||
for (CardInSet cis : cardsInSet) {
|
|
||||||
addSetCard(ed, cis, cr);
|
|
||||||
reIndexNecessary = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reIndexNecessary)
|
if (reIndexNecessary)
|
||||||
@@ -324,11 +332,20 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
|
|
||||||
for (CardEdition.CardInSet cis : e.getAllCardsInSet()) {
|
for (CardEdition.CardInSet cis : e.getAllCardsInSet()) {
|
||||||
CardRules cr = rulesByName.get(cis.name);
|
CardRules cr = rulesByName.get(cis.name);
|
||||||
if (cr != null) {
|
if (cr == null) {
|
||||||
addSetCard(e, cis, cr);
|
|
||||||
} else {
|
|
||||||
missingCards.add(cis.name);
|
missingCards.add(cis.name);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
if (cr.hasFunctionalVariants()) {
|
||||||
|
if (StringUtils.isNotEmpty(cis.functionalVariantName)
|
||||||
|
&& !cr.getSupportedFunctionalVariants().contains(cis.functionalVariantName)) {
|
||||||
|
//Supported card, unsupported variant.
|
||||||
|
//Could note the card as missing but since these are often un-cards,
|
||||||
|
//it's likely absent because it does something out of scope.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addSetCard(e, cis, cr);
|
||||||
}
|
}
|
||||||
if (isCoreExpSet && logMissingPerEdition) {
|
if (isCoreExpSet && logMissingPerEdition) {
|
||||||
if (missingCards.isEmpty()) {
|
if (missingCards.isEmpty()) {
|
||||||
@@ -871,14 +888,16 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getArtCount(String cardName, String setCode) {
|
public int getArtCount(String cardName, String setCode) {
|
||||||
|
return getArtCount(cardName, setCode, null);
|
||||||
|
}
|
||||||
|
public int getArtCount(String cardName, String setCode, String functionalVariantName) {
|
||||||
if (cardName == null || setCode == null)
|
if (cardName == null || setCode == null)
|
||||||
return 0;
|
return 0;
|
||||||
Collection<PaperCard> cardsInSet = getAllCards(cardName, new Predicate<PaperCard>() {
|
Predicate<PaperCard> predicate = card -> card.getEdition().equalsIgnoreCase(setCode);
|
||||||
@Override
|
if(functionalVariantName != null && !functionalVariantName.equals(IPaperCard.NO_FUNCTIONAL_VARIANT)) {
|
||||||
public boolean apply(PaperCard card) {
|
predicate = Predicates.and(predicate, card -> functionalVariantName.equals(card.getFunctionalVariant()));
|
||||||
return card.getEdition().equalsIgnoreCase(setCode);
|
|
||||||
}
|
}
|
||||||
});
|
Collection<PaperCard> cardsInSet = getAllCards(cardName, predicate);
|
||||||
return cardsInSet.size();
|
return cardsInSet.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1142,7 +1161,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hasBadSetInfo) {
|
if (!hasBadSetInfo) {
|
||||||
int artCount = getArtCount(card.getName(), card.getEdition());
|
int artCount = getArtCount(card.getName(), card.getEdition(), card.getFunctionalVariant());
|
||||||
sb.append(CardDb.NameSetSeparator).append(card.getEdition());
|
sb.append(CardDb.NameSetSeparator).append(card.getEdition());
|
||||||
if (artCount >= IPaperCard.DEFAULT_ART_INDEX) {
|
if (artCount >= IPaperCard.DEFAULT_ART_INDEX) {
|
||||||
sb.append(CardDb.NameSetSeparator).append(card.getArtIndex()); // indexes start at 1 to match image file name conventions
|
sb.append(CardDb.NameSetSeparator).append(card.getArtIndex()); // indexes start at 1 to match image file name conventions
|
||||||
@@ -1229,7 +1248,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
int artIdx = IPaperCard.DEFAULT_ART_INDEX;
|
int artIdx = IPaperCard.DEFAULT_ART_INDEX;
|
||||||
for (CardInSet cis : e.getCardInSet(cardName))
|
for (CardInSet cis : e.getCardInSet(cardName))
|
||||||
paperCards.add(new PaperCard(rules, e.getCode(), cis.rarity, artIdx++, false,
|
paperCards.add(new PaperCard(rules, e.getCode(), cis.rarity, artIdx++, false,
|
||||||
cis.collectorNumber, cis.artistName));
|
cis.collectorNumber, cis.artistName, cis.functionalVariantName));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String lastEdition = null;
|
String lastEdition = null;
|
||||||
@@ -1249,7 +1268,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
int cardInSetIndex = Math.max(artIdx-1, 0); // make sure doesn't go below zero
|
int cardInSetIndex = Math.max(artIdx-1, 0); // make sure doesn't go below zero
|
||||||
CardInSet cds = cardsInSet.get(cardInSetIndex); // use ArtIndex to get the right Coll. Number
|
CardInSet cds = cardsInSet.get(cardInSetIndex); // use ArtIndex to get the right Coll. Number
|
||||||
paperCards.add(new PaperCard(rules, lastEdition, tuple.getValue(), artIdx++, false,
|
paperCards.add(new PaperCard(rules, lastEdition, tuple.getValue(), artIdx++, false,
|
||||||
cds.collectorNumber, cds.artistName));
|
cds.collectorNumber, cds.artistName, cds.functionalVariantName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (paperCards.isEmpty()) {
|
if (paperCards.isEmpty()) {
|
||||||
|
|||||||
@@ -166,12 +166,14 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
public final String collectorNumber;
|
public final String collectorNumber;
|
||||||
public final String name;
|
public final String name;
|
||||||
public final String artistName;
|
public final String artistName;
|
||||||
|
public final String functionalVariantName;
|
||||||
|
|
||||||
public CardInSet(final String name, final String collectorNumber, final CardRarity rarity, final String artistName) {
|
public CardInSet(final String name, final String collectorNumber, final CardRarity rarity, final String artistName, final String functionalVariantName) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.collectorNumber = collectorNumber;
|
this.collectorNumber = collectorNumber;
|
||||||
this.rarity = rarity;
|
this.rarity = rarity;
|
||||||
this.artistName = artistName;
|
this.artistName = artistName;
|
||||||
|
this.functionalVariantName = functionalVariantName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
@@ -189,6 +191,10 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
sb.append(" @");
|
sb.append(" @");
|
||||||
sb.append(artistName);
|
sb.append(artistName);
|
||||||
}
|
}
|
||||||
|
if (functionalVariantName != null) {
|
||||||
|
sb.append(" $");
|
||||||
|
sb.append(functionalVariantName);
|
||||||
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,9 +573,11 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
* cnum - grouping #2
|
* cnum - grouping #2
|
||||||
* rarity - grouping #4
|
* rarity - grouping #4
|
||||||
* name - grouping #5
|
* name - grouping #5
|
||||||
|
* artist name - grouping #7
|
||||||
|
* functional variant name - grouping #9
|
||||||
*/
|
*/
|
||||||
// "(^(.?[0-9A-Z]+.?))?(([SCURML]) )?(.*)$"
|
// "(^(.?[0-9A-Z]+.?))?(([SCURML]) )?(.*)$"
|
||||||
"(^(.?[0-9A-Z]+\\S?[A-Z]*)\\s)?(([SCURML])\\s)?([^@]*)( @(.*))?$"
|
"(^(.?[0-9A-Z]+\\S?[A-Z]*)\\s)?(([SCURML])\\s)?([^@\\$]*)( @([^\\$]*))?( \\$(.+))?$"
|
||||||
);
|
);
|
||||||
|
|
||||||
ListMultimap<String, CardInSet> cardMap = ArrayListMultimap.create();
|
ListMultimap<String, CardInSet> cardMap = ArrayListMultimap.create();
|
||||||
@@ -595,7 +603,8 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
CardRarity r = CardRarity.smartValueOf(matcher.group(4));
|
CardRarity r = CardRarity.smartValueOf(matcher.group(4));
|
||||||
String cardName = matcher.group(5);
|
String cardName = matcher.group(5);
|
||||||
String artistName = matcher.group(7);
|
String artistName = matcher.group(7);
|
||||||
CardInSet cis = new CardInSet(cardName, collectorNumber, r, artistName);
|
String functionalVariantName = matcher.group(9);
|
||||||
|
CardInSet cis = new CardInSet(cardName, collectorNumber, r, artistName, functionalVariantName);
|
||||||
|
|
||||||
cardMap.put(sectionName, cis);
|
cardMap.put(sectionName, cis);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package forge.card;
|
package forge.card;
|
||||||
|
|
||||||
import forge.card.mana.ManaCost;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import forge.card.mana.ManaCost;
|
||||||
|
|
||||||
//
|
//
|
||||||
// DO NOT AUTOFORMAT / CHECKSTYLE THIS FILE
|
// DO NOT AUTOFORMAT / CHECKSTYLE THIS FILE
|
||||||
@@ -40,6 +42,7 @@ final class CardFace implements ICardFace, Cloneable {
|
|||||||
private String toughness = null;
|
private String toughness = null;
|
||||||
private String initialLoyalty = "";
|
private String initialLoyalty = "";
|
||||||
private String defense = "";
|
private String defense = "";
|
||||||
|
private Set<Integer> attractionLights = null;
|
||||||
|
|
||||||
private String nonAbilityText = null;
|
private String nonAbilityText = null;
|
||||||
private List<String> keywords = null;
|
private List<String> keywords = null;
|
||||||
@@ -50,6 +53,8 @@ final class CardFace implements ICardFace, Cloneable {
|
|||||||
private List<String> replacements = null;
|
private List<String> replacements = null;
|
||||||
private Map<String, String> variables = null;
|
private Map<String, String> variables = null;
|
||||||
|
|
||||||
|
private Map<String, CardFace> functionalVariants = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// these implement ICardCharacteristics
|
// these implement ICardCharacteristics
|
||||||
@@ -60,6 +65,7 @@ final class CardFace implements ICardFace, Cloneable {
|
|||||||
@Override public String getToughness() { return toughness; }
|
@Override public String getToughness() { return toughness; }
|
||||||
@Override public String getInitialLoyalty() { return initialLoyalty; }
|
@Override public String getInitialLoyalty() { return initialLoyalty; }
|
||||||
@Override public String getDefense() { return defense; }
|
@Override public String getDefense() { return defense; }
|
||||||
|
@Override public Set<Integer> getAttractionLights() { return attractionLights; }
|
||||||
@Override public String getName() { return this.name; }
|
@Override public String getName() { return this.name; }
|
||||||
@Override public CardType getType() { return this.type; }
|
@Override public CardType getType() { return this.type; }
|
||||||
@Override public ManaCost getManaCost() { return this.manaCost; }
|
@Override public ManaCost getManaCost() { return this.manaCost; }
|
||||||
@@ -73,7 +79,11 @@ final class CardFace implements ICardFace, Cloneable {
|
|||||||
@Override public Iterable<String> getDraftActions() { return draftActions; }
|
@Override public Iterable<String> getDraftActions() { return draftActions; }
|
||||||
@Override public Iterable<String> getReplacements() { return replacements; }
|
@Override public Iterable<String> getReplacements() { return replacements; }
|
||||||
@Override public String getNonAbilityText() { return nonAbilityText; }
|
@Override public String getNonAbilityText() { return nonAbilityText; }
|
||||||
@Override public Iterable<Entry<String, String>> getVariables() { return variables.entrySet(); }
|
@Override public Iterable<Entry<String, String>> getVariables() {
|
||||||
|
if (variables == null)
|
||||||
|
return null;
|
||||||
|
return variables.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
@Override public String getAltName() { return this.altName; }
|
@Override public String getAltName() { return this.altName; }
|
||||||
|
|
||||||
@@ -91,6 +101,13 @@ final class CardFace implements ICardFace, Cloneable {
|
|||||||
void setOracleText(String text) { this.oracleText = text; }
|
void setOracleText(String text) { this.oracleText = text; }
|
||||||
void setInitialLoyalty(String value) { this.initialLoyalty = value; }
|
void setInitialLoyalty(String value) { this.initialLoyalty = value; }
|
||||||
void setDefense(String value) { this.defense = value; }
|
void setDefense(String value) { this.defense = value; }
|
||||||
|
void setAttractionLights(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
this.attractionLights = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.attractionLights = Arrays.stream(value.split(" ")).map(Integer::parseInt).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
void setPtText(String value) {
|
void setPtText(String value) {
|
||||||
final String[] k = value.split("/");
|
final String[] k = value.split("/");
|
||||||
@@ -128,6 +145,27 @@ final class CardFace implements ICardFace, Cloneable {
|
|||||||
void addSVar(String key, String value) { if (null == this.variables) { this.variables = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); } this.variables.put(key, value); }
|
void addSVar(String key, String value) { if (null == this.variables) { this.variables = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); } this.variables.put(key, value); }
|
||||||
|
|
||||||
|
|
||||||
|
//Functional variant methods. Used for Attractions and some Un-cards,
|
||||||
|
//when cards with the same name can have different logic.
|
||||||
|
public boolean hasFunctionalVariants() {
|
||||||
|
return this.functionalVariants != null;
|
||||||
|
}
|
||||||
|
@Override public ICardFace getFunctionalVariant(String variant) {
|
||||||
|
if(this.functionalVariants == null)
|
||||||
|
return null;
|
||||||
|
return this.functionalVariants.get(variant);
|
||||||
|
}
|
||||||
|
CardFace getOrCreateFunctionalVariant(String variant) {
|
||||||
|
if (this.functionalVariants == null) {
|
||||||
|
this.functionalVariants = new HashMap<>();
|
||||||
|
}
|
||||||
|
if (!this.functionalVariants.containsKey(variant)) {
|
||||||
|
this.functionalVariants.put(variant, new CardFace(this.name));
|
||||||
|
}
|
||||||
|
return this.functionalVariants.get(variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void assignMissingFields() { // Most scripts do not specify color explicitly
|
void assignMissingFields() { // Most scripts do not specify color explicitly
|
||||||
if ( null == oracleText ) { System.err.println(name + " has no Oracle text."); oracleText = ""; }
|
if ( null == oracleText ) { System.err.println(name + " has no Oracle text."); oracleText = ""; }
|
||||||
if ( manaCost == null && color == null ) System.err.println(name + " has neither ManaCost nor Color");
|
if ( manaCost == null && color == null ) System.err.println(name + " has neither ManaCost nor Color");
|
||||||
@@ -140,6 +178,7 @@ final class CardFace implements ICardFace, Cloneable {
|
|||||||
if ( replacements == null ) replacements = emptyList;
|
if ( replacements == null ) replacements = emptyList;
|
||||||
if ( variables == null ) variables = emptyMap;
|
if ( variables == null ) variables = emptyMap;
|
||||||
if ( null == nonAbilityText ) nonAbilityText = "";
|
if ( null == nonAbilityText ) nonAbilityText = "";
|
||||||
|
//Not assigning attractionLightVariants here. Too rarely used. Will test for it downstream.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ package forge.card;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.card.mana.IParserManaCost;
|
import forge.card.mana.IParserManaCost;
|
||||||
@@ -135,7 +135,8 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
public boolean isVariant() {
|
public boolean isVariant() {
|
||||||
CardType t = getType();
|
CardType t = getType();
|
||||||
return t.isVanguard() || t.isScheme() || t.isPlane() || t.isPhenomenon() || t.isConspiracy() || t.isDungeon();
|
return t.isVanguard() || t.isScheme() || t.isPlane() || t.isPhenomenon()
|
||||||
|
|| t.isConspiracy() || t.isDungeon() || t.isAttraction();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CardSplitType getSplitType() {
|
public CardSplitType getSplitType() {
|
||||||
@@ -251,6 +252,8 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
return mainPart.getDefense();
|
return mainPart.getDefense();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public Set<Integer> getAttractionLights() { return mainPart.getAttractionLights(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getOracleText() {
|
public String getOracleText() {
|
||||||
switch (splitType.getAggregationMethod()) {
|
switch (splitType.getAggregationMethod()) {
|
||||||
@@ -414,6 +417,14 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
this.deltaLife = Integer.parseInt(TextUtil.fastReplace(pt.substring(slashPos+1), "+", ""));
|
this.deltaLife = Integer.parseInt(TextUtil.fastReplace(pt.substring(slashPos+1), "+", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<String> supportedFunctionalVariants;
|
||||||
|
public boolean hasFunctionalVariants() {
|
||||||
|
return this.supportedFunctionalVariants != null;
|
||||||
|
}
|
||||||
|
public Set<String> getSupportedFunctionalVariants() {
|
||||||
|
return this.supportedFunctionalVariants;
|
||||||
|
}
|
||||||
|
|
||||||
public ColorSet getColorIdentity() {
|
public ColorSet getColorIdentity() {
|
||||||
return colorIdentity;
|
return colorIdentity;
|
||||||
}
|
}
|
||||||
@@ -438,6 +449,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
private boolean addsWildCardColor = false;
|
private boolean addsWildCardColor = false;
|
||||||
private String handLife = null;
|
private String handLife = null;
|
||||||
private String normalizedName = "";
|
private String normalizedName = "";
|
||||||
|
private Set<String> supportedFunctionalVariants = null;
|
||||||
|
|
||||||
private List<String> tokens = Lists.newArrayList();
|
private List<String> tokens = Lists.newArrayList();
|
||||||
|
|
||||||
@@ -475,6 +487,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
this.partnerWith = "";
|
this.partnerWith = "";
|
||||||
this.addsWildCardColor = false;
|
this.addsWildCardColor = false;
|
||||||
this.normalizedName = "";
|
this.normalizedName = "";
|
||||||
|
this.supportedFunctionalVariants = null;
|
||||||
this.tokens = Lists.newArrayList();
|
this.tokens = Lists.newArrayList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,6 +516,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(handLife))
|
if (StringUtils.isNotBlank(handLife))
|
||||||
result.setVanguardProperties(handLife);
|
result.setVanguardProperties(handLife);
|
||||||
|
result.supportedFunctionalVariants = this.supportedFunctionalVariants;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,7 +526,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
if (line.isEmpty() || line.charAt(0) == '#') {
|
if (line.isEmpty() || line.charAt(0) == '#') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.parseLine(line);
|
this.parseLine(line, this.faces[curFace]);
|
||||||
}
|
}
|
||||||
this.normalizedName = filename;
|
this.normalizedName = filename;
|
||||||
return this.getCard();
|
return this.getCard();
|
||||||
@@ -523,12 +537,15 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the line.
|
* Parses a single line of a card script.
|
||||||
*
|
*
|
||||||
* @param line
|
* @param line Line of text to parse.
|
||||||
* the line
|
|
||||||
*/
|
*/
|
||||||
public final void parseLine(final String line) {
|
public final void parseLine(final String line) {
|
||||||
|
this.parseLine(line, this.faces[curFace]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseLine(final String line, CardFace face) {
|
||||||
int colonPos = line.indexOf(':');
|
int colonPos = line.indexOf(':');
|
||||||
String key = colonPos > 0 ? line.substring(0, colonPos) : line;
|
String key = colonPos > 0 ? line.substring(0, colonPos) : line;
|
||||||
String value = colonPos > 0 ? line.substring(1+colonPos).trim() : null;
|
String value = colonPos > 0 ? line.substring(1+colonPos).trim() : null;
|
||||||
@@ -548,7 +565,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
switch (key.charAt(0)) {
|
switch (key.charAt(0)) {
|
||||||
case 'A':
|
case 'A':
|
||||||
if ("A".equals(key)) {
|
if ("A".equals(key)) {
|
||||||
this.faces[curFace].addAbility(value);
|
face.addAbility(value);
|
||||||
} else if ("AI".equals(key)) {
|
} else if ("AI".equals(key)) {
|
||||||
colonPos = value.indexOf(':');
|
colonPos = value.indexOf(':');
|
||||||
String variable = colonPos > 0 ? value.substring(0, colonPos) : value;
|
String variable = colonPos > 0 ? value.substring(0, colonPos) : value;
|
||||||
@@ -564,7 +581,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
} else if ("ALTERNATE".equals(key)) {
|
} else if ("ALTERNATE".equals(key)) {
|
||||||
this.curFace = 1;
|
this.curFace = 1;
|
||||||
} else if ("AltName".equals(key)) {
|
} else if ("AltName".equals(key)) {
|
||||||
this.faces[curFace].setAltName(value);
|
face.setAltName(value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -573,7 +590,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
// This is forge.card.CardColor not forge.CardColor.
|
// This is forge.card.CardColor not forge.CardColor.
|
||||||
// Why do we have two classes with the same name?
|
// Why do we have two classes with the same name?
|
||||||
ColorSet newCol = ColorSet.fromNames(value.split(","));
|
ColorSet newCol = ColorSet.fromNames(value.split(","));
|
||||||
this.faces[this.curFace].setColor(newCol);
|
face.setColor(newCol);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -585,9 +602,9 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
} else if ("DeckHas".equals(key)) {
|
} else if ("DeckHas".equals(key)) {
|
||||||
has = new DeckHints(value);
|
has = new DeckHints(value);
|
||||||
} else if ("Defense".equals(key)) {
|
} else if ("Defense".equals(key)) {
|
||||||
this.faces[this.curFace].setDefense(value);
|
face.setDefense(value);
|
||||||
} else if ("Draft".equals(key)) {
|
} else if ("Draft".equals(key)) {
|
||||||
this.faces[this.curFace].addDraftAction(value);
|
face.addDraftAction(value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -599,7 +616,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
case 'K':
|
case 'K':
|
||||||
if ("K".equals(key)) {
|
if ("K".equals(key)) {
|
||||||
this.faces[this.curFace].addKeyword(value);
|
face.addKeyword(value);
|
||||||
if (value.startsWith("Partner:")) {
|
if (value.startsWith("Partner:")) {
|
||||||
this.partnerWith = value.split(":")[1];
|
this.partnerWith = value.split(":")[1];
|
||||||
}
|
}
|
||||||
@@ -608,13 +625,16 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
if ("Loyalty".equals(key)) {
|
if ("Loyalty".equals(key)) {
|
||||||
this.faces[this.curFace].setInitialLoyalty(value);
|
face.setInitialLoyalty(value);
|
||||||
|
}
|
||||||
|
if ("Lights".equals(key)) {
|
||||||
|
face.setAttractionLights(value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
if ("ManaCost".equals(key)) {
|
if ("ManaCost".equals(key)) {
|
||||||
this.faces[this.curFace].setManaCost("no cost".equals(value) ? ManaCost.NO_COST
|
face.setManaCost("no cost".equals(value) ? ManaCost.NO_COST
|
||||||
: new ManaCost(new ManaCostParser(value)));
|
: new ManaCost(new ManaCostParser(value)));
|
||||||
} else if ("MeldPair".equals(key)) {
|
} else if ("MeldPair".equals(key)) {
|
||||||
this.meldWith = value;
|
this.meldWith = value;
|
||||||
@@ -629,25 +649,25 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
case 'O':
|
case 'O':
|
||||||
if ("Oracle".equals(key)) {
|
if ("Oracle".equals(key)) {
|
||||||
this.faces[this.curFace].setOracleText(value);
|
face.setOracleText(value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
if ("PT".equals(key)) {
|
if ("PT".equals(key)) {
|
||||||
this.faces[this.curFace].setPtText(value);
|
face.setPtText(value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'R':
|
case 'R':
|
||||||
if ("R".equals(key)) {
|
if ("R".equals(key)) {
|
||||||
this.faces[this.curFace].addReplacementEffect(value);
|
face.addReplacementEffect(value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'S':
|
case 'S':
|
||||||
if ("S".equals(key)) {
|
if ("S".equals(key)) {
|
||||||
this.faces[this.curFace].addStaticAbility(value);
|
face.addStaticAbility(value);
|
||||||
} else if (key.startsWith("SPECIALIZE")) {
|
} else if (key.startsWith("SPECIALIZE")) {
|
||||||
if (value.equals("WHITE")) {
|
if (value.equals("WHITE")) {
|
||||||
this.curFace = 2;
|
this.curFace = 2;
|
||||||
@@ -667,17 +687,32 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
String variable = colonPos > 0 ? value.substring(0, colonPos) : value;
|
String variable = colonPos > 0 ? value.substring(0, colonPos) : value;
|
||||||
value = colonPos > 0 ? value.substring(1+colonPos) : null;
|
value = colonPos > 0 ? value.substring(1+colonPos) : null;
|
||||||
|
|
||||||
this.faces[curFace].addSVar(variable, value);
|
face.addSVar(variable, value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'T':
|
case 'T':
|
||||||
if ("T".equals(key)) {
|
if ("T".equals(key)) {
|
||||||
this.faces[this.curFace].addTrigger(value);
|
face.addTrigger(value);
|
||||||
} else if ("Types".equals(key)) {
|
} else if ("Types".equals(key)) {
|
||||||
this.faces[this.curFace].setType(CardType.parse(value, false));
|
face.setType(CardType.parse(value, false));
|
||||||
} else if ("Text".equals(key) && !"no text".equals(value) && StringUtils.isNotBlank(value)) {
|
} else if ("Text".equals(key) && !"no text".equals(value) && StringUtils.isNotBlank(value)) {
|
||||||
this.faces[this.curFace].setNonAbilityText(value);
|
face.setNonAbilityText(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'V':
|
||||||
|
if("Variant".equals(key)) {
|
||||||
|
if (value == null) value = "";
|
||||||
|
colonPos = value.indexOf(':');
|
||||||
|
if(colonPos <= 0) throw new IllegalArgumentException("Missing variant name");
|
||||||
|
String variantName = value.substring(0, colonPos);
|
||||||
|
CardFace varFace = face.getOrCreateFunctionalVariant(variantName);
|
||||||
|
String variantLine = value.substring(1 + colonPos);
|
||||||
|
this.parseLine(variantLine, varFace);
|
||||||
|
if(this.supportedFunctionalVariants == null)
|
||||||
|
this.supportedFunctionalVariants = new HashSet<>();
|
||||||
|
this.supportedFunctionalVariants.add(variantName);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -356,6 +356,10 @@ public final class CardRulesPredicates {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Predicate<CardRules> canBePartnerCommanderWith(final CardRules commander) {
|
||||||
|
return (rules) -> rules.canBePartnerCommanders(commander);
|
||||||
|
}
|
||||||
|
|
||||||
private static class LeafString extends PredicateString<CardRules> {
|
private static class LeafString extends PredicateString<CardRules> {
|
||||||
public enum CardField {
|
public enum CardField {
|
||||||
ORACLE_TEXT, NAME, SUBTYPE, JOINED_TYPE, COST
|
ORACLE_TEXT, NAME, SUBTYPE, JOINED_TYPE, COST
|
||||||
@@ -654,6 +658,9 @@ 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_DUNGEON = CardRulesPredicates.coreType(true, CardType.CoreType.Dungeon);
|
public static final Predicate<CardRules> IS_DUNGEON = CardRulesPredicates.coreType(true, CardType.CoreType.Dungeon);
|
||||||
|
public static final Predicate<CardRules> IS_ATTRACTION = Predicates.and(Presets.IS_ARTIFACT,
|
||||||
|
CardRulesPredicates.subType("Attraction")
|
||||||
|
);
|
||||||
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.and(Presets.IS_LEGENDARY,
|
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
|
||||||
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
|
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
|
||||||
|
|||||||
@@ -500,6 +500,9 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
public final boolean isEquipment() { return hasSubtype("Equipment"); }
|
public final boolean isEquipment() { return hasSubtype("Equipment"); }
|
||||||
@Override
|
@Override
|
||||||
public final boolean isFortification() { return hasSubtype("Fortification"); }
|
public final boolean isFortification() { return hasSubtype("Fortification"); }
|
||||||
|
public boolean isAttraction() {
|
||||||
|
return hasSubtype("Attraction");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSaga() {
|
public boolean isSaga() {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public interface CardTypeView extends Iterable<String>, Serializable {
|
|||||||
boolean isAura();
|
boolean isAura();
|
||||||
boolean isEquipment();
|
boolean isEquipment();
|
||||||
boolean isFortification();
|
boolean isFortification();
|
||||||
|
boolean isAttraction();
|
||||||
|
|
||||||
boolean isSaga();
|
boolean isSaga();
|
||||||
boolean isHistoric();
|
boolean isHistoric();
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package forge.card;
|
|||||||
|
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public interface ICardCharacteristics {
|
public interface ICardCharacteristics {
|
||||||
String getName();
|
String getName();
|
||||||
CardType getType();
|
CardType getType();
|
||||||
@@ -14,6 +16,7 @@ public interface ICardCharacteristics {
|
|||||||
String getToughness();
|
String getToughness();
|
||||||
String getInitialLoyalty();
|
String getInitialLoyalty();
|
||||||
String getDefense();
|
String getDefense();
|
||||||
|
Set<Integer> getAttractionLights();
|
||||||
|
|
||||||
String getOracleText();
|
String getOracleText();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,7 @@ package forge.card;
|
|||||||
*/
|
*/
|
||||||
public interface ICardFace extends ICardCharacteristics, ICardRawAbilites, Comparable<ICardFace> {
|
public interface ICardFace extends ICardCharacteristics, ICardRawAbilites, Comparable<ICardFace> {
|
||||||
String getAltName();
|
String getAltName();
|
||||||
|
|
||||||
|
boolean hasFunctionalVariants();
|
||||||
|
ICardFace getFunctionalVariant(String variant);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,12 +155,12 @@ public class CardPool extends ItemPool<PaperCard> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int countByName(String cardName, boolean isCommonCard) {
|
public int countByName(String cardName) {
|
||||||
PaperCard pc = isCommonCard
|
return this.countAll((c) -> c.getName().equals(cardName));
|
||||||
? StaticData.instance().getCommonCards().getCard(cardName)
|
}
|
||||||
: StaticData.instance().getVariantCards().getCard(cardName);
|
|
||||||
|
|
||||||
return this.count(pc);
|
public int countByName(PaperCard card) {
|
||||||
|
return this.countAll((c) -> c.getName().equals(card.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -552,6 +552,17 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
|||||||
return allCards;
|
return allCards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the number of cards with the given name across all deck sections.
|
||||||
|
*/
|
||||||
|
public int countByName(String cardName) {
|
||||||
|
int sum = 0;
|
||||||
|
for (Entry<DeckSection, CardPool> section : this) {
|
||||||
|
sum += section.getValue().countByName(cardName);
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAiHints(String aiHintsInfo) {
|
public void setAiHints(String aiHintsInfo) {
|
||||||
if (aiHintsInfo == null || aiHintsInfo.trim().equals("")) {
|
if (aiHintsInfo == null || aiHintsInfo.trim().equals("")) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -50,7 +50,15 @@ public enum DeckFormat {
|
|||||||
// Main board: allowed size SB: restriction Max distinct non basic cards
|
// Main board: allowed size SB: restriction Max distinct non basic cards
|
||||||
Constructed ( Range.between(60, Integer.MAX_VALUE), Range.between(0, 15), 4),
|
Constructed ( Range.between(60, Integer.MAX_VALUE), Range.between(0, 15), 4),
|
||||||
QuestDeck ( Range.between(40, Integer.MAX_VALUE), Range.between(0, 15), 4),
|
QuestDeck ( Range.between(40, Integer.MAX_VALUE), Range.between(0, 15), 4),
|
||||||
Limited ( Range.between(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE),
|
Limited ( Range.between(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE) {
|
||||||
|
@Override
|
||||||
|
public String getAttractionDeckConformanceProblem(Deck deck) {
|
||||||
|
//Limited attraction decks have a minimum size of 3 and no singleton restriction.
|
||||||
|
if (deck.get(DeckSection.Attractions).countAll() < 3)
|
||||||
|
return "must contain at least 3 attractions, or none at all";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
Commander ( Range.is(99), Range.between(0, 10), 1, null, new Predicate<PaperCard>() {
|
Commander ( Range.is(99), Range.between(0, 10), 1, null, new Predicate<PaperCard>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(PaperCard card) {
|
public boolean apply(PaperCard card) {
|
||||||
@@ -210,9 +218,9 @@ public enum DeckFormat {
|
|||||||
// Adjust minimum base on number of Advantageous Proclamation or similar cards
|
// Adjust minimum base on number of Advantageous Proclamation or similar cards
|
||||||
CardPool conspiracies = deck.get(DeckSection.Conspiracy);
|
CardPool conspiracies = deck.get(DeckSection.Conspiracy);
|
||||||
if (conspiracies != null) {
|
if (conspiracies != null) {
|
||||||
min -= (5 * conspiracies.countByName(ADVPROCLAMATION, false));
|
min -= (5 * conspiracies.countByName(ADVPROCLAMATION));
|
||||||
// Commented out to remove warnings from the code.
|
// Commented out to remove warnings from the code.
|
||||||
// noBasicLands = conspiracies.countByName(SOVREALM, false) > 0;
|
// noBasicLands = conspiracies.countByName(SOVREALM) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasCommander()) {
|
if (hasCommander()) {
|
||||||
@@ -330,6 +338,12 @@ public enum DeckFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (deck.has(DeckSection.Attractions)) {
|
||||||
|
String attractionError = getAttractionDeckConformanceProblem(deck);
|
||||||
|
if (attractionError != null)
|
||||||
|
return attractionError;
|
||||||
|
}
|
||||||
|
|
||||||
final int maxCopies = getMaxCardCopies();
|
final int maxCopies = getMaxCardCopies();
|
||||||
//Must contain no more than 4 of the same card
|
//Must contain no more than 4 of the same card
|
||||||
//shared among the main deck and sideboard, except
|
//shared among the main deck and sideboard, except
|
||||||
@@ -356,7 +370,7 @@ public enum DeckFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Integer cardCopies = canHaveSpecificNumberInDeck(simpleCard);
|
Integer cardCopies = canHaveSpecificNumberInDeck(simpleCard);
|
||||||
if (cardCopies != null && deck.getMain().countByName(cp.getKey(), true) > cardCopies) {
|
if (cardCopies != null && deck.getMain().countByName(cp.getKey()) > cardCopies) {
|
||||||
return TextUtil.concatWithSpace("must not contain more than", String.valueOf(cardCopies), "copies of the card", cp.getKey());
|
return TextUtil.concatWithSpace("must not contain more than", String.valueOf(cardCopies), "copies of the card", cp.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,6 +391,18 @@ public enum DeckFormat {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAttractionDeckConformanceProblem(Deck deck) {
|
||||||
|
CardPool attractionDeck = deck.get(DeckSection.Attractions);
|
||||||
|
if (attractionDeck.countAll() < 10)
|
||||||
|
return "must contain at least 10 attractions, or none at all";
|
||||||
|
for (Entry<PaperCard, Integer> cp : attractionDeck) {
|
||||||
|
//Constructed Attraction deck must be singleton
|
||||||
|
if (attractionDeck.countByName(cp.getKey()) > 1)
|
||||||
|
return TextUtil.concatWithSpace("contains more than 1 copy of the attraction", cp.getKey().getName());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean canHaveAnyNumberOf(final IPaperCard icard) {
|
public static boolean canHaveAnyNumberOf(final IPaperCard icard) {
|
||||||
return icard.getRules().getType().isBasicLand()
|
return icard.getRules().getType().isBasicLand()
|
||||||
|| Iterables.contains(icard.getRules().getMainPart().getKeywords(),
|
|| Iterables.contains(icard.getRules().getMainPart().getKeywords(),
|
||||||
@@ -532,16 +558,14 @@ public enum DeckFormat {
|
|||||||
|
|
||||||
public Predicate<PaperCard> isLegalCardForCommanderPredicate(List<PaperCard> commanders) {
|
public Predicate<PaperCard> isLegalCardForCommanderPredicate(List<PaperCard> commanders) {
|
||||||
byte cmdCI = 0;
|
byte cmdCI = 0;
|
||||||
boolean hasPartner = false;
|
|
||||||
for (final PaperCard p : commanders) {
|
for (final PaperCard p : commanders) {
|
||||||
cmdCI |= p.getRules().getColorIdentity().getColor();
|
cmdCI |= p.getRules().getColorIdentity().getColor();
|
||||||
if (p.getRules().canBePartnerCommander()) {
|
|
||||||
hasPartner = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Predicate<CardRules> predicate = CardRulesPredicates.hasColorIdentity(cmdCI);
|
Predicate<CardRules> predicate = CardRulesPredicates.hasColorIdentity(cmdCI);
|
||||||
if (hasPartner) { //also show available partners a commander can have a partner
|
if (commanders.size() == 1 && commanders.get(0).getRules().canBePartnerCommander()) { //also show available partners a commander can have a partner
|
||||||
predicate = Predicates.or(predicate, CardRulesPredicates.Presets.CAN_BE_PARTNER_COMMANDER);
|
//702.124g If a legendary card has more than one partner ability, you may choose which one to use when designating your commander, but you can’t use both.
|
||||||
|
//Notably, no partner ability or combination of partner abilities can ever let a player have more than two commanders.
|
||||||
|
predicate = Predicates.or(predicate, CardRulesPredicates.canBePartnerCommanderWith(commanders.get(0).getRules()));
|
||||||
}
|
}
|
||||||
return Predicates.compose(predicate, PaperCard.FN_GET_RULES);
|
return Predicates.compose(predicate, PaperCard.FN_GET_RULES);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,6 +151,8 @@ public class DeckRecognizer {
|
|||||||
matchedSection = DeckSection.Conspiracy;
|
matchedSection = DeckSection.Conspiracy;
|
||||||
else if (sectionName.equals("planes"))
|
else if (sectionName.equals("planes"))
|
||||||
matchedSection = DeckSection.Planes;
|
matchedSection = DeckSection.Planes;
|
||||||
|
else if (sectionName.equals("attractions"))
|
||||||
|
matchedSection = DeckSection.Attractions;
|
||||||
|
|
||||||
if (matchedSection == null) // no match found
|
if (matchedSection == null) // no match found
|
||||||
return null;
|
return null;
|
||||||
@@ -760,6 +762,9 @@ public class DeckRecognizer {
|
|||||||
// is not supported, but other possibilities exist (e.g. Commander card in Constructed
|
// is not supported, but other possibilities exist (e.g. Commander card in Constructed
|
||||||
// could potentially go in Main)
|
// could potentially go in Main)
|
||||||
DeckSection matchedSection = DeckSection.matchingSection(card);
|
DeckSection matchedSection = DeckSection.matchingSection(card);
|
||||||
|
// If it's a commander candidate, put it there.
|
||||||
|
if (matchedSection == DeckSection.Main && this.isAllowed(DeckSection.Commander) && DeckSection.Commander.validate(card))
|
||||||
|
return DeckSection.Commander;
|
||||||
if (this.isAllowed(matchedSection))
|
if (this.isAllowed(matchedSection))
|
||||||
return matchedSection;
|
return matchedSection;
|
||||||
// if matched section is not allowed, try to match the card to main.
|
// if matched section is not allowed, try to match the card to main.
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ public enum DeckSection {
|
|||||||
Planes(10, Validators.PLANES_VALIDATOR),
|
Planes(10, Validators.PLANES_VALIDATOR),
|
||||||
Schemes(20, Validators.SCHEME_VALIDATOR),
|
Schemes(20, Validators.SCHEME_VALIDATOR),
|
||||||
Conspiracy(0, Validators.CONSPIRACY_VALIDATOR),
|
Conspiracy(0, Validators.CONSPIRACY_VALIDATOR),
|
||||||
Dungeon(0, Validators.DUNGEON_VALIDATOR);
|
Dungeon(0, Validators.DUNGEON_VALIDATOR),
|
||||||
|
Attractions(0, Validators.ATTRACTION_VALIDATOR);
|
||||||
|
|
||||||
private final int typicalSize; // Rules enforcement is done in DeckFormat class, this is for reference only
|
private final int typicalSize; // Rules enforcement is done in DeckFormat class, this is for reference only
|
||||||
private Function<PaperCard, Boolean> fnValidator;
|
private Function<PaperCard, Boolean> fnValidator;
|
||||||
@@ -40,10 +41,10 @@ public enum DeckSection {
|
|||||||
return Avatar;
|
return Avatar;
|
||||||
if (DeckSection.Planes.validate(card))
|
if (DeckSection.Planes.validate(card))
|
||||||
return Planes;
|
return Planes;
|
||||||
if (DeckSection.Commander.validate(card))
|
|
||||||
return Commander;
|
|
||||||
if (DeckSection.Dungeon.validate(card))
|
if (DeckSection.Dungeon.validate(card))
|
||||||
return Dungeon;
|
return Dungeon;
|
||||||
|
if (DeckSection.Attractions.validate(card))
|
||||||
|
return Attractions;
|
||||||
return Main; // default
|
return Main; // default
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,5 +120,10 @@ public enum DeckSection {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static final Function<PaperCard, Boolean> ATTRACTION_VALIDATOR = card -> {
|
||||||
|
CardType t = card.getRules().getType();
|
||||||
|
return t.isAttraction();
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public interface IPaperCard extends InventoryItem, Serializable {
|
|||||||
int DEFAULT_ART_INDEX = 1;
|
int DEFAULT_ART_INDEX = 1;
|
||||||
int NO_ART_INDEX = -1; // Placeholder when NO ArtIndex is Specified
|
int NO_ART_INDEX = -1; // Placeholder when NO ArtIndex is Specified
|
||||||
String NO_ARTIST_NAME = "";
|
String NO_ARTIST_NAME = "";
|
||||||
|
String NO_FUNCTIONAL_VARIANT = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of filters based on CardPrinted values.
|
* Number of filters based on CardPrinted values.
|
||||||
@@ -243,6 +244,7 @@ public interface IPaperCard extends InventoryItem, Serializable {
|
|||||||
String getName();
|
String getName();
|
||||||
String getEdition();
|
String getEdition();
|
||||||
String getCollectorNumber();
|
String getCollectorNumber();
|
||||||
|
String getFunctionalVariant();
|
||||||
int getArtIndex();
|
int getArtIndex();
|
||||||
boolean isFoil();
|
boolean isFoil();
|
||||||
boolean isToken();
|
boolean isToken();
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
|||||||
private final boolean foil;
|
private final boolean foil;
|
||||||
private Boolean hasImage;
|
private Boolean hasImage;
|
||||||
private String sortableName;
|
private String sortableName;
|
||||||
|
private final String functionalVariant;
|
||||||
|
|
||||||
// Calculated fields are below:
|
// Calculated fields are below:
|
||||||
private transient CardRarity rarity; // rarity is given in ctor when set is assigned
|
private transient CardRarity rarity; // rarity is given in ctor when set is assigned
|
||||||
@@ -79,6 +80,11 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
|||||||
return collectorNumber;
|
return collectorNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFunctionalVariant() {
|
||||||
|
return functionalVariant;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getArtIndex() {
|
public int getArtIndex() {
|
||||||
return artIndex;
|
return artIndex;
|
||||||
@@ -120,7 +126,7 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
|||||||
|
|
||||||
if (this.foiledVersion == null) {
|
if (this.foiledVersion == null) {
|
||||||
this.foiledVersion = new PaperCard(this.rules, this.edition, this.rarity,
|
this.foiledVersion = new PaperCard(this.rules, this.edition, this.rarity,
|
||||||
this.artIndex, true, String.valueOf(collectorNumber), this.artist);
|
this.artIndex, true, String.valueOf(collectorNumber), this.artist, this.functionalVariant);
|
||||||
}
|
}
|
||||||
return this.foiledVersion;
|
return this.foiledVersion;
|
||||||
}
|
}
|
||||||
@@ -129,7 +135,7 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
|||||||
return this;
|
return this;
|
||||||
|
|
||||||
PaperCard unFoiledVersion = new PaperCard(this.rules, this.edition, this.rarity,
|
PaperCard unFoiledVersion = new PaperCard(this.rules, this.edition, this.rarity,
|
||||||
this.artIndex, false, String.valueOf(collectorNumber), this.artist);
|
this.artIndex, false, String.valueOf(collectorNumber), this.artist, this.functionalVariant);
|
||||||
return unFoiledVersion;
|
return unFoiledVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,11 +178,12 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
|||||||
|
|
||||||
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0) {
|
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0) {
|
||||||
this(rules0, edition0, rarity0, IPaperCard.DEFAULT_ART_INDEX, false,
|
this(rules0, edition0, rarity0, IPaperCard.DEFAULT_ART_INDEX, false,
|
||||||
IPaperCard.NO_COLLECTOR_NUMBER, IPaperCard.NO_ARTIST_NAME);
|
IPaperCard.NO_COLLECTOR_NUMBER, IPaperCard.NO_ARTIST_NAME, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0,
|
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0,
|
||||||
final int artIndex0, final boolean foil0, final String collectorNumber0, final String artist0) {
|
final int artIndex0, final boolean foil0, final String collectorNumber0,
|
||||||
|
final String artist0, final String functionalVariant) {
|
||||||
if (rules0 == null || edition0 == null || rarity0 == null) {
|
if (rules0 == null || edition0 == null || rarity0 == null) {
|
||||||
throw new IllegalArgumentException("Cannot create card without rules, edition or rarity");
|
throw new IllegalArgumentException("Cannot create card without rules, edition or rarity");
|
||||||
}
|
}
|
||||||
@@ -191,6 +198,7 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
|||||||
// If the user changes the language this will make cards sort by the old language until they restart the game.
|
// If the user changes the language this will make cards sort by the old language until they restart the game.
|
||||||
// This is a good tradeoff
|
// This is a good tradeoff
|
||||||
sortableName = TextUtil.toSortableName(CardTranslation.getTranslatedName(rules0.getName()));
|
sortableName = TextUtil.toSortableName(CardTranslation.getTranslatedName(rules0.getName()));
|
||||||
|
this.functionalVariant = functionalVariant != null ? functionalVariant : IPaperCard.NO_FUNCTIONAL_VARIANT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PaperCard FAKE_CARD = new PaperCard(CardRules.getUnsupportedCardNamed("Fake Card"), "Fake Edition", CardRarity.Common);
|
public static PaperCard FAKE_CARD = new PaperCard(CardRules.getUnsupportedCardNamed("Fake Card"), "Fake Edition", CardRarity.Common);
|
||||||
|
|||||||
@@ -150,6 +150,12 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
|||||||
return IPaperCard.NO_COLLECTOR_NUMBER;
|
return IPaperCard.NO_COLLECTOR_NUMBER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFunctionalVariant() {
|
||||||
|
//Tokens aren't differentiated by name, so they don't really need support for this.
|
||||||
|
return IPaperCard.NO_FUNCTIONAL_VARIANT;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getArtIndex() {
|
public int getArtIndex() {
|
||||||
return artIndex;
|
return artIndex;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import forge.StaticData;
|
|||||||
import forge.card.CardDb;
|
import forge.card.CardDb;
|
||||||
import forge.card.CardRules;
|
import forge.card.CardRules;
|
||||||
import forge.card.CardSplitType;
|
import forge.card.CardSplitType;
|
||||||
|
import forge.item.IPaperCard;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -74,7 +75,7 @@ public class ImageUtil {
|
|||||||
final boolean hasManyPictures;
|
final boolean hasManyPictures;
|
||||||
final CardDb db = !card.isVariant() ? StaticData.instance().getCommonCards() : StaticData.instance().getVariantCards();
|
final CardDb db = !card.isVariant() ? StaticData.instance().getCommonCards() : StaticData.instance().getVariantCards();
|
||||||
if (includeSet) {
|
if (includeSet) {
|
||||||
cntPictures = db.getArtCount(card.getName(), edition);
|
cntPictures = db.getArtCount(card.getName(), edition, cp.getFunctionalVariant());
|
||||||
hasManyPictures = cntPictures > 1;
|
hasManyPictures = cntPictures > 1;
|
||||||
} else {
|
} else {
|
||||||
cntPictures = 1;
|
cntPictures = 1;
|
||||||
@@ -149,6 +150,8 @@ public class ImageUtil {
|
|||||||
}
|
}
|
||||||
} else if (CardSplitType.Split == cp.getRules().getSplitType()) {
|
} else if (CardSplitType.Split == cp.getRules().getSplitType()) {
|
||||||
return card.getMainPart().getName() + card.getOtherPart().getName();
|
return card.getMainPart().getName() + card.getOtherPart().getName();
|
||||||
|
} else if (!IPaperCard.NO_FUNCTIONAL_VARIANT.equals(cp.getFunctionalVariant())) {
|
||||||
|
return cp.getName() + " " + cp.getFunctionalVariant();
|
||||||
}
|
}
|
||||||
return cp.getName();
|
return cp.getName();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,6 +156,16 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//717.6. If a card with an Astrotorium card back would be put into a zone other than the battlefield, exile,
|
||||||
|
//or the command zone from anywhere, instead its owner puts it into the junkyard.
|
||||||
|
if (c.isAttractionCard() && !toBattlefield && !zoneTo.is(ZoneType.AttractionDeck)
|
||||||
|
&& !zoneTo.is(ZoneType.Junkyard) && !zoneTo.is(ZoneType.Exile) && !zoneTo.is(ZoneType.Command)) {
|
||||||
|
//This should technically be a replacement effect, but with the "can apply more than once to the same event"
|
||||||
|
//clause, this seems sufficient for now.
|
||||||
|
//TODO: Figure out what on earth happens if you animate an attraction, mutate a creature/commander/token onto it, and it dies...
|
||||||
|
return moveToJunkyard(c, cause, params);
|
||||||
|
}
|
||||||
|
|
||||||
boolean suppress = !c.isToken() && zoneFrom.equals(zoneTo);
|
boolean suppress = !c.isToken() && zoneFrom.equals(zoneTo);
|
||||||
|
|
||||||
Card copied = null;
|
Card copied = null;
|
||||||
@@ -447,7 +457,8 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
game.getCombat().removeFromCombat(c);
|
game.getCombat().removeFromCombat(c);
|
||||||
}
|
}
|
||||||
if ((zoneFrom.is(ZoneType.Library) || zoneFrom.is(ZoneType.PlanarDeck) || zoneFrom.is(ZoneType.SchemeDeck))
|
if ((zoneFrom.is(ZoneType.Library) || zoneFrom.is(ZoneType.PlanarDeck)
|
||||||
|
|| zoneFrom.is(ZoneType.SchemeDeck) || zoneFrom.is(ZoneType.AttractionDeck))
|
||||||
&& zoneFrom == zoneTo && position.equals(zoneFrom.size()) && position != 0) {
|
&& zoneFrom == zoneTo && position.equals(zoneFrom.size()) && position != 0) {
|
||||||
position--;
|
position--;
|
||||||
}
|
}
|
||||||
@@ -507,7 +518,7 @@ public class GameAction {
|
|||||||
if (card.isRealCommander()) {
|
if (card.isRealCommander()) {
|
||||||
card.setMoveToCommandZone(true);
|
card.setMoveToCommandZone(true);
|
||||||
}
|
}
|
||||||
// 723.3e & 903.9a
|
// 727.3e & 903.9a
|
||||||
if (wasToken && !card.isRealToken() || card.isRealCommander()) {
|
if (wasToken && !card.isRealToken() || card.isRealCommander()) {
|
||||||
Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(card);
|
Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(card);
|
||||||
repParams.put(AbilityKey.CardLKI, card);
|
repParams.put(AbilityKey.CardLKI, card);
|
||||||
@@ -754,6 +765,8 @@ public class GameAction {
|
|||||||
case Stack: return moveToStack(c, cause, params);
|
case Stack: return moveToStack(c, cause, params);
|
||||||
case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause, params);
|
case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause, params);
|
||||||
case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause, params);
|
case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause, params);
|
||||||
|
case AttractionDeck: return moveToVariantDeck(c, ZoneType.AttractionDeck, libPosition, cause, params);
|
||||||
|
case Junkyard: return moveToJunkyard(c, cause, params);
|
||||||
default: // sideboard will also get there
|
default: // sideboard will also get there
|
||||||
return moveTo(c.getOwner().getZone(name), c, cause);
|
return moveTo(c.getOwner().getZone(name), c, cause);
|
||||||
}
|
}
|
||||||
@@ -900,6 +913,11 @@ public class GameAction {
|
|||||||
return changeZone(game.getZoneOf(c), deck, c, deckPosition, cause, params);
|
return changeZone(game.getZoneOf(c), deck, c, deckPosition, cause, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final Card moveToJunkyard(Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||||
|
final PlayerZone junkyard = c.getOwner().getZone(ZoneType.Junkyard);
|
||||||
|
return moveTo(junkyard, c, cause, params);
|
||||||
|
}
|
||||||
|
|
||||||
public final CardCollection exile(final CardCollection cards, SpellAbility cause, Map<AbilityKey, Object> params) {
|
public final CardCollection exile(final CardCollection cards, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||||
CardCollection result = new CardCollection();
|
CardCollection result = new CardCollection();
|
||||||
for (Card card : cards) {
|
for (Card card : cards) {
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ public enum AbilityKey {
|
|||||||
ReplacementResult("ReplacementResult"),
|
ReplacementResult("ReplacementResult"),
|
||||||
ReplacementResultMap("ReplacementResultMap"),
|
ReplacementResultMap("ReplacementResultMap"),
|
||||||
Result("Result"),
|
Result("Result"),
|
||||||
|
RolledToVisitAttractions("RolledToVisitAttractions"),
|
||||||
RoomName("RoomName"),
|
RoomName("RoomName"),
|
||||||
Scheme("Scheme"),
|
Scheme("Scheme"),
|
||||||
ScryBottom("ScryBottom"),
|
ScryBottom("ScryBottom"),
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ public enum ApiType {
|
|||||||
ChooseSector (ChooseSectorEffect.class),
|
ChooseSector (ChooseSectorEffect.class),
|
||||||
ChooseSource (ChooseSourceEffect.class),
|
ChooseSource (ChooseSourceEffect.class),
|
||||||
ChooseType (ChooseTypeEffect.class),
|
ChooseType (ChooseTypeEffect.class),
|
||||||
|
ClaimThePrize (ClaimThePrizeEffect.class),
|
||||||
Clash (ClashEffect.class),
|
Clash (ClashEffect.class),
|
||||||
ClassLevelUp (ClassLevelUpEffect.class),
|
ClassLevelUp (ClassLevelUpEffect.class),
|
||||||
Cleanup (CleanUpEffect.class),
|
Cleanup (CleanUpEffect.class),
|
||||||
@@ -124,6 +125,7 @@ public enum ApiType {
|
|||||||
Mutate (MutateEffect.class),
|
Mutate (MutateEffect.class),
|
||||||
NameCard (ChooseCardNameEffect.class),
|
NameCard (ChooseCardNameEffect.class),
|
||||||
NoteCounters (CountersNoteEffect.class),
|
NoteCounters (CountersNoteEffect.class),
|
||||||
|
OpenAttraction (OpenAttractionEffect.class),
|
||||||
PeekAndReveal (PeekAndRevealEffect.class),
|
PeekAndReveal (PeekAndRevealEffect.class),
|
||||||
PermanentCreature (PermanentCreatureEffect.class),
|
PermanentCreature (PermanentCreatureEffect.class),
|
||||||
PermanentNoncreature (PermanentNoncreatureEffect.class),
|
PermanentNoncreature (PermanentNoncreatureEffect.class),
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import forge.game.Game;
|
||||||
|
import forge.game.ability.AbilityKey;
|
||||||
|
import forge.game.ability.AbilityUtils;
|
||||||
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardCollection;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.trigger.TriggerType;
|
||||||
|
import forge.util.Lang;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ClaimThePrizeEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolve(SpellAbility sa) {
|
||||||
|
final Card host = sa.getHostCard();
|
||||||
|
final Player activator = sa.getActivatingPlayer();
|
||||||
|
final Game game = activator.getGame();
|
||||||
|
final CardCollection attractions = AbilityUtils.getDefinedCards(host, sa.getParamOrDefault("Defined", "Self"), sa);
|
||||||
|
|
||||||
|
for(Card c : attractions) {
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(activator);
|
||||||
|
runParams.put(AbilityKey.Card, c);
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.ClaimPrize, runParams, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
|
final Card host = sa.getHostCard();
|
||||||
|
final CardCollection attractions = AbilityUtils.getDefinedCards(host, sa.getParamOrDefault("Defined", "Self"), sa);
|
||||||
|
return String.format("Claim the Prize from %s!", Lang.joinHomogenous(attractions));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import forge.game.ability.AbilityKey;
|
||||||
|
import forge.game.ability.AbilityUtils;
|
||||||
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardZoneTable;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.zone.PlayerZone;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
|
import forge.util.Lang;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class OpenAttractionEffect extends SpellAbilityEffect {
|
||||||
|
@Override
|
||||||
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
final List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
|
||||||
|
int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa) : 1;
|
||||||
|
|
||||||
|
if(tgtPlayers.isEmpty())
|
||||||
|
return "";
|
||||||
|
|
||||||
|
sb.append(Lang.joinHomogenous(tgtPlayers));
|
||||||
|
|
||||||
|
if (tgtPlayers.size() > 1) {
|
||||||
|
sb.append(" each");
|
||||||
|
}
|
||||||
|
sb.append(Lang.joinVerb(tgtPlayers, " open")).append(" ");
|
||||||
|
sb.append(amount == 1 ? "an Attraction." : (Lang.getNumeral(amount) + " Attractions."));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolve(SpellAbility sa) {
|
||||||
|
final Card source = sa.getHostCard();
|
||||||
|
final List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
|
||||||
|
int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa) : 1;
|
||||||
|
|
||||||
|
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||||
|
final CardZoneTable triggerList = AbilityKey.addCardZoneTableParams(moveParams, sa);
|
||||||
|
|
||||||
|
for (Player p : tgtPlayers) {
|
||||||
|
if (!p.isInGame())
|
||||||
|
continue;
|
||||||
|
final PlayerZone attractionDeck = p.getZone(ZoneType.AttractionDeck);
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
if(attractionDeck.isEmpty())
|
||||||
|
continue;
|
||||||
|
Card attraction = attractionDeck.get(0);
|
||||||
|
attraction = p.getGame().getAction().moveToPlay(attraction, sa, moveParams);
|
||||||
|
if (sa.hasParam("Remember")) {
|
||||||
|
source.addRemembered(attraction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
triggerList.triggerChangesZoneAll(sa.getHostCard().getGame(), sa);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import forge.game.player.PlayerCollection;
|
|||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
|
import forge.util.Lang;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -44,6 +45,13 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
final PlayerCollection player = getTargetPlayers(sa);
|
final PlayerCollection player = getTargetPlayers(sa);
|
||||||
|
|
||||||
|
if(sa.hasParam("ToVisitYourAttractions")) {
|
||||||
|
if (player.size() == 1 && player.get(0).equals(sa.getActivatingPlayer()))
|
||||||
|
return "Roll to Visit Your Attractions.";
|
||||||
|
else
|
||||||
|
return String.format("%s %s to visit their Attractions.", Lang.joinHomogenous(player), Lang.joinVerb(player, "roll"));
|
||||||
|
}
|
||||||
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
if (player.size() == 1 && player.get(0).equals(sa.getActivatingPlayer())) {
|
if (player.size() == 1 && player.get(0).equals(sa.getActivatingPlayer())) {
|
||||||
stringBuilder.append("Roll ");
|
stringBuilder.append("Roll ");
|
||||||
@@ -60,9 +68,13 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int rollDiceForPlayer(SpellAbility sa, Player player, int amount, int sides) {
|
public static int rollDiceForPlayer(SpellAbility sa, Player player, int amount, int sides) {
|
||||||
return rollDiceForPlayer(sa, player, amount, sides, 0, 0, null);
|
boolean toVisitAttractions = sa != null && sa.hasParam("ToVisitYourAttractions");
|
||||||
|
return rollDiceForPlayer(sa, player, amount, sides, 0, 0, null, toVisitAttractions);
|
||||||
}
|
}
|
||||||
private static int rollDiceForPlayer(SpellAbility sa, Player player, int amount, int sides, int ignore, int modifier, List<Integer> rollsResult) {
|
public static int rollDiceForPlayerToVisitAttractions(Player player) {
|
||||||
|
return rollDiceForPlayer(null, player, 1, 6, 0, 0, null, true);
|
||||||
|
}
|
||||||
|
private static int rollDiceForPlayer(SpellAbility sa, Player player, int amount, int sides, int ignore, int modifier, List<Integer> rollsResult, boolean toVisitAttractions) {
|
||||||
Map<Player, Integer> ignoreChosenMap = Maps.newHashMap();
|
Map<Player, Integer> ignoreChosenMap = Maps.newHashMap();
|
||||||
|
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(player);
|
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(player);
|
||||||
@@ -76,6 +88,7 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
case Updated: {
|
case Updated: {
|
||||||
amount = (int) repParams.get(AbilityKey.Number);
|
amount = (int) repParams.get(AbilityKey.Number);
|
||||||
ignore = (int) repParams.get(AbilityKey.Ignore);
|
ignore = (int) repParams.get(AbilityKey.Ignore);
|
||||||
|
//noinspection unchecked
|
||||||
ignoreChosenMap = (Map<Player, Integer>) repParams.get(AbilityKey.IgnoreChosen);
|
ignoreChosenMap = (Map<Player, Integer>) repParams.get(AbilityKey.IgnoreChosen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -121,8 +134,9 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
//Notify of results
|
//Notify of results
|
||||||
if (amount > 0) {
|
if (amount > 0) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(Localizer.getInstance().getMessage("lblPlayerRolledResult", player,
|
String rollResults = StringUtils.join(naturalRolls, ", ");
|
||||||
StringUtils.join(naturalRolls, ", ")));
|
String resultMessage = toVisitAttractions ? "lblAttractionRollResult" : "lblPlayerRolledResult";
|
||||||
|
sb.append(Localizer.getInstance().getMessage(resultMessage, player, rollResults));
|
||||||
if (!ignored.isEmpty()) {
|
if (!ignored.isEmpty()) {
|
||||||
sb.append("\r\n").append(Localizer.getInstance().getMessage("lblIgnoredRolls",
|
sb.append("\r\n").append(Localizer.getInstance().getMessage("lblIgnoredRolls",
|
||||||
StringUtils.join(ignored, ", ")));
|
StringUtils.join(ignored, ", ")));
|
||||||
@@ -149,6 +163,7 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
countMaxRolls++;
|
countMaxRolls++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sa != null) {
|
||||||
if (sa.hasParam("EvenOddResults")) {
|
if (sa.hasParam("EvenOddResults")) {
|
||||||
sa.setSVar("EvenResults", Integer.toString(evenResults));
|
sa.setSVar("EvenResults", Integer.toString(evenResults));
|
||||||
sa.setSVar("OddResults", Integer.toString(oddResults));
|
sa.setSVar("OddResults", Integer.toString(oddResults));
|
||||||
@@ -159,6 +174,7 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
if (sa.hasParam("MaxRollsResults")) {
|
if (sa.hasParam("MaxRollsResults")) {
|
||||||
sa.setSVar("MaxRolls", Integer.toString(countMaxRolls));
|
sa.setSVar("MaxRolls", Integer.toString(countMaxRolls));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
total += modifier;
|
total += modifier;
|
||||||
|
|
||||||
// Run triggers
|
// Run triggers
|
||||||
@@ -168,6 +184,7 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
runParams.put(AbilityKey.Sides, sides);
|
runParams.put(AbilityKey.Sides, sides);
|
||||||
runParams.put(AbilityKey.Modifier, modifier);
|
runParams.put(AbilityKey.Modifier, modifier);
|
||||||
runParams.put(AbilityKey.Result, roll);
|
runParams.put(AbilityKey.Result, roll);
|
||||||
|
runParams.put(AbilityKey.RolledToVisitAttractions, toVisitAttractions);
|
||||||
runParams.put(AbilityKey.Number, player.getNumRollsThisTurn() - amount + rollNum);
|
runParams.put(AbilityKey.Number, player.getNumRollsThisTurn() - amount + rollNum);
|
||||||
player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDie, runParams, false);
|
player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDie, runParams, false);
|
||||||
rollNum++;
|
rollNum++;
|
||||||
@@ -175,6 +192,7 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(player);
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(player);
|
||||||
runParams.put(AbilityKey.Sides, sides);
|
runParams.put(AbilityKey.Sides, sides);
|
||||||
runParams.put(AbilityKey.Result, rolls);
|
runParams.put(AbilityKey.Result, rolls);
|
||||||
|
runParams.put(AbilityKey.RolledToVisitAttractions, toVisitAttractions);
|
||||||
player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDieOnce, runParams, false);
|
player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDieOnce, runParams, false);
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
@@ -210,7 +228,13 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
final int ignore = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("IgnoreLower", "0"), sa);
|
final int ignore = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("IgnoreLower", "0"), sa);
|
||||||
|
|
||||||
List<Integer> rolls = new ArrayList<>();
|
List<Integer> rolls = new ArrayList<>();
|
||||||
int total = rollDiceForPlayer(sa, player, amount, sides, ignore, modifier, rolls);
|
int total = rollDiceForPlayer(sa, player, amount, sides, ignore, modifier, rolls, sa.hasParam("ToVisitYourAttractions"));
|
||||||
|
|
||||||
|
if (sa.hasParam("UseHighestRoll")) {
|
||||||
|
total = Collections.max(rolls);
|
||||||
|
} else if (sa.hasParam("UseDifferenceBetweenRolls")) {
|
||||||
|
total = Collections.max(rolls) - Collections.min(rolls);
|
||||||
|
}
|
||||||
|
|
||||||
if (sa.hasParam("StoreResults")) {
|
if (sa.hasParam("StoreResults")) {
|
||||||
host.addStoredRolls(rolls);
|
host.addStoredRolls(rolls);
|
||||||
@@ -234,9 +258,6 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
sa.setSVar(sa.getParam("OtherSVar"), Integer.toString(other));
|
sa.setSVar(sa.getParam("OtherSVar"), Integer.toString(other));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sa.hasParam("UseHighestRoll")) {
|
|
||||||
total = Collections.max(rolls);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("SubsForEach")) {
|
if (sa.hasParam("SubsForEach")) {
|
||||||
for (Integer roll : rolls) {
|
for (Integer roll : rolls) {
|
||||||
@@ -278,6 +299,9 @@ public class RollDiceEffect extends SpellAbilityEffect {
|
|||||||
} else {
|
} else {
|
||||||
int result = rollDice(sa, player, amount, sides);
|
int result = rollDice(sa, player, amount, sides);
|
||||||
results.add(result);
|
results.add(result);
|
||||||
|
if (sa.hasParam("ToVisitYourAttractions")) {
|
||||||
|
player.visitAttractions(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rememberHighest) {
|
if (rememberHighest) {
|
||||||
|
|||||||
@@ -141,12 +141,16 @@ public class SubgameEffect extends SpellAbilityEffect {
|
|||||||
// Planes
|
// Planes
|
||||||
setCardsInZone(player, ZoneType.PlanarDeck, maingamePlayer.getCardsIn(ZoneType.PlanarDeck), false);
|
setCardsInZone(player, ZoneType.PlanarDeck, maingamePlayer.getCardsIn(ZoneType.PlanarDeck), false);
|
||||||
|
|
||||||
|
// Attractions
|
||||||
|
setCardsInZone(player, ZoneType.AttractionDeck, maingamePlayer.getCardsIn(ZoneType.AttractionDeck), false);
|
||||||
|
|
||||||
// Vanguard and Commanders
|
// Vanguard and Commanders
|
||||||
initVariantsZonesSubgame(subgame, maingamePlayer, player);
|
initVariantsZonesSubgame(subgame, maingamePlayer, player);
|
||||||
|
|
||||||
player.shuffle(null);
|
player.shuffle(null);
|
||||||
player.getZone(ZoneType.SchemeDeck).shuffle();
|
player.getZone(ZoneType.SchemeDeck).shuffle();
|
||||||
player.getZone(ZoneType.PlanarDeck).shuffle();
|
player.getZone(ZoneType.PlanarDeck).shuffle();
|
||||||
|
player.getZone(ZoneType.AttractionDeck).shuffle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +253,7 @@ public class SubgameEffect extends SpellAbilityEffect {
|
|||||||
player.shuffle(sa);
|
player.shuffle(sa);
|
||||||
player.getZone(ZoneType.SchemeDeck).shuffle();
|
player.getZone(ZoneType.SchemeDeck).shuffle();
|
||||||
player.getZone(ZoneType.PlanarDeck).shuffle();
|
player.getZone(ZoneType.PlanarDeck).shuffle();
|
||||||
|
player.getZone(ZoneType.AttractionDeck).shuffle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
private int timesSaddledThisTurn = 0;
|
private int timesSaddledThisTurn = 0;
|
||||||
private CardCollection saddledByThisTurn;
|
private CardCollection saddledByThisTurn;
|
||||||
|
|
||||||
|
private boolean visitedThisTurn = false;
|
||||||
|
|
||||||
private int classLevel = 1;
|
private int classLevel = 1;
|
||||||
private long bestowTimestamp = -1;
|
private long bestowTimestamp = -1;
|
||||||
private long transformedTimestamp = 0;
|
private long transformedTimestamp = 0;
|
||||||
@@ -249,6 +251,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
private boolean isImmutable = false;
|
private boolean isImmutable = false;
|
||||||
private boolean isEmblem = false;
|
private boolean isEmblem = false;
|
||||||
private boolean isBoon = false;
|
private boolean isBoon = false;
|
||||||
|
private boolean isAttractionCard = false;
|
||||||
|
|
||||||
private int exertThisTurn = 0;
|
private int exertThisTurn = 0;
|
||||||
private PlayerCollection exertedByPlayer = new PlayerCollection();
|
private PlayerCollection exertedByPlayer = new PlayerCollection();
|
||||||
@@ -263,8 +266,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
private Table<Long, Long, Pair<Integer,Integer>> newPT = TreeBasedTable.create(); // Layer 7b
|
private Table<Long, Long, Pair<Integer,Integer>> newPT = TreeBasedTable.create(); // Layer 7b
|
||||||
private Table<Long, Long, Pair<Integer,Integer>> boostPT = TreeBasedTable.create(); // Layer 7c
|
private Table<Long, Long, Pair<Integer,Integer>> boostPT = TreeBasedTable.create(); // Layer 7c
|
||||||
|
|
||||||
private String oracleText = "";
|
|
||||||
|
|
||||||
private final Map<Card, Integer> assignedDamageMap = Maps.newTreeMap();
|
private final Map<Card, Integer> assignedDamageMap = Maps.newTreeMap();
|
||||||
private Map<Integer, Integer> damage = Maps.newHashMap();
|
private Map<Integer, Integer> damage = Maps.newHashMap();
|
||||||
private boolean hasBeenDealtDeathtouchDamage;
|
private boolean hasBeenDealtDeathtouchDamage;
|
||||||
@@ -2504,7 +2505,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
|| keyword.startsWith("Class") || keyword.startsWith("Blitz")
|
|| keyword.startsWith("Class") || keyword.startsWith("Blitz")
|
||||||
|| keyword.startsWith("Specialize") || keyword.equals("Ravenous")
|
|| keyword.startsWith("Specialize") || keyword.equals("Ravenous")
|
||||||
|| keyword.equals("For Mirrodin") || keyword.startsWith("Craft")
|
|| keyword.equals("For Mirrodin") || keyword.startsWith("Craft")
|
||||||
|| keyword.startsWith("Landwalk")) {
|
|| keyword.startsWith("Landwalk") || keyword.startsWith("Visit")) {
|
||||||
// keyword parsing takes care of adding a proper description
|
// keyword parsing takes care of adding a proper description
|
||||||
} else if (keyword.equals("Read ahead")) {
|
} else if (keyword.equals("Read ahead")) {
|
||||||
sb.append(Localizer.getInstance().getMessage("lblReadAhead")).append(" (").append(Localizer.getInstance().getMessage("lblReadAheadDesc"));
|
sb.append(Localizer.getInstance().getMessage("lblReadAhead")).append(" (").append(Localizer.getInstance().getMessage("lblReadAheadDesc"));
|
||||||
@@ -3496,7 +3497,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
public final void setCopiedPermanent(final Card c) {
|
public final void setCopiedPermanent(final Card c) {
|
||||||
if (copiedPermanent == c) { return; }
|
if (copiedPermanent == c) { return; }
|
||||||
copiedPermanent = c;
|
copiedPermanent = c;
|
||||||
currentState.getView().updateOracleText(this);
|
if(c != null)
|
||||||
|
currentState.setOracleText(c.getOracleText());
|
||||||
|
//Could fetch the card rules oracle text in an "else" clause here,
|
||||||
|
//but CardRules isn't aware of the card's state. May be better to
|
||||||
|
//just stash the original oracle text if this comes up.
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isCopiedSpell() {
|
public final boolean isCopiedSpell() {
|
||||||
@@ -4235,6 +4240,14 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
currentState.setBaseDefense(Integer.toString(n));
|
currentState.setBaseDefense(Integer.toString(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final Set<Integer> getAttractionLights() {
|
||||||
|
return currentState.getAttractionLights();
|
||||||
|
}
|
||||||
|
public final void setAttractionLights(Set<Integer> attractionLights) {
|
||||||
|
currentState.setAttractionLights(attractionLights);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public final int getBasePower() {
|
public final int getBasePower() {
|
||||||
return currentState.getBasePower();
|
return currentState.getBasePower();
|
||||||
}
|
}
|
||||||
@@ -5473,6 +5486,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
|
|
||||||
public final boolean isEquipment() { return getType().isEquipment(); }
|
public final boolean isEquipment() { return getType().isEquipment(); }
|
||||||
public final boolean isFortification() { return getType().isFortification(); }
|
public final boolean isFortification() { return getType().isFortification(); }
|
||||||
|
public final boolean isAttraction() { return getType().isAttraction(); }
|
||||||
public final boolean isCurse() { return getType().hasSubtype("Curse"); }
|
public final boolean isCurse() { return getType().hasSubtype("Curse"); }
|
||||||
public final boolean isAura() { return getType().isAura(); }
|
public final boolean isAura() { return getType().isAura(); }
|
||||||
public final boolean isShrine() { return getType().hasSubtype("Shrine"); }
|
public final boolean isShrine() { return getType().hasSubtype("Shrine"); }
|
||||||
@@ -5849,6 +5863,16 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
view.updateBoon(this);
|
view.updateBoon(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if this is physically an Attraction card with an Astrotorium card back. False otherwise.
|
||||||
|
*/
|
||||||
|
public final boolean isAttractionCard() {
|
||||||
|
return this.isAttractionCard;
|
||||||
|
}
|
||||||
|
public final void setAttractionCard(boolean isAttractionCard) {
|
||||||
|
this.isAttractionCard = isAttractionCard;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* there are easy checkers for Color. The CardUtil functions should be made
|
* there are easy checkers for Color. The CardUtil functions should be made
|
||||||
* part of the Card class, so calling out is not necessary
|
* part of the Card class, so calling out is not necessary
|
||||||
@@ -6605,6 +6629,17 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
crewedByThisTurn = crew;
|
crewedByThisTurn = crew;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void visitAttraction(Player visitor) {
|
||||||
|
this.visitedThisTurn = true;
|
||||||
|
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(this);
|
||||||
|
runParams.put(AbilityKey.Player, visitor);
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.VisitAttraction, runParams, false);
|
||||||
|
}
|
||||||
|
public final boolean wasVisitedThisTurn() {
|
||||||
|
return this.visitedThisTurn;
|
||||||
|
}
|
||||||
|
|
||||||
public final int getClassLevel() {
|
public final int getClassLevel() {
|
||||||
return classLevel;
|
return classLevel;
|
||||||
}
|
}
|
||||||
@@ -7115,6 +7150,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
resetExertedThisTurn();
|
resetExertedThisTurn();
|
||||||
resetCrewed();
|
resetCrewed();
|
||||||
resetSaddled();
|
resetSaddled();
|
||||||
|
visitedThisTurn = false;
|
||||||
resetChosenModeTurn();
|
resetChosenModeTurn();
|
||||||
resetAbilityResolvedThisTurn();
|
resetAbilityResolvedThisTurn();
|
||||||
}
|
}
|
||||||
@@ -7253,7 +7289,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
public void setRules(CardRules r) {
|
public void setRules(CardRules r) {
|
||||||
cardRules = r;
|
cardRules = r;
|
||||||
currentState.getView().updateRulesText(r, getType());
|
currentState.getView().updateRulesText(r, getType());
|
||||||
currentState.getView().updateOracleText(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCommander() {
|
public boolean isCommander() {
|
||||||
@@ -7582,15 +7617,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getOracleText() {
|
public String getOracleText() {
|
||||||
CardRules rules = cardRules;
|
return currentState.getOracleText();
|
||||||
if (copiedPermanent != null) { //return oracle text of copied permanent if applicable
|
|
||||||
rules = copiedPermanent.getRules();
|
|
||||||
}
|
}
|
||||||
return rules != null ? rules.getOracleText() : oracleText;
|
public void setOracleText(final String oracleText) {
|
||||||
}
|
currentState.setOracleText(oracleText);
|
||||||
public void setOracleText(final String oracleText0) {
|
|
||||||
oracleText = oracleText0;
|
|
||||||
currentState.getView().updateOracleText(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -44,9 +44,7 @@ import forge.item.IPaperCard;
|
|||||||
import forge.util.CardTranslation;
|
import forge.util.CardTranslation;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -190,6 +188,8 @@ public class CardFactory {
|
|||||||
c.setImageKey(originalPicture);
|
c.setImageKey(originalPicture);
|
||||||
c.setToken(cp.isToken());
|
c.setToken(cp.isToken());
|
||||||
|
|
||||||
|
c.setAttractionCard(cardRules.getType().isAttraction());
|
||||||
|
|
||||||
if (c.hasAlternateState()) {
|
if (c.hasAlternateState()) {
|
||||||
if (c.isFlipCard()) {
|
if (c.isFlipCard()) {
|
||||||
c.setState(CardStateName.Flipped, false);
|
c.setState(CardStateName.Flipped, false);
|
||||||
@@ -346,9 +346,9 @@ public class CardFactory {
|
|||||||
card.setColor(combinedColor);
|
card.setColor(combinedColor);
|
||||||
card.setType(new CardType(rules.getType()));
|
card.setType(new CardType(rules.getType()));
|
||||||
|
|
||||||
// Combined text based on Oracle text - might not be necessary, temporarily disabled.
|
// Combined text based on Oracle text - might not be necessary
|
||||||
//String combinedText = String.format("%s: %s\n%s: %s", rules.getMainPart().getName(), rules.getMainPart().getOracleText(), rules.getOtherPart().getName(), rules.getOtherPart().getOracleText());
|
String combinedText = String.format("(%s) %s\r\n\r\n(%s) %s", rules.getMainPart().getName(), rules.getMainPart().getOracleText(), rules.getOtherPart().getName(), rules.getOtherPart().getOracleText());
|
||||||
//card.setText(combinedText);
|
card.getState(CardStateName.Original).setOracleText(combinedText);
|
||||||
}
|
}
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
@@ -380,7 +380,7 @@ public class CardFactory {
|
|||||||
c.getCurrentState().setBaseLoyalty(face.getInitialLoyalty());
|
c.getCurrentState().setBaseLoyalty(face.getInitialLoyalty());
|
||||||
c.getCurrentState().setBaseDefense(face.getDefense());
|
c.getCurrentState().setBaseDefense(face.getDefense());
|
||||||
|
|
||||||
c.setOracleText(face.getOracleText());
|
c.getCurrentState().setOracleText(face.getOracleText());
|
||||||
|
|
||||||
// Super and 'middle' types should use enums.
|
// Super and 'middle' types should use enums.
|
||||||
c.setType(new CardType(face.getType()));
|
c.setType(new CardType(face.getType()));
|
||||||
@@ -396,6 +396,8 @@ public class CardFactory {
|
|||||||
c.setBaseToughnessString(face.getToughness());
|
c.setBaseToughnessString(face.getToughness());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.setAttractionLights(face.getAttractionLights());
|
||||||
|
|
||||||
// SpellPermanent only for Original State
|
// SpellPermanent only for Original State
|
||||||
if (c.getCurrentStateName() == CardStateName.Original || c.getCurrentStateName() == CardStateName.Modal || c.getCurrentStateName().toString().startsWith("Specialize")) {
|
if (c.getCurrentStateName() == CardStateName.Original || c.getCurrentStateName() == CardStateName.Modal || c.getCurrentStateName().toString().startsWith("Specialize")) {
|
||||||
// this is the "default" spell for permanents like creatures and artifacts
|
// this is the "default" spell for permanents like creatures and artifacts
|
||||||
@@ -412,6 +414,73 @@ public class CardFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CardFactoryUtil.addAbilityFactoryAbilities(c, face.getAbilities());
|
CardFactoryUtil.addAbilityFactoryAbilities(c, face.getAbilities());
|
||||||
|
|
||||||
|
if (face.hasFunctionalVariants()) {
|
||||||
|
applyFunctionalVariant(c, face);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyFunctionalVariant(Card c, ICardFace originalFace) {
|
||||||
|
String variantName = c.getPaperCard().getFunctionalVariant();
|
||||||
|
if (IPaperCard.NO_FUNCTIONAL_VARIANT.equals(variantName))
|
||||||
|
return;
|
||||||
|
ICardFace variant = originalFace.getFunctionalVariant(variantName);
|
||||||
|
if (variant == null) {
|
||||||
|
System.out.printf("Tried to apply unknown or unsupported variant - Card: \"%s\"; Variant: %s\n", originalFace.getName(), variantName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant.getVariables() != null)
|
||||||
|
for (Entry<String, String> v : variant.getVariables())
|
||||||
|
c.setSVar(v.getKey(), v.getValue());
|
||||||
|
if (variant.getReplacements() != null)
|
||||||
|
for (String r : variant.getReplacements())
|
||||||
|
c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true, c.getCurrentState()));
|
||||||
|
if (variant.getStaticAbilities() != null)
|
||||||
|
for (String s : variant.getStaticAbilities())
|
||||||
|
c.addStaticAbility(s);
|
||||||
|
if (variant.getTriggers() != null)
|
||||||
|
for (String t : variant.getTriggers())
|
||||||
|
c.addTrigger(TriggerHandler.parseTrigger(t, c, true, c.getCurrentState()));
|
||||||
|
|
||||||
|
if (variant.getKeywords() != null)
|
||||||
|
c.addIntrinsicKeywords(variant.getKeywords(), false);
|
||||||
|
|
||||||
|
if (variant.getManaCost() != ManaCost.NO_COST)
|
||||||
|
c.setManaCost(variant.getManaCost());
|
||||||
|
if (variant.getNonAbilityText() != null)
|
||||||
|
c.setText(variant.getNonAbilityText());
|
||||||
|
|
||||||
|
if (!"".equals(variant.getInitialLoyalty()))
|
||||||
|
c.getCurrentState().setBaseLoyalty(variant.getInitialLoyalty());
|
||||||
|
if (!"".equals(variant.getDefense()))
|
||||||
|
c.getCurrentState().setBaseDefense(variant.getDefense());
|
||||||
|
|
||||||
|
if (variant.getOracleText() != null)
|
||||||
|
c.getCurrentState().setOracleText(variant.getOracleText());
|
||||||
|
|
||||||
|
if (variant.getType() != null) {
|
||||||
|
for(String type : variant.getType())
|
||||||
|
c.addType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant.getColor() != null)
|
||||||
|
c.setColor(variant.getColor().getColor());
|
||||||
|
|
||||||
|
if (variant.getIntPower() != Integer.MAX_VALUE) {
|
||||||
|
c.setBasePower(variant.getIntPower());
|
||||||
|
c.setBasePowerString(variant.getPower());
|
||||||
|
}
|
||||||
|
if (variant.getIntToughness() != Integer.MAX_VALUE) {
|
||||||
|
c.setBaseToughness(variant.getIntToughness());
|
||||||
|
c.setBaseToughnessString(variant.getToughness());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant.getAttractionLights() != null)
|
||||||
|
c.setAttractionLights(variant.getAttractionLights());
|
||||||
|
|
||||||
|
if (variant.getAbilities() != null)
|
||||||
|
CardFactoryUtil.addAbilityFactoryAbilities(c, variant.getAbilities());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void copySpellAbility(SpellAbility from, SpellAbility to, final Card host, final Player p, final boolean lki, final boolean keepTextChanges) {
|
public static void copySpellAbility(SpellAbility from, SpellAbility to, final Card host, final Player p, final boolean lki, final boolean keepTextChanges) {
|
||||||
|
|||||||
@@ -1965,6 +1965,31 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
inst.addTrigger(parsedUpkeepTrig);
|
inst.addTrigger(parsedUpkeepTrig);
|
||||||
inst.addTrigger(parsedSacTrigger);
|
inst.addTrigger(parsedSacTrigger);
|
||||||
|
} else if (keyword.startsWith("Visit")) {
|
||||||
|
final String[] k = keyword.split(":");
|
||||||
|
|
||||||
|
SpellAbility sa = AbilityFactory.getAbility(card, k[1]);
|
||||||
|
String descStr = "Visit — " + sa.getDescription();
|
||||||
|
|
||||||
|
final String trigStr = "Mode$ VisitAttraction | TriggerZones$ Battlefield | ValidCard$ Card.Self" +
|
||||||
|
"| TriggerDescription$ " + descStr;
|
||||||
|
|
||||||
|
final Trigger t = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
||||||
|
t.setOverridingAbility(sa);
|
||||||
|
inst.addTrigger(t);
|
||||||
|
|
||||||
|
} else if (keyword.startsWith("Prize")) {
|
||||||
|
final String[] k = keyword.split(":");
|
||||||
|
|
||||||
|
SpellAbility sa = AbilityFactory.getAbility(card, k[1]);
|
||||||
|
String descStr = "Prize — " + sa.getDescription();
|
||||||
|
|
||||||
|
final String trigStr = "Mode$ ClaimPrize | Static$ True | TriggerZones$ Battlefield | ValidCard$ Card.Self" +
|
||||||
|
"| TriggerDescription$ " + descStr;
|
||||||
|
|
||||||
|
final Trigger t = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
||||||
|
t.setOverridingAbility(sa);
|
||||||
|
inst.addTrigger(t);
|
||||||
} else if (keyword.startsWith("Dungeon")) {
|
} else if (keyword.startsWith("Dungeon")) {
|
||||||
final List<String> abs = Arrays.asList(keyword.substring("Dungeon:".length()).split(","));
|
final List<String> abs = Arrays.asList(keyword.substring("Dungeon:".length()).split(","));
|
||||||
final Map<String, SpellAbility> saMap = new LinkedHashMap<>();
|
final Map<String, SpellAbility> saMap = new LinkedHashMap<>();
|
||||||
|
|||||||
@@ -532,6 +532,10 @@ public final class CardPredicates {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Predicate<Card> isAttractionWithLight(int light) {
|
||||||
|
return c -> c.isAttraction() && c.getAttractionLights().contains(light);
|
||||||
|
}
|
||||||
|
|
||||||
public static class Presets {
|
public static class Presets {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -768,6 +772,7 @@ public final class CardPredicates {
|
|||||||
return c.canBeDestroyed();
|
return c.canBeDestroyed();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
public static final Predicate<Card> ATTRACTIONS = Card::isAttraction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Accessors {
|
public static class Accessors {
|
||||||
|
|||||||
@@ -1947,6 +1947,10 @@ public class CardProperty {
|
|||||||
}
|
}
|
||||||
} else if (property.equals("SaddledThisTurn")) {
|
} else if (property.equals("SaddledThisTurn")) {
|
||||||
if (!hasTimestampMatch(card, source.getSaddledByThisTurn())) return false;
|
if (!hasTimestampMatch(card, source.getSaddledByThisTurn())) return false;
|
||||||
|
} else if (property.equals("VisitedThisTurn")) {
|
||||||
|
if (!card.wasVisitedThisTurn()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else if (property.equals("IsSuspected")) {
|
} else if (property.equals("IsSuspected")) {
|
||||||
if (!card.isSuspected()) {
|
if (!card.isSuspected()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package forge.game.card;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
@@ -57,6 +58,7 @@ public class CardState extends GameObject implements IHasSVars {
|
|||||||
private CardType type = new CardType(false);
|
private CardType type = new CardType(false);
|
||||||
private ManaCost manaCost = ManaCost.NO_COST;
|
private ManaCost manaCost = ManaCost.NO_COST;
|
||||||
private byte color = MagicColor.COLORLESS;
|
private byte color = MagicColor.COLORLESS;
|
||||||
|
private String oracleText = "";
|
||||||
private int basePower = 0;
|
private int basePower = 0;
|
||||||
private int baseToughness = 0;
|
private int baseToughness = 0;
|
||||||
private String basePowerString = null;
|
private String basePowerString = null;
|
||||||
@@ -64,6 +66,7 @@ public class CardState extends GameObject implements IHasSVars {
|
|||||||
private String baseLoyalty = "";
|
private String baseLoyalty = "";
|
||||||
private String baseDefense = "";
|
private String baseDefense = "";
|
||||||
private KeywordCollection intrinsicKeywords = new KeywordCollection();
|
private KeywordCollection intrinsicKeywords = new KeywordCollection();
|
||||||
|
private Set<Integer> attractionLights = null;
|
||||||
|
|
||||||
private final FCollection<SpellAbility> nonManaAbilities = new FCollection<>();
|
private final FCollection<SpellAbility> nonManaAbilities = new FCollection<>();
|
||||||
private final FCollection<SpellAbility> manaAbilities = new FCollection<>();
|
private final FCollection<SpellAbility> manaAbilities = new FCollection<>();
|
||||||
@@ -192,6 +195,15 @@ public class CardState extends GameObject implements IHasSVars {
|
|||||||
view.updateColors(card);
|
view.updateColors(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOracleText() {
|
||||||
|
return oracleText;
|
||||||
|
}
|
||||||
|
public void setOracleText(final String oracleText) {
|
||||||
|
this.oracleText = oracleText;
|
||||||
|
view.setOracleText(oracleText);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public final int getBasePower() {
|
public final int getBasePower() {
|
||||||
return basePower;
|
return basePower;
|
||||||
}
|
}
|
||||||
@@ -240,6 +252,15 @@ public class CardState extends GameObject implements IHasSVars {
|
|||||||
view.updateDefense(this);
|
view.updateDefense(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getAttractionLights() {
|
||||||
|
return this.attractionLights;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setAttractionLights(Set<Integer> attractionLights) {
|
||||||
|
this.attractionLights = attractionLights;
|
||||||
|
view.updateAttractionLights(this);
|
||||||
|
}
|
||||||
|
|
||||||
public final Collection<KeywordInterface> getCachedKeywords() {
|
public final Collection<KeywordInterface> getCachedKeywords() {
|
||||||
return cachedKeywords.getValues();
|
return cachedKeywords.getValues();
|
||||||
}
|
}
|
||||||
@@ -584,10 +605,12 @@ public class CardState extends GameObject implements IHasSVars {
|
|||||||
setType(source.type);
|
setType(source.type);
|
||||||
setManaCost(source.getManaCost());
|
setManaCost(source.getManaCost());
|
||||||
setColor(source.getColor());
|
setColor(source.getColor());
|
||||||
|
setOracleText(source.getOracleText());
|
||||||
setBasePower(source.getBasePower());
|
setBasePower(source.getBasePower());
|
||||||
setBaseToughness(source.getBaseToughness());
|
setBaseToughness(source.getBaseToughness());
|
||||||
setBaseLoyalty(source.getBaseLoyalty());
|
setBaseLoyalty(source.getBaseLoyalty());
|
||||||
setBaseDefense(source.getBaseDefense());
|
setBaseDefense(source.getBaseDefense());
|
||||||
|
setAttractionLights(source.getAttractionLights());
|
||||||
setSVars(source.getSVars());
|
setSVars(source.getSVars());
|
||||||
|
|
||||||
manaAbilities.clear();
|
manaAbilities.clear();
|
||||||
|
|||||||
@@ -583,6 +583,7 @@ public class CardView extends GameEntityView {
|
|||||||
case Graveyard:
|
case Graveyard:
|
||||||
case Flashback:
|
case Flashback:
|
||||||
case Stack:
|
case Stack:
|
||||||
|
case Junkyard:
|
||||||
//cards in these zones are visible to all
|
//cards in these zones are visible to all
|
||||||
return true;
|
return true;
|
||||||
case Exile:
|
case Exile:
|
||||||
@@ -605,6 +606,7 @@ public class CardView extends GameEntityView {
|
|||||||
return true;
|
return true;
|
||||||
case Library:
|
case Library:
|
||||||
case PlanarDeck:
|
case PlanarDeck:
|
||||||
|
case AttractionDeck:
|
||||||
//cards in these zones are hidden to all unless they specify otherwise
|
//cards in these zones are hidden to all unless they specify otherwise
|
||||||
break;
|
break;
|
||||||
case SchemeDeck:
|
case SchemeDeck:
|
||||||
@@ -808,6 +810,12 @@ public class CardView extends GameEntityView {
|
|||||||
sb.append(nonAbilityText.replaceAll("CARDNAME", getName()));
|
sb.append(nonAbilityText.replaceAll("CARDNAME", getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<Integer> attractionLights = get(TrackableProperty.AttractionLights);
|
||||||
|
if (attractionLights != null && !attractionLights.isEmpty()) {
|
||||||
|
sb.append("\r\n\r\nLights: ");
|
||||||
|
sb.append(StringUtils.join(attractionLights, ", "));
|
||||||
|
}
|
||||||
|
|
||||||
sb.append(getRemembered());
|
sb.append(getRemembered());
|
||||||
|
|
||||||
Direction chosenDirection = getChosenDirection();
|
Direction chosenDirection = getChosenDirection();
|
||||||
@@ -1023,6 +1031,8 @@ public class CardView extends GameEntityView {
|
|||||||
currentState.getView().updateKeywords(c, currentState); //update keywords even if state doesn't change
|
currentState.getView().updateKeywords(c, currentState); //update keywords even if state doesn't change
|
||||||
currentState.getView().setOriginalColors(c); //set original Colors
|
currentState.getView().setOriginalColors(c); //set original Colors
|
||||||
|
|
||||||
|
currentStateView.updateAttractionLights(currentState);
|
||||||
|
|
||||||
CardState alternateState = isSplitCard && isFaceDown() ? c.getState(CardStateName.RightSplit) : c.getAlternateState();
|
CardState alternateState = isSplitCard && isFaceDown() ? c.getState(CardStateName.RightSplit) : c.getAlternateState();
|
||||||
|
|
||||||
if (isSplitCard && isFaceDown()) {
|
if (isSplitCard && isFaceDown()) {
|
||||||
@@ -1301,8 +1311,8 @@ public class CardView extends GameEntityView {
|
|||||||
public String getOracleText() {
|
public String getOracleText() {
|
||||||
return get(TrackableProperty.OracleText);
|
return get(TrackableProperty.OracleText);
|
||||||
}
|
}
|
||||||
void updateOracleText(Card c) {
|
void setOracleText(String oracleText) {
|
||||||
set(TrackableProperty.OracleText, c.getOracleText().replace("\\n", "\r\n\r\n").trim());
|
set(TrackableProperty.OracleText, oracleText.replace("\\n", "\r\n\r\n").trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRulesText() {
|
public String getRulesText() {
|
||||||
@@ -1432,6 +1442,13 @@ public class CardView extends GameEntityView {
|
|||||||
updateDefense("0");
|
updateDefense("0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getAttractionLights() {
|
||||||
|
return get(TrackableProperty.AttractionLights);
|
||||||
|
}
|
||||||
|
void updateAttractionLights(CardState c) {
|
||||||
|
set(TrackableProperty.AttractionLights, c.getAttractionLights());
|
||||||
|
}
|
||||||
|
|
||||||
public String getSetCode() {
|
public String getSetCode() {
|
||||||
return get(TrackableProperty.SetCode);
|
return get(TrackableProperty.SetCode);
|
||||||
}
|
}
|
||||||
@@ -1718,6 +1735,9 @@ public class CardView extends GameEntityView {
|
|||||||
return false;
|
return false;
|
||||||
return Iterables.size(getType().getCoreTypes()) > 1;
|
return Iterables.size(getType().getCoreTypes()) > 1;
|
||||||
}
|
}
|
||||||
|
public boolean isAttraction() {
|
||||||
|
return getType().isAttraction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//special methods for updating card and player properties as needed and returning the new collection
|
//special methods for updating card and player properties as needed and returning the new collection
|
||||||
|
|||||||
@@ -17,10 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package forge.game.phase;
|
package forge.game.phase;
|
||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.*;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
import forge.game.*;
|
import forge.game.*;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.effects.AddTurnEffect;
|
import forge.game.ability.effects.AddTurnEffect;
|
||||||
@@ -280,12 +277,16 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
playerTurn.setSchemeInMotion(null);
|
playerTurn.setSchemeInMotion(null);
|
||||||
}
|
}
|
||||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||||
// all Saga get Lore counter at the begin of pre combat
|
// all Sagas get a Lore counter at the beginning of pre combat
|
||||||
for (Card c : playerTurn.getCardsIn(ZoneType.Battlefield)) {
|
for (Card c : playerTurn.getCardsIn(ZoneType.Battlefield)) {
|
||||||
if (c.isSaga()) {
|
if (c.isSaga()) {
|
||||||
c.addCounter(CounterEnumType.LORE, 1, playerTurn, table);
|
c.addCounter(CounterEnumType.LORE, 1, playerTurn, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// roll for attractions if we have any
|
||||||
|
if (Iterables.any(playerTurn.getCardsIn(ZoneType.Battlefield), Presets.ATTRACTIONS)) {
|
||||||
|
playerTurn.rollToVisitAttractions();
|
||||||
|
}
|
||||||
table.replaceCounterEffect(game, null, false);
|
table.replaceCounterEffect(game, null, false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import forge.game.ability.AbilityFactory;
|
|||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.ability.effects.DetachedCardEffect;
|
import forge.game.ability.effects.DetachedCardEffect;
|
||||||
|
import forge.game.ability.effects.RollDiceEffect;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
import forge.game.event.*;
|
import forge.game.event.*;
|
||||||
@@ -78,7 +79,8 @@ import java.util.Map.Entry;
|
|||||||
public class Player extends GameEntity implements Comparable<Player> {
|
public class Player extends GameEntity implements Comparable<Player> {
|
||||||
public static final List<ZoneType> ALL_ZONES = Collections.unmodifiableList(Arrays.asList(ZoneType.Battlefield,
|
public static final List<ZoneType> ALL_ZONES = Collections.unmodifiableList(Arrays.asList(ZoneType.Battlefield,
|
||||||
ZoneType.Library, ZoneType.Graveyard, ZoneType.Hand, ZoneType.Exile, ZoneType.Command, ZoneType.Ante,
|
ZoneType.Library, ZoneType.Graveyard, ZoneType.Hand, ZoneType.Exile, ZoneType.Command, ZoneType.Ante,
|
||||||
ZoneType.Sideboard, ZoneType.PlanarDeck, ZoneType.SchemeDeck, ZoneType.Merged, ZoneType.Subgame, ZoneType.None));
|
ZoneType.Sideboard, ZoneType.PlanarDeck, ZoneType.SchemeDeck, ZoneType.AttractionDeck, ZoneType.Junkyard,
|
||||||
|
ZoneType.Merged, ZoneType.Subgame, ZoneType.None));
|
||||||
|
|
||||||
private final Map<Card, Integer> commanderDamage = Maps.newHashMap();
|
private final Map<Card, Integer> commanderDamage = Maps.newHashMap();
|
||||||
|
|
||||||
@@ -2999,6 +3001,14 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
com.add(conspire);
|
com.add(conspire);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attractions
|
||||||
|
PlayerZone attractionDeck = getZone(ZoneType.AttractionDeck);
|
||||||
|
for (IPaperCard cp : registeredPlayer.getAttractions()) {
|
||||||
|
attractionDeck.add(Card.fromPaperCard(cp, this));
|
||||||
|
}
|
||||||
|
if (!attractionDeck.isEmpty())
|
||||||
|
attractionDeck.shuffle();
|
||||||
|
|
||||||
// Adventure Mode items
|
// Adventure Mode items
|
||||||
Iterable<? extends IPaperCard> adventureItemCards = registeredPlayer.getExtraCardsInCommandZone();
|
Iterable<? extends IPaperCard> adventureItemCards = registeredPlayer.getExtraCardsInCommandZone();
|
||||||
if (adventureItemCards != null) {
|
if (adventureItemCards != null) {
|
||||||
@@ -3850,6 +3860,16 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
committedCrimeThisTurn = v;
|
committedCrimeThisTurn = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void visitAttractions(int light) {
|
||||||
|
CardCollection attractions = CardLists.filter(getCardsIn(ZoneType.Battlefield), CardPredicates.isAttractionWithLight(light));
|
||||||
|
for (Card c : attractions) {
|
||||||
|
c.visitAttraction(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void rollToVisitAttractions() {
|
||||||
|
this.visitAttractions(RollDiceEffect.rollDiceForPlayerToVisitAttractions(this));
|
||||||
|
}
|
||||||
|
|
||||||
public void addDeclaresAttackers(long ts, Player p) {
|
public void addDeclaresAttackers(long ts, Player p) {
|
||||||
this.declaresAttackers.put(ts, p);
|
this.declaresAttackers.put(ts, p);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public class RegisteredPlayer {
|
|||||||
private Iterable<? extends IPaperCard> schemes = null;
|
private Iterable<? extends IPaperCard> schemes = null;
|
||||||
private Iterable<PaperCard> planes = null;
|
private Iterable<PaperCard> planes = null;
|
||||||
private Iterable<PaperCard> conspiracies = null;
|
private Iterable<PaperCard> conspiracies = null;
|
||||||
|
private Iterable<PaperCard> attractions = null;
|
||||||
private List<PaperCard> commanders = Lists.newArrayList();
|
private List<PaperCard> commanders = Lists.newArrayList();
|
||||||
private List<PaperCard> vanguardAvatars = null;
|
private List<PaperCard> vanguardAvatars = null;
|
||||||
private PaperCard planeswalker = null;
|
private PaperCard planeswalker = null;
|
||||||
@@ -231,8 +232,18 @@ public class RegisteredPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterable<PaperCard> getAttractions() {
|
||||||
|
return attractions;
|
||||||
|
}
|
||||||
|
private void assignAttractions() {
|
||||||
|
attractions = currentDeck.has(DeckSection.Attractions)
|
||||||
|
? currentDeck.get(DeckSection.Attractions).toFlatList()
|
||||||
|
: EmptyList;
|
||||||
|
}
|
||||||
|
|
||||||
public void restoreDeck() {
|
public void restoreDeck() {
|
||||||
currentDeck = (Deck) originalDeck.copyTo(originalDeck.getName());
|
currentDeck = (Deck) originalDeck.copyTo(originalDeck.getName());
|
||||||
|
assignAttractions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean useRandomFoil() {
|
public boolean useRandomFoil() {
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package forge.game.trigger;
|
||||||
|
|
||||||
|
import forge.game.ability.AbilityKey;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.util.Localizer;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class TriggerClaimPrize extends Trigger{
|
||||||
|
public TriggerClaimPrize(Map<String, String> params, Card host, boolean intrinsic) {
|
||||||
|
super(params, host, intrinsic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean performTest(Map<AbilityKey, Object> runParams) {
|
||||||
|
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Card))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||||
|
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player, AbilityKey.Card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getImportantStackObjects(SpellAbility sa) {
|
||||||
|
return Localizer.getInstance().getMessage("lblPlayer") + ": " +
|
||||||
|
sa.getTriggeringObject(AbilityKey.Player);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,10 @@ public class TriggerRolledDie extends Trigger {
|
|||||||
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
|
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (hasParam("RolledToVisitAttractions")) {
|
||||||
|
if (!(boolean) runParams.getOrDefault(AbilityKey.RolledToVisitAttractions, false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (hasParam("ValidResult")) {
|
if (hasParam("ValidResult")) {
|
||||||
String[] params = getParam("ValidResult").split(",");
|
String[] params = getParam("ValidResult").split(",");
|
||||||
int result = (int) runParams.get(AbilityKey.Result);
|
int result = (int) runParams.get(AbilityKey.Result);
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ public class TriggerRolledDieOnce extends Trigger {
|
|||||||
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
|
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (hasParam("RolledToVisitAttractions")) {
|
||||||
|
if (!(boolean) runParams.getOrDefault(AbilityKey.RolledToVisitAttractions, false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public enum TriggerType {
|
|||||||
ChangesZone(TriggerChangesZone.class),
|
ChangesZone(TriggerChangesZone.class),
|
||||||
ChangesZoneAll(TriggerChangesZoneAll.class),
|
ChangesZoneAll(TriggerChangesZoneAll.class),
|
||||||
ChaosEnsues(TriggerChaosEnsues.class),
|
ChaosEnsues(TriggerChaosEnsues.class),
|
||||||
|
ClaimPrize(TriggerClaimPrize.class),
|
||||||
Clashed(TriggerClashed.class),
|
Clashed(TriggerClashed.class),
|
||||||
ClassLevelGained(TriggerClassLevelGained.class),
|
ClassLevelGained(TriggerClassLevelGained.class),
|
||||||
CommitCrime(TriggerCommitCrime.class),
|
CommitCrime(TriggerCommitCrime.class),
|
||||||
@@ -138,6 +139,7 @@ public enum TriggerType {
|
|||||||
Unattach(TriggerUnattach.class),
|
Unattach(TriggerUnattach.class),
|
||||||
UntapAll(TriggerUntapAll.class),
|
UntapAll(TriggerUntapAll.class),
|
||||||
Untaps(TriggerUntaps.class),
|
Untaps(TriggerUntaps.class),
|
||||||
|
VisitAttraction(TriggerVisitAttraction.class),
|
||||||
Vote(TriggerVote.class);
|
Vote(TriggerVote.class);
|
||||||
|
|
||||||
private final Constructor<? extends Trigger> constructor;
|
private final Constructor<? extends Trigger> constructor;
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package forge.game.trigger;
|
||||||
|
|
||||||
|
import forge.game.ability.AbilityKey;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.util.Localizer;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public class TriggerVisitAttraction extends Trigger {
|
||||||
|
|
||||||
|
public TriggerVisitAttraction(Map<String, String> params, Card host, boolean intrinsic) {
|
||||||
|
super(params, host, intrinsic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean performTest(Map<AbilityKey, Object> runParams) {
|
||||||
|
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Card))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||||
|
//TODO: Attraction roll value? Person who caused the attraction roll?
|
||||||
|
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player, AbilityKey.Card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getImportantStackObjects(SpellAbility sa) {
|
||||||
|
return Localizer.getInstance().getMessage("lblPlayer") + ": " +
|
||||||
|
sa.getTriggeringObject(AbilityKey.Player);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,8 @@ public enum ZoneType {
|
|||||||
Merged(false, "lblBattlefieldZone"),
|
Merged(false, "lblBattlefieldZone"),
|
||||||
SchemeDeck(true, "lblSchemeDeckZone"),
|
SchemeDeck(true, "lblSchemeDeckZone"),
|
||||||
PlanarDeck(true, "lblPlanarDeckZone"),
|
PlanarDeck(true, "lblPlanarDeckZone"),
|
||||||
|
AttractionDeck(true, "lblAttractionDeckZone"),
|
||||||
|
Junkyard(false, "lblJunkyardZone"),
|
||||||
Subgame(true, "lblSubgameZone"),
|
Subgame(true, "lblSubgameZone"),
|
||||||
// ExtraHand is used for Backup Plan for temporary extra hands
|
// ExtraHand is used for Backup Plan for temporary extra hands
|
||||||
ExtraHand(true, "lblHandZone"),
|
ExtraHand(true, "lblHandZone"),
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ public enum TrackableProperty {
|
|||||||
Toughness(TrackableTypes.IntegerType),
|
Toughness(TrackableTypes.IntegerType),
|
||||||
Loyalty(TrackableTypes.StringType),
|
Loyalty(TrackableTypes.StringType),
|
||||||
Defense(TrackableTypes.StringType),
|
Defense(TrackableTypes.StringType),
|
||||||
|
AttractionLights(TrackableTypes.IntegerSetType),
|
||||||
ChangedColorWords(TrackableTypes.StringMapType),
|
ChangedColorWords(TrackableTypes.StringMapType),
|
||||||
HasChangedColors(TrackableTypes.BooleanType),
|
HasChangedColors(TrackableTypes.BooleanType),
|
||||||
ChangedTypes(TrackableTypes.StringMapType),
|
ChangedTypes(TrackableTypes.StringMapType),
|
||||||
|
|||||||
@@ -559,6 +559,34 @@ public class TrackableTypes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final TrackableType<Set<Integer>> IntegerSetType = new TrackableType<Set<Integer>>() {
|
||||||
|
@Override
|
||||||
|
public Set<Integer> getDefaultValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> deserialize(TrackableDeserializer td, Set<Integer> oldValue) {
|
||||||
|
int size = td.readInt();
|
||||||
|
if (size > 0) {
|
||||||
|
Set<Integer> set = Sets.newHashSet();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
set.add(td.readInt());
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(TrackableSerializer ts, Set<Integer> value) {
|
||||||
|
ts.write(value.size());
|
||||||
|
for (int i : value) {
|
||||||
|
ts.write(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
public static final TrackableType<Map<Integer, Integer>> IntegerMapType = new TrackableType<Map<Integer, Integer>>() {
|
public static final TrackableType<Map<Integer, Integer>> IntegerMapType = new TrackableType<Map<Integer, Integer>>() {
|
||||||
@Override
|
@Override
|
||||||
public Map<Integer, Integer> getDefaultValue() {
|
public Map<Integer, Integer> getDefaultValue() {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class MessageUtil {
|
|||||||
// These are not much related to PlayerController
|
// These are not much related to PlayerController
|
||||||
public static String formatNotificationMessage(SpellAbility sa, Player player, GameObject target, String value) {
|
public static String formatNotificationMessage(SpellAbility sa, Player player, GameObject target, String value) {
|
||||||
if (sa == null || sa.getApi() == null || sa.getHostCard() == null) {
|
if (sa == null || sa.getApi() == null || sa.getHostCard() == null) {
|
||||||
return Localizer.getInstance().getMessage("lblResultIs", value);
|
return String.valueOf(value);
|
||||||
}
|
}
|
||||||
String choser = StringUtils.capitalize(mayBeYou(player, target));
|
String choser = StringUtils.capitalize(mayBeYou(player, target));
|
||||||
switch(sa.getApi()) {
|
switch(sa.getApi()) {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
|
|||||||
private DeckController<Deck> controller;
|
private DeckController<Deck> controller;
|
||||||
private final List<DeckSection> allSections = new ArrayList<>();
|
private final List<DeckSection> allSections = new ArrayList<>();
|
||||||
private ItemPool<PaperCard> normalPool, avatarPool, planePool, schemePool, conspiracyPool,
|
private ItemPool<PaperCard> normalPool, avatarPool, planePool, schemePool, conspiracyPool,
|
||||||
commanderPool, dungeonPool;
|
commanderPool, dungeonPool, attractionPool;
|
||||||
|
|
||||||
CardManager catalogManager;
|
CardManager catalogManager;
|
||||||
CardManager deckManager;
|
CardManager deckManager;
|
||||||
@@ -131,6 +131,9 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allSections.add(DeckSection.Attractions);
|
||||||
|
attractionPool = FModel.getAttractionPool();
|
||||||
|
|
||||||
catalogManager = new CardManager(getCDetailPicture(), wantUnique, false, false);
|
catalogManager = new CardManager(getCDetailPicture(), wantUnique, false, false);
|
||||||
deckManager = new CardManager(getCDetailPicture(), false, false, false);
|
deckManager = new CardManager(getCDetailPicture(), false, false, false);
|
||||||
deckManager.setAlwaysNonUnique(true);
|
deckManager.setAlwaysNonUnique(true);
|
||||||
@@ -342,6 +345,9 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
|
|||||||
case Dungeon:
|
case Dungeon:
|
||||||
cmb.addMoveItems(localizer.getMessage("lblAdd"), localizer.getMessage("lbltodungeondeck"));
|
cmb.addMoveItems(localizer.getMessage("lblAdd"), localizer.getMessage("lbltodungeondeck"));
|
||||||
break;
|
break;
|
||||||
|
case Attractions:
|
||||||
|
cmb.addMoveItems(localizer.getMessage("lblAdd"), localizer.getMessage("lbltoattractiondeck"));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,6 +380,9 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
|
|||||||
case Dungeon:
|
case Dungeon:
|
||||||
cmb.addMoveItems(localizer.getMessage("lblRemove"), localizer.getMessage("lblfromdungeondeck"));
|
cmb.addMoveItems(localizer.getMessage("lblRemove"), localizer.getMessage("lblfromdungeondeck"));
|
||||||
break;
|
break;
|
||||||
|
case Attractions:
|
||||||
|
cmb.addMoveItems(localizer.getMessage("lblRemove"), localizer.getMessage("lblfromattractiondeck"));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (foilAvailable) {
|
if (foilAvailable) {
|
||||||
cmb.addMakeFoils();
|
cmb.addMakeFoils();
|
||||||
@@ -482,6 +491,12 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
|
|||||||
this.getCatalogManager().setAllowMultipleSelections(true);
|
this.getCatalogManager().setAllowMultipleSelections(true);
|
||||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Dungeon));
|
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Dungeon));
|
||||||
break;
|
break;
|
||||||
|
case Attractions:
|
||||||
|
this.getCatalogManager().setup(ItemManagerConfig.ATTRACTION_POOL);
|
||||||
|
this.getCatalogManager().setPool(attractionPool, true);
|
||||||
|
this.getCatalogManager().setAllowMultipleSelections(true);
|
||||||
|
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Attractions));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case Commander:
|
case Commander:
|
||||||
case Oathbreaker:
|
case Oathbreaker:
|
||||||
@@ -506,6 +521,12 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
|
|||||||
this.getCatalogManager().setAllowMultipleSelections(false);
|
this.getCatalogManager().setAllowMultipleSelections(false);
|
||||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Commander));
|
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Commander));
|
||||||
break;
|
break;
|
||||||
|
case Attractions:
|
||||||
|
this.getCatalogManager().setup(ItemManagerConfig.ATTRACTION_POOL);
|
||||||
|
this.getCatalogManager().setPool(attractionPool, true);
|
||||||
|
this.getCatalogManager().setAllowMultipleSelections(true);
|
||||||
|
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Attractions));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ public final class CEditorLimited extends CDeckEditor<DeckGroup> {
|
|||||||
|
|
||||||
allSections.add(DeckSection.Main);
|
allSections.add(DeckSection.Main);
|
||||||
allSections.add(DeckSection.Conspiracy);
|
allSections.add(DeckSection.Conspiracy);
|
||||||
|
allSections.add(DeckSection.Attractions);
|
||||||
|
|
||||||
this.getCbxSection().removeAllItems();
|
this.getCbxSection().removeAllItems();
|
||||||
for (DeckSection section : allSections) {
|
for (DeckSection section : allSections) {
|
||||||
@@ -221,6 +222,10 @@ public final class CEditorLimited extends CDeckEditor<DeckGroup> {
|
|||||||
this.getCatalogManager().setup(ItemManagerConfig.DRAFT_CONSPIRACY);
|
this.getCatalogManager().setup(ItemManagerConfig.DRAFT_CONSPIRACY);
|
||||||
this.getDeckManager().setPool(getHumanDeck().getOrCreate(DeckSection.Conspiracy));
|
this.getDeckManager().setPool(getHumanDeck().getOrCreate(DeckSection.Conspiracy));
|
||||||
break;
|
break;
|
||||||
|
case Attractions:
|
||||||
|
this.getCatalogManager().setup(ItemManagerConfig.ATTRACTION_POOL);
|
||||||
|
this.getDeckManager().setPool(getHumanDeck().getOrCreate(DeckSection.Attractions));
|
||||||
|
break;
|
||||||
case Main:
|
case Main:
|
||||||
this.getCatalogManager().setup(getScreen() == FScreen.DECK_EDITOR_DRAFT ? ItemManagerConfig.DRAFT_POOL : ItemManagerConfig.SEALED_POOL);
|
this.getCatalogManager().setup(getScreen() == FScreen.DECK_EDITOR_DRAFT ? ItemManagerConfig.DRAFT_POOL : ItemManagerConfig.SEALED_POOL);
|
||||||
this.getDeckManager().setPool(getHumanDeck().getOrCreate(DeckSection.Main));
|
this.getDeckManager().setPool(getHumanDeck().getOrCreate(DeckSection.Main));
|
||||||
|
|||||||
@@ -1408,7 +1408,7 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
|||||||
selected++;
|
selected++;
|
||||||
if (selected > 2)
|
if (selected > 2)
|
||||||
selected = 0;
|
selected = 0;
|
||||||
setSelectedPage(tabPages[selected]);
|
setSelectedPage(tabPages.get(selected));
|
||||||
if (getSelectedPage() instanceof CatalogPage) {
|
if (getSelectedPage() instanceof CatalogPage) {
|
||||||
((CatalogPage) getSelectedPage()).cardManager.getConfig().setPileBy(null);
|
((CatalogPage) getSelectedPage()).cardManager.getConfig().setPileBy(null);
|
||||||
((CatalogPage) getSelectedPage()).cardManager.setHideFilters(true);
|
((CatalogPage) getSelectedPage()).cardManager.setHideFilters(true);
|
||||||
|
|||||||
@@ -471,6 +471,7 @@ public class CardRenderer {
|
|||||||
public static void drawCardListItem(Graphics g, FSkinFont font, FSkinColor foreColor, FImageComplex cardArt, CardView card, String set, CardRarity rarity, int power, int toughness, String loyalty, int count, String suffix, float x, float y, float w, float h, boolean compactMode) {
|
public static void drawCardListItem(Graphics g, FSkinFont font, FSkinColor foreColor, FImageComplex cardArt, CardView card, String set, CardRarity rarity, int power, int toughness, String loyalty, int count, String suffix, float x, float y, float w, float h, boolean compactMode) {
|
||||||
float cardArtHeight = h + 2 * FList.PADDING;
|
float cardArtHeight = h + 2 * FList.PADDING;
|
||||||
float cardArtWidth = cardArtHeight * CARD_ART_RATIO;
|
float cardArtWidth = cardArtHeight * CARD_ART_RATIO;
|
||||||
|
CardView.CardStateView cardCurrentState = card.getCurrentState();
|
||||||
if (cardArt != null) {
|
if (cardArt != null) {
|
||||||
float artX = x - FList.PADDING;
|
float artX = x - FList.PADDING;
|
||||||
float artY = y - FList.PADDING;
|
float artY = y - FList.PADDING;
|
||||||
@@ -485,7 +486,7 @@ public class CardRenderer {
|
|||||||
g.drawRotatedImage(cardArt.getTexture(), artX, artY, cardArtHeight, cardArtWidth / 2, artX + cardArtWidth / 2, artY + cardArtWidth / 2, cardArt.getRegionX(), (int) srcY, (int) cardArt.getWidth(), (int) srcHeight, -90);
|
g.drawRotatedImage(cardArt.getTexture(), artX, artY, cardArtHeight, cardArtWidth / 2, artX + cardArtWidth / 2, artY + cardArtWidth / 2, cardArt.getRegionX(), (int) srcY, (int) cardArt.getWidth(), (int) srcHeight, -90);
|
||||||
g.drawRotatedImage(cardArt.getTexture(), artX, artY + cardArtWidth / 2, cardArtHeight, cardArtWidth / 2, artX + cardArtWidth / 2, artY + cardArtWidth / 2, cardArt.getRegionX(), (int) cardArt.getHeight() - (int) (srcY + srcHeight), (int) cardArt.getWidth(), (int) srcHeight, -90);
|
g.drawRotatedImage(cardArt.getTexture(), artX, artY + cardArtWidth / 2, cardArtHeight, cardArtWidth / 2, artX + cardArtWidth / 2, artY + cardArtWidth / 2, cardArt.getRegionX(), (int) cardArt.getHeight() - (int) (srcY + srcHeight), (int) cardArt.getWidth(), (int) srcHeight, -90);
|
||||||
} else if (card.getText().contains("Aftermath")) {
|
} else if (card.getText().contains("Aftermath")) {
|
||||||
FImageComplex secondArt = CardRenderer.getAftermathSecondCardArt(card.getCurrentState().getImageKey());
|
FImageComplex secondArt = CardRenderer.getAftermathSecondCardArt(cardCurrentState.getImageKey());
|
||||||
g.drawRotatedImage(cardArt.getTexture(), artX, artY, cardArtWidth, cardArtHeight / 2, artX + cardArtWidth, artY + cardArtHeight / 2, cardArt.getRegionX(), cardArt.getRegionY(), (int) cardArt.getWidth(), (int) cardArt.getHeight() / 2, 0);
|
g.drawRotatedImage(cardArt.getTexture(), artX, artY, cardArtWidth, cardArtHeight / 2, artX + cardArtWidth, artY + cardArtHeight / 2, cardArt.getRegionX(), cardArt.getRegionY(), (int) cardArt.getWidth(), (int) cardArt.getHeight() / 2, 0);
|
||||||
g.drawRotatedImage(secondArt.getTexture(), artX - cardArtHeight / 2, artY + cardArtHeight / 2, cardArtHeight / 2, cardArtWidth, artX, artY + cardArtHeight / 2, secondArt.getRegionX(), secondArt.getRegionY(), (int) secondArt.getWidth(), (int) secondArt.getHeight(), 90);
|
g.drawRotatedImage(secondArt.getTexture(), artX - cardArtHeight / 2, artY + cardArtHeight / 2, cardArtHeight / 2, cardArtWidth, artX, artY + cardArtHeight / 2, secondArt.getRegionX(), secondArt.getRegionY(), (int) secondArt.getWidth(), (int) secondArt.getHeight(), 90);
|
||||||
} else {
|
} else {
|
||||||
@@ -495,7 +496,7 @@ public class CardRenderer {
|
|||||||
|
|
||||||
//render card name and mana cost on first line
|
//render card name and mana cost on first line
|
||||||
float manaCostWidth = 0;
|
float manaCostWidth = 0;
|
||||||
ManaCost mainManaCost = card.getCurrentState().getManaCost();
|
ManaCost mainManaCost = cardCurrentState.getManaCost();
|
||||||
if (card.isSplitCard()) {
|
if (card.isSplitCard()) {
|
||||||
//handle rendering both parts of split card
|
//handle rendering both parts of split card
|
||||||
mainManaCost = card.getLeftSplitState().getManaCost();
|
mainManaCost = card.getLeftSplitState().getManaCost();
|
||||||
@@ -535,14 +536,17 @@ public class CardRenderer {
|
|||||||
drawSetLabel(g, typeFont, set, rarity, x + availableTypeWidth + SET_BOX_MARGIN, y - SET_BOX_MARGIN, setWidth, lineHeight + 2 * SET_BOX_MARGIN);
|
drawSetLabel(g, typeFont, set, rarity, x + availableTypeWidth + SET_BOX_MARGIN, y - SET_BOX_MARGIN, setWidth, lineHeight + 2 * SET_BOX_MARGIN);
|
||||||
}
|
}
|
||||||
String type = CardDetailUtil.formatCardType(card.getCurrentState(), true);
|
String type = CardDetailUtil.formatCardType(card.getCurrentState(), true);
|
||||||
if (card.getCurrentState().isCreature()) { //include P/T or Loyalty at end of type
|
if (cardCurrentState.isCreature()) { //include P/T or Loyalty at end of type
|
||||||
type += " (" + power + " / " + toughness + ")";
|
type += " (" + power + " / " + toughness + ")";
|
||||||
} else if (card.getCurrentState().isPlaneswalker()) {
|
} else if (cardCurrentState.isPlaneswalker()) {
|
||||||
type += " (" + loyalty + ")";
|
type += " (" + loyalty + ")";
|
||||||
} else if (card.getCurrentState().getType().hasSubtype("Vehicle")) {
|
} else if (cardCurrentState.isVehicle()) {
|
||||||
type += String.format(" [%s / %s]", power, toughness);
|
type += String.format(" [%s / %s]", power, toughness);
|
||||||
} else if (card.getCurrentState().isBattle()) {
|
} else if (cardCurrentState.isBattle()) {
|
||||||
type += " (" + card.getCurrentState().getDefense() + ")";
|
type += " (" + cardCurrentState.getDefense() + ")";
|
||||||
|
} else if (cardCurrentState.isAttraction()) {
|
||||||
|
//TODO: Probably shouldn't be non-localized text here? Not sure what to do if someone makes an attraction with no lights...
|
||||||
|
type += " (" + (cardCurrentState.getAttractionLights().isEmpty() ? "No Lights" : StringUtils.join(cardCurrentState.getAttractionLights(), ", ")) + ")";
|
||||||
}
|
}
|
||||||
g.drawText(type, typeFont, foreColor, x, y, availableTypeWidth, lineHeight, false, Align.left, true);
|
g.drawText(type, typeFont, foreColor, x, y, availableTypeWidth, lineHeight, false, Align.left, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class GameEntityPicker extends TabPageScreen<GameEntityPicker> {
|
|||||||
@Override
|
@Override
|
||||||
public void run(Integer result) {
|
public void run(Integer result) {
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
callback.run(((PickerTab)tabPages[0]).list.getSelectedItem());
|
callback.run(((PickerTab) tabPages.get(0)).list.getSelectedItem());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
callback.run(null);
|
callback.run(null);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.Forge;
|
import forge.Forge;
|
||||||
import forge.Graphics;
|
import forge.Graphics;
|
||||||
@@ -81,6 +82,7 @@ public class FDeckImportDialog extends FDialog {
|
|||||||
supportedSections.add(DeckSection.Sideboard);
|
supportedSections.add(DeckSection.Sideboard);
|
||||||
if (editorType != FDeckEditor.EditorType.Constructed)
|
if (editorType != FDeckEditor.EditorType.Constructed)
|
||||||
supportedSections.add(DeckSection.Commander);
|
supportedSections.add(DeckSection.Commander);
|
||||||
|
supportedSections.addAll(Lists.newArrayList(FDeckEditor.getExtraSections(editorType)));
|
||||||
controller.setAllowedSections(supportedSections);
|
controller.setAllowedSections(supportedSections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,16 +60,16 @@ public class FSideboardDialog extends FDialog {
|
|||||||
new SideboardPage(sideboard),
|
new SideboardPage(sideboard),
|
||||||
new MainDeckPage(main)
|
new MainDeckPage(main)
|
||||||
}, false);
|
}, false);
|
||||||
((SideboardPage)tabPages[0]).parent = this;
|
((SideboardPage) tabPages.get(0)).parent = this;
|
||||||
((MainDeckPage)tabPages[1]).parent = this;
|
((MainDeckPage) tabPages.get(1)).parent = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SideboardPage getSideboardPage() {
|
private SideboardPage getSideboardPage() {
|
||||||
return ((SideboardPage)tabPages[0]);
|
return ((SideboardPage) tabPages.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MainDeckPage getMainDeckPage() {
|
private MainDeckPage getMainDeckPage() {
|
||||||
return ((MainDeckPage)tabPages[1]);
|
return ((MainDeckPage) tabPages.get(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
|
|||||||
* ItemManager Constructor.
|
* ItemManager Constructor.
|
||||||
*
|
*
|
||||||
* @param genericType0 the class of item that this table will contain
|
* @param genericType0 the class of item that this table will contain
|
||||||
* @param statLabels0 stat labels for this item manager
|
|
||||||
* @param wantUnique0 whether this table should display only one item with the same name
|
* @param wantUnique0 whether this table should display only one item with the same name
|
||||||
*/
|
*/
|
||||||
protected ItemManager(final Class<T> genericType0, final boolean wantUnique0) {
|
protected ItemManager(final Class<T> genericType0, final boolean wantUnique0) {
|
||||||
@@ -749,6 +748,33 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void applyAdvancedSearchFilter(String filterString) {
|
||||||
|
applyAdvancedSearchFilter(new String[]{filterString}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Programmatic method to set this ItemManager's advanced search filter value.
|
||||||
|
* Other filters will be cleared.
|
||||||
|
*/
|
||||||
|
public void applyAdvancedSearchFilter(String[] filterStrings, boolean joinAnd) {
|
||||||
|
if(advancedSearchFilter == null) {
|
||||||
|
advancedSearchFilter = createAdvancedSearchFilter();
|
||||||
|
ItemManager.this.add(advancedSearchFilter.getWidget());
|
||||||
|
}
|
||||||
|
lockFiltering = true;
|
||||||
|
for (final ItemFilter<? extends T> filter : filters) {
|
||||||
|
filter.reset();
|
||||||
|
}
|
||||||
|
searchFilter.reset();
|
||||||
|
advancedSearchFilter.reset();
|
||||||
|
advancedSearchFilter.setFilterParts(filterStrings, joinAnd);
|
||||||
|
lockFiltering = false;
|
||||||
|
|
||||||
|
applyFilters();
|
||||||
|
advancedSearchFilter.refreshWidget();
|
||||||
|
revalidate();
|
||||||
|
}
|
||||||
|
|
||||||
//Refresh displayed items
|
//Refresh displayed items
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
updateView(true, getSelectedItems());
|
updateView(true, getSelectedItems());
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.google.common.collect.Iterables;
|
|||||||
import forge.Forge;
|
import forge.Forge;
|
||||||
import forge.assets.FSkinImage;
|
import forge.assets.FSkinImage;
|
||||||
import forge.assets.TextRenderer;
|
import forge.assets.TextRenderer;
|
||||||
|
import forge.gui.GuiBase;
|
||||||
import forge.gui.interfaces.IButton;
|
import forge.gui.interfaces.IButton;
|
||||||
import forge.item.InventoryItem;
|
import forge.item.InventoryItem;
|
||||||
import forge.itemmanager.AdvancedSearch;
|
import forge.itemmanager.AdvancedSearch;
|
||||||
@@ -71,6 +72,34 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
|||||||
editScreen = null;
|
editScreen = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFilterParts(final String[] items, boolean joinAnd) {
|
||||||
|
//This could be made more robust, processing "and"s, "or"s, and parentheses to fully configure the filter,
|
||||||
|
//but that'll have to wait until there's a use case for it.
|
||||||
|
//This could also probably be moved up to the interface and shared with the desktop version,
|
||||||
|
//but again, can't think of a use case at the moment.
|
||||||
|
this.reset();
|
||||||
|
editScreen = new EditScreen();
|
||||||
|
EditScreen.Filter currFilter = this.editScreen.getNewestFilter();
|
||||||
|
for (int i = 0; i < items.length; i++) {
|
||||||
|
String filterText = items[i];
|
||||||
|
AdvancedSearch.Filter<T> filter = AdvancedSearch.getFilter(itemManager.getGenericType(), filterText);
|
||||||
|
if(filter == null)
|
||||||
|
continue;
|
||||||
|
currFilter.setFilter(filter);
|
||||||
|
currFilter.getBtnFilter().setText(GuiBase.getInterface().encodeSymbols(filter.toString(), false));
|
||||||
|
if(i < items.length - 1) {
|
||||||
|
if (joinAnd)
|
||||||
|
currFilter.btnAnd.setSelected(true);
|
||||||
|
else
|
||||||
|
currFilter.btnOr.setSelected(true);
|
||||||
|
this.editScreen.addNewFilter(currFilter);
|
||||||
|
currFilter = this.editScreen.getNewestFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onFilterChange.run();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void buildWidget(Widget widget) {
|
protected void buildWidget(Widget widget) {
|
||||||
label = new FiltersLabel();
|
label = new FiltersLabel();
|
||||||
@@ -203,6 +232,11 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
|||||||
scroller.setBounds(0, startY, width, height - startY);
|
scroller.setBounds(0, startY, width, height - startY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") //Nothing except Filters are ever added to this FScrollPane.
|
||||||
|
private Filter getNewestFilter() {
|
||||||
|
return (Filter) scroller.getChildAt(scroller.getChildCount() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
private void addNewFilter(Filter fromFilter) {
|
private void addNewFilter(Filter fromFilter) {
|
||||||
if (scroller.getChildAt(scroller.getChildCount() - 1) == fromFilter) {
|
if (scroller.getChildAt(scroller.getChildCount() - 1) == fromFilter) {
|
||||||
Filter filter = new Filter();
|
Filter filter = new Filter();
|
||||||
|
|||||||
@@ -18,11 +18,14 @@ import forge.toolbox.FLabel;
|
|||||||
import forge.toolbox.FScrollPane;
|
import forge.toolbox.FScrollPane;
|
||||||
import forge.util.Utils;
|
import forge.util.Utils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class TabPageScreen<T extends TabPageScreen<T>> extends FScreen {
|
public class TabPageScreen<T extends TabPageScreen<T>> extends FScreen {
|
||||||
public static boolean COMPACT_TABS = FModel.getPreferences().getPrefBoolean(FPref.UI_COMPACT_TABS);
|
public static boolean COMPACT_TABS = FModel.getPreferences().getPrefBoolean(FPref.UI_COMPACT_TABS);
|
||||||
|
|
||||||
protected final TabHeader<T> tabHeader;
|
protected final TabHeader<T> tabHeader;
|
||||||
protected final TabPage<T>[] tabPages;
|
protected final List<TabPage<T>> tabPages;
|
||||||
private TabPage<T> selectedPage;
|
private TabPage<T> selectedPage;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -72,7 +75,17 @@ public class TabPageScreen<T extends TabPageScreen<T>> extends FScreen {
|
|||||||
add(tabPage);
|
add(tabPage);
|
||||||
tabPage.setVisible(false);
|
tabPage.setVisible(false);
|
||||||
}
|
}
|
||||||
setSelectedPage(tabPages[0]);
|
setSelectedPage(tabPages.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void addTabPage(TabPage<T> tabPage) {
|
||||||
|
tabHeader.addTab(tabPage);
|
||||||
|
tabPage.index = tabPages.size();
|
||||||
|
tabPage.parentScreen = (T) this;
|
||||||
|
add(tabPage);
|
||||||
|
tabPage.setVisible(false);
|
||||||
|
this.revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TabPage<T> getSelectedPage() {
|
public TabPage<T> getSelectedPage() {
|
||||||
@@ -135,7 +148,7 @@ public class TabPageScreen<T extends TabPageScreen<T>> extends FScreen {
|
|||||||
private static final float BACK_BUTTON_WIDTH = Math.round(HEIGHT / 2);
|
private static final float BACK_BUTTON_WIDTH = Math.round(HEIGHT / 2);
|
||||||
private static final FSkinColor SEPARATOR_COLOR = getBackColor().stepColor(-40);
|
private static final FSkinColor SEPARATOR_COLOR = getBackColor().stepColor(-40);
|
||||||
|
|
||||||
private final TabPage<T>[] tabPages;
|
private final List<TabPage<T>> tabPages = new ArrayList<>();
|
||||||
public final FLabel btnBack;
|
public final FLabel btnBack;
|
||||||
private boolean isScrollable;
|
private boolean isScrollable;
|
||||||
private FDisplayObject finalVisibleTab;
|
private FDisplayObject finalVisibleTab;
|
||||||
@@ -184,8 +197,7 @@ public class TabPageScreen<T extends TabPageScreen<T>> extends FScreen {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
public TabHeader(TabPage<T>[] tabPages0, boolean showBackButton) {
|
public TabHeader(TabPage<T>[] tabPages, boolean showBackButton) {
|
||||||
tabPages = tabPages0;
|
|
||||||
if (showBackButton) {
|
if (showBackButton) {
|
||||||
btnBack = add(new FLabel.Builder().icon(new BackIcon(BACK_BUTTON_WIDTH, BACK_BUTTON_WIDTH)).pressedColor(getBtnPressedColor()).align(Align.center).command(e -> Forge.back()).build());
|
btnBack = add(new FLabel.Builder().icon(new BackIcon(BACK_BUTTON_WIDTH, BACK_BUTTON_WIDTH)).pressedColor(getBtnPressedColor()).align(Align.center).command(e -> Forge.back()).build());
|
||||||
}
|
}
|
||||||
@@ -194,11 +206,11 @@ public class TabPageScreen<T extends TabPageScreen<T>> extends FScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (TabPage<T> tabPage : tabPages) {
|
for (TabPage<T> tabPage : tabPages) {
|
||||||
scroller.add(tabPage.tab);
|
this.tabPages.add(tabPage);
|
||||||
|
this.scroller.add(tabPage.tab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public TabHeader(TabPage<T>[] tabPages0, FEventHandler backButton) {
|
public TabHeader(TabPage<T>[] tabPages, FEventHandler backButton) {
|
||||||
tabPages = tabPages0;
|
|
||||||
if(backButton==null) {
|
if(backButton==null) {
|
||||||
btnBack = add(new FLabel.Builder().icon(new BackIcon(BACK_BUTTON_WIDTH, BACK_BUTTON_WIDTH)).pressedColor(getBtnPressedColor()).align(Align.center).command(e -> Forge.back()).build());
|
btnBack = add(new FLabel.Builder().icon(new BackIcon(BACK_BUTTON_WIDTH, BACK_BUTTON_WIDTH)).pressedColor(getBtnPressedColor()).align(Align.center).command(e -> Forge.back()).build());
|
||||||
}
|
}
|
||||||
@@ -208,16 +220,24 @@ public class TabPageScreen<T extends TabPageScreen<T>> extends FScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (TabPage<T> tabPage : tabPages) {
|
for (TabPage<T> tabPage : tabPages) {
|
||||||
scroller.add(tabPage.tab);
|
this.tabPages.add(tabPage);
|
||||||
|
this.scroller.add(tabPage.tab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addTab(TabPage<T> tabPage) {
|
||||||
|
this.tabPages.add(tabPage);
|
||||||
|
this.scroller.add(tabPage.tab);
|
||||||
|
this.scroller.revalidate();
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean showBackButtonInLandscapeMode() {
|
protected boolean showBackButtonInLandscapeMode() {
|
||||||
return btnBack != null;
|
return btnBack != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getPreferredHeight() {
|
public float getPreferredHeight() {
|
||||||
return tabPages[0].parentScreen.showCompactTabs() ? COMPACT_HEIGHT : HEIGHT;
|
return tabPages.get(0).parentScreen.showCompactTabs() ? COMPACT_HEIGHT : HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -262,7 +282,7 @@ public class TabPageScreen<T extends TabPageScreen<T>> extends FScreen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
btnBack.setIconScaleAuto(tabPages[0].parentScreen.showCompactTabs());
|
btnBack.setIconScaleAuto(tabPages.get(0).parentScreen.showCompactTabs());
|
||||||
btnBack.setSize(BACK_BUTTON_WIDTH, height);
|
btnBack.setSize(BACK_BUTTON_WIDTH, height);
|
||||||
x += BACK_BUTTON_WIDTH;
|
x += BACK_BUTTON_WIDTH;
|
||||||
}
|
}
|
||||||
@@ -326,12 +346,12 @@ public class TabPageScreen<T extends TabPageScreen<T>> extends FScreen {
|
|||||||
//switch to next/previous tab page when flung left or right
|
//switch to next/previous tab page when flung left or right
|
||||||
if (Math.abs(velocityX) > Math.abs(velocityY)) {
|
if (Math.abs(velocityX) > Math.abs(velocityY)) {
|
||||||
if (velocityX < 0) {
|
if (velocityX < 0) {
|
||||||
if (index < parentScreen.tabPages.length - 1) {
|
if (index < parentScreen.tabPages.size() - 1) {
|
||||||
parentScreen.setSelectedPage(parentScreen.tabPages[index + 1]);
|
parentScreen.setSelectedPage(parentScreen.tabPages.get(index + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (index > 0) {
|
else if (index > 0) {
|
||||||
parentScreen.setSelectedPage(parentScreen.tabPages[index - 1]);
|
parentScreen.setSelectedPage(parentScreen.tabPages.get(index - 1));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -352,16 +372,16 @@ public class TabPageScreen<T extends TabPageScreen<T>> extends FScreen {
|
|||||||
|
|
||||||
if (!b0 && parentScreen.getSelectedPage() == TabPage.this) {
|
if (!b0 && parentScreen.getSelectedPage() == TabPage.this) {
|
||||||
//select next page if this page is hidden
|
//select next page if this page is hidden
|
||||||
for (int i = index + 1; i < parentScreen.tabPages.length; i++) {
|
for (int i = index + 1; i < parentScreen.tabPages.size(); i++) {
|
||||||
if (parentScreen.tabPages[i].tab.isVisible()) {
|
if (parentScreen.tabPages.get(i).tab.isVisible()) {
|
||||||
parentScreen.setSelectedPage(parentScreen.tabPages[i]);
|
parentScreen.setSelectedPage(parentScreen.tabPages.get(i));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//select previous page if selecting next page is not possible
|
//select previous page if selecting next page is not possible
|
||||||
for (int i = index - 1; i >= 0; i--) {
|
for (int i = index - 1; i >= 0; i--) {
|
||||||
if (parentScreen.tabPages[i].tab.isVisible()) {
|
if (parentScreen.tabPages.get(i).tab.isVisible()) {
|
||||||
parentScreen.setSelectedPage(parentScreen.tabPages[i]);
|
parentScreen.setSelectedPage(parentScreen.tabPages.get(i));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class ConquestCollectionScreen extends TabPageScreen<ConquestCollectionSc
|
|||||||
FThreads.invokeInBackgroundThread(new Runnable() {
|
FThreads.invokeInBackgroundThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (getSelectedPage() == tabPages[0]) {
|
if (getSelectedPage() == tabPages.get(0)) {
|
||||||
int value = 0;
|
int value = 0;
|
||||||
for (PaperCard card : cards) {
|
for (PaperCard card : cards) {
|
||||||
value += ConquestUtil.getShardValue(card, CQPref.AETHER_BASE_EXILE_VALUE);
|
value += ConquestUtil.getShardValue(card, CQPref.AETHER_BASE_EXILE_VALUE);
|
||||||
@@ -143,7 +143,7 @@ public class ConquestCollectionScreen extends TabPageScreen<ConquestCollectionSc
|
|||||||
String caption;
|
String caption;
|
||||||
CQPref baseValuePref;
|
CQPref baseValuePref;
|
||||||
Collection<PaperCard> cards;
|
Collection<PaperCard> cards;
|
||||||
if (getSelectedPage() == tabPages[0]) {
|
if (getSelectedPage() == tabPages.get(0)) {
|
||||||
caption = Forge.getLocalizer().getMessage("lblExile");
|
caption = Forge.getLocalizer().getMessage("lblExile");
|
||||||
baseValuePref = CQPref.AETHER_BASE_EXILE_VALUE;
|
baseValuePref = CQPref.AETHER_BASE_EXILE_VALUE;
|
||||||
cards = getCollectionTab().list.getSelectedItems();
|
cards = getCollectionTab().list.getSelectedItems();
|
||||||
@@ -172,11 +172,11 @@ public class ConquestCollectionScreen extends TabPageScreen<ConquestCollectionSc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CollectionTab getCollectionTab() {
|
private CollectionTab getCollectionTab() {
|
||||||
return (CollectionTab)tabPages[0];
|
return (CollectionTab) tabPages.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CollectionTab getExileTab() {
|
private CollectionTab getExileTab() {
|
||||||
return (CollectionTab)tabPages[1];
|
return (CollectionTab) tabPages.get(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ public class QuestSpellShopScreen extends TabPageScreen<QuestSpellShopScreen> {
|
|||||||
|
|
||||||
public QuestSpellShopScreen() {
|
public QuestSpellShopScreen() {
|
||||||
super("", QuestMenu.getMenu(), new SpellShopBasePage[] { new SpellShopPage(), new InventoryPage() }, true);
|
super("", QuestMenu.getMenu(), new SpellShopBasePage[] { new SpellShopPage(), new InventoryPage() }, true);
|
||||||
spellShopPage = ((SpellShopPage)tabPages[0]);
|
spellShopPage = ((SpellShopPage) tabPages.get(0));
|
||||||
inventoryPage = ((InventoryPage)tabPages[1]);
|
inventoryPage = ((InventoryPage) tabPages.get(1));
|
||||||
|
|
||||||
btnBuySellMultiple.setVisible(false); //hide unless in multi-select mode
|
btnBuySellMultiple.setVisible(false); //hide unless in multi-select mode
|
||||||
btnBuySellMultiple.setCommand(event -> {
|
btnBuySellMultiple.setCommand(event -> {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class SettingsScreen extends TabPageScreen<SettingsScreen> {
|
|||||||
return !fromHomeScreen; //don't show back button if launched from home screen
|
return !fromHomeScreen; //don't show back button if launched from home screen
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
settingsPage = (SettingsPage) tabPages[0];
|
settingsPage = (SettingsPage) tabPages.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FScreen getLandscapeBackdropScreen() {
|
public FScreen getLandscapeBackdropScreen() {
|
||||||
|
|||||||
11
forge-gui/res/cardsfolder/b/bounce_chamber.txt
Normal file
11
forge-gui/res/cardsfolder/b/bounce_chamber.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Name:Bounce Chamber
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 6
|
||||||
|
Variant:B:Lights:3 6
|
||||||
|
Variant:C:Lights:4 6
|
||||||
|
Variant:D:Lights:5 6
|
||||||
|
K:Visit:TrigChoose
|
||||||
|
SVar:TrigChoose:DB$ ChooseCard | Choices$ Creature.YouDontCtrl+leastToughnessControlledByOpponent | ChoiceTitle$ Choose a creature with the least toughness among creatures your opponents control | Mandatory$ True | SubAbility$ DBBounce | SpellDescription$ Return a creature you don’t control with the lowest toughness among creatures you don’t control to its owner’s hand.
|
||||||
|
SVar:DBBounce:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Hand
|
||||||
|
Oracle:Visit — Return a creature you don’t control with the lowest toughness among creatures you don’t control to its owner’s hand. (If multiple creatures are tied, choose any one of them.)
|
||||||
10
forge-gui/res/cardsfolder/c/clown_extruder.txt
Normal file
10
forge-gui/res/cardsfolder/c/clown_extruder.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Clown Extruder
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 6
|
||||||
|
Variant:B:Lights:3 6
|
||||||
|
Variant:C:Lights:4 6
|
||||||
|
Variant:D:Lights:5 6
|
||||||
|
K:Visit:TrigToken
|
||||||
|
SVar:TrigToken:DB$ Token | TokenScript$ w_1_1_a_clown_robot | TokenOwner$ You | SpellDescription$ Create a 1/1 white Clown Robot artifact creature token.
|
||||||
|
Oracle:Visit — Create a 1/1 white Clown Robot artifact creature token.
|
||||||
10
forge-gui/res/cardsfolder/c/concession_stand.txt
Normal file
10
forge-gui/res/cardsfolder/c/concession_stand.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Concession Stand
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 6
|
||||||
|
Variant:B:Lights:3 6
|
||||||
|
Variant:C:Lights:4 6
|
||||||
|
Variant:D:Lights:5 6
|
||||||
|
K:Visit:TrigFood
|
||||||
|
SVar:TrigFood:DB$ Token | TokenScript$ c_a_food_sac | TokenOwner$ You | SpellDescription$ Create a Food token.
|
||||||
|
Oracle:Visit — Create a Food token. (It’s an artifact with “{2}, {T}, Sacrifice this artifact: You gain 3 life.”)
|
||||||
11
forge-gui/res/cardsfolder/e/everythingamajig.txt
Normal file
11
forge-gui/res/cardsfolder/e/everythingamajig.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Name:Everythingamajig
|
||||||
|
ManaCost:5
|
||||||
|
Types:Artifact
|
||||||
|
Variant:C:A:AB$ FlipACoin | Cost$ 1 | WinSubAbility$ DBAddMana | InstantSpeed$ True | SpellDescription$ Flip a coin. If you win the flip, add {C}{C}.
|
||||||
|
Variant:C:SVar:DBAddMana:DB$ Mana | Produced$ C | Amount$ 2
|
||||||
|
Variant:C:A:AB$ Discard | Cost$ 3 T | ValidTgts$ Player | NumCards$ 1 | Mode$ TgtChoose | PlayerTurn$ True | SpellDescription$ Target player discards a card.
|
||||||
|
Variant:C:A:AB$ Animate | Cost$ X | Defined$ Self | Power$ X | Toughness$ X | Types$ Creature,Artifact,Construct | RemoveCreatureTypes$ True | SpellDescription$ CARDNAME becomes an X/X Construct artifact creature until end of turn.
|
||||||
|
Variant:C:SVar:X:Count$xPaid
|
||||||
|
AI:RemoveDeck:All
|
||||||
|
Oracle:<Unsupported Variant>
|
||||||
|
Variant:C:Oracle:{1}: Flip a coin. If you win the flip, add {C}{C}. Activate only as an instant.\n{3}, {T}: Target player discards a card. Activate only during your turn.\n{X}: Everythingamajig becomes an X/X Construct artifact creature until end of turn.
|
||||||
12
forge-gui/res/cardsfolder/f/foam_weapons_kiosk.txt
Normal file
12
forge-gui/res/cardsfolder/f/foam_weapons_kiosk.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Name:Foam Weapons Kiosk
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 6
|
||||||
|
Variant:B:Lights:3 6
|
||||||
|
Variant:C:Lights:4 6
|
||||||
|
Variant:D:Lights:5 6
|
||||||
|
K:Visit:TrigCounter
|
||||||
|
SVar:TrigCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on target creature you control. It gains Vigilance until end of turn.
|
||||||
|
SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Vigilance
|
||||||
|
DeckHas:Ability$Counters
|
||||||
|
Oracle:Visit — Put a +1/+1 counter on target creature you control. That creature gains vigilance until end of turn.
|
||||||
12
forge-gui/res/cardsfolder/f/fortune_teller.txt
Normal file
12
forge-gui/res/cardsfolder/f/fortune_teller.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Name:Fortune Teller
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 3 6
|
||||||
|
Variant:B:Lights:2 4 6
|
||||||
|
Variant:C:Lights:2 5 6
|
||||||
|
Variant:D:Lights:3 4 6
|
||||||
|
Variant:E:Lights:3 5 6
|
||||||
|
Variant:F:Lights:4 5 6
|
||||||
|
K:Visit:TrigScry
|
||||||
|
SVar:TrigScry:DB$ Scry | ScryNum$ 1 | SpellDescription$ Scry 1.
|
||||||
|
Oracle:Visit — Scry 1.
|
||||||
16
forge-gui/res/cardsfolder/g/garbage_elemental.txt
Normal file
16
forge-gui/res/cardsfolder/g/garbage_elemental.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
Name:Garbage Elemental
|
||||||
|
ManaCost:4 R
|
||||||
|
Types:Creature Elemental
|
||||||
|
Variant:C:PT:3/2
|
||||||
|
Variant:C:K:Battle cry
|
||||||
|
Variant:C:T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRoll | TriggerDescription$ When CARDNAME enters the battlefield, roll two six-sided dice. Create a number of 1/1 red Goblin creature tokens equal to the difference between those results.
|
||||||
|
Variant:C:SVar:TrigRoll:DB$ RollDice | ResultSVar$ Result | Sides$ 6 | Amount$ 2 | UseDifferenceBetweenRolls$ True | SubAbility$ DBToken
|
||||||
|
Variant:C:SVar:DBToken:DB$ Token | TokenScript$ r_1_1_goblin | TokenAmount$ Result
|
||||||
|
Variant:D:PT:3/3
|
||||||
|
Variant:D:K:Cascade
|
||||||
|
Variant:D:T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRoll | TriggerDescription$ When CARDNAME enters the battlefield, roll a six-sided die. CARDNAME deals damage equal to the result to target opponent or planeswalker.
|
||||||
|
Variant:D:SVar:TrigRoll:DB$ RollDice | ResultSVar$ Result | SubAbility$ DBDamage
|
||||||
|
Variant:D:SVar:DBDamage:DB$ DealDamage | ValidTgts$ Opponent,Planeswalker | TgtPrompt$ Select target opponent or planeswalker | NumDmg$ Result
|
||||||
|
Oracle:<Unsupported Variant>
|
||||||
|
Variant:C:Oracle:Battle cry (Whenever this creature attacks, each other attacking creature gets +1/+0 until end of turn.)\nWhen Garbage Elemental enters the battlefield, roll two six-sided dice. Create a number of 1/1 red Goblin creature tokens equal to the difference between those results.
|
||||||
|
Variant:D:Oracle:Cascade (When you cast this spell, exile cards from the top of your library until you exile a nonland card that costs less. You may cast it without paying its mana cost. Put the exiled cards on the bottom of your library in a random order.)\nWhen Garbage Elemental enters the battlefield, roll a six-sided die. Garbage Elemental deals damage equal to the result to target opponent or planeswalker.
|
||||||
9
forge-gui/res/cardsfolder/h/haunted_house.txt
Normal file
9
forge-gui/res/cardsfolder/h/haunted_house.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Name:Haunted House
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:3 6
|
||||||
|
Variant:B:Lights:4 6
|
||||||
|
K:Visit:TrigRes
|
||||||
|
SVar:TrigRes:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn | TgtPrompt$ Choose target creature in your graveyard. | SubAbility$ DBHaste | SpellDescription$ Return target creature card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step.
|
||||||
|
SVar:DBHaste:DB$ Animate | Defined$ Targeted | Keywords$ Haste | Duration$ Permanent | AtEOT$ YourExile
|
||||||
|
Oracle:Visit — Return target creature card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of your next end step.
|
||||||
10
forge-gui/res/cardsfolder/i/information_booth.txt
Normal file
10
forge-gui/res/cardsfolder/i/information_booth.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Information Booth
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 6
|
||||||
|
Variant:B:Lights:3 6
|
||||||
|
Variant:C:Lights:4 6
|
||||||
|
Variant:D:Lights:5 6
|
||||||
|
K:Visit:TrigDraw
|
||||||
|
SVar:TrigDraw:DB$ Draw | SpellDescription$ Draw a card.
|
||||||
|
Oracle:Visit — Draw a card.
|
||||||
12
forge-gui/res/cardsfolder/k/kiddie_coaster.txt
Normal file
12
forge-gui/res/cardsfolder/k/kiddie_coaster.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Name:Kiddie Coaster
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 3 6
|
||||||
|
Variant:B:Lights:2 4 6
|
||||||
|
Variant:C:Lights:2 5 6
|
||||||
|
Variant:D:Lights:3 4 6
|
||||||
|
Variant:E:Lights:3 5 6
|
||||||
|
Variant:F:Lights:4 5 6
|
||||||
|
K:Visit:TrigPump
|
||||||
|
SVar:TrigPump:DB$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +1 | SpellDescription$ Creatures you control get +1/+0 until end of turn.
|
||||||
|
Oracle:Visit — Creatures you control get +1/+0 until end of turn.
|
||||||
10
forge-gui/res/cardsfolder/l/lifetime_pass_holder.txt
Normal file
10
forge-gui/res/cardsfolder/l/lifetime_pass_holder.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:"Lifetime" Pass Holder
|
||||||
|
ManaCost:B
|
||||||
|
Types:Creature Zombie Guest
|
||||||
|
PT:2/1
|
||||||
|
K:CARDNAME enters the battlefield tapped.
|
||||||
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigOpenAttraction | TriggerDescription$ When CARDNAME dies, open an Attraction.
|
||||||
|
SVar:TrigOpenAttraction:DB$ OpenAttraction
|
||||||
|
T:Mode$ RolledDie | TriggerZones$ Graveyard | Execute$ TrigReturn | ValidResult$ 6 | RolledToVisitAttractions$ True | ValidPlayer$ You | TriggerDescription$ Whenever you roll to visit your Attractions, if you roll a 6, you may return CARDNAME from your graveyard to the battlefield.
|
||||||
|
SVar:TrigReturn:DB$ ChangeZone | Defined$ Self | Origin$ Graveyard | Destination$ Battlefield
|
||||||
|
Oracle:"Lifetime" Pass Holder enters the battlefield tapped.\nWhen "Lifetime" Pass Holder dies, open an Attraction.\nWhenever you roll to visit your Attractions, if you roll a 6, you may return "Lifetime" Pass Holder from your graveyard to the battlefield.
|
||||||
8
forge-gui/res/cardsfolder/m/merry_go_round.txt
Normal file
8
forge-gui/res/cardsfolder/m/merry_go_round.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Name:Merry-Go-Round
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 6
|
||||||
|
Variant:B:Lights:5 6
|
||||||
|
K:Visit:TrigPump
|
||||||
|
SVar:TrigPump:DB$ PumpAll | ValidCards$ Creature.powerLE2+YouCtrl | KW$ Horsemanship | SpellDescription$ Creatures you control with power 2 or less gain horsemanship until end of turn.
|
||||||
|
Oracle:Visit — Creatures you control with power 2 or less gain horsemanship until end of turn. (They can’t be blocked except by creatures with horsemanship.)
|
||||||
8
forge-gui/res/cardsfolder/p/petting_zookeeper.txt
Normal file
8
forge-gui/res/cardsfolder/p/petting_zookeeper.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Name:Petting Zookeeper
|
||||||
|
ManaCost:2 G
|
||||||
|
Types:Creature Elf Employee
|
||||||
|
PT:0/4
|
||||||
|
K:Reach
|
||||||
|
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigOpenAttraction | TriggerDescription$ When CARDNAME enters the battlefield, open an Attraction.
|
||||||
|
SVar:TrigOpenAttraction:DB$ OpenAttraction
|
||||||
|
Oracle:Reach\nWhen Petting Zookeeper enters the battlefield, open an Attraction.
|
||||||
19
forge-gui/res/cardsfolder/p/pick_a_beeble.txt
Normal file
19
forge-gui/res/cardsfolder/p/pick_a_beeble.txt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Name:Pick-a-Beeble
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 3 6
|
||||||
|
Variant:B:Lights:2 4 6
|
||||||
|
Variant:C:Lights:2 5 6
|
||||||
|
Variant:D:Lights:3 4 6
|
||||||
|
Variant:E:Lights:3 5 6
|
||||||
|
Variant:F:Lights:4 5 6
|
||||||
|
K:Visit:TrigRoll
|
||||||
|
K:Prize:TrigPrize
|
||||||
|
SVar:TrigRoll:DB$ RollDice | ResultSVar$ Result | Sides$ 6 | SubAbility$ DBCounters | SpellDescription$ Roll a six-sided die. Put a number of luck counters on CARDNAME equal to the result and create a Treasure token. Then if there are six or more luck counters on CARDNAME, claim the prize!
|
||||||
|
SVar:DBCounters:DB$ PutCounter | Defined$ Self | CounterType$ LUCK | CounterNum$ Result | SubAbility$ DBTreasure
|
||||||
|
SVar:DBTreasure:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_treasure_sac | SubAbility$ DBClaim
|
||||||
|
SVar:DBClaim:DB$ ClaimThePrize | ConditionDefined$ Self | ConditionPresent$ Card.Self+counters_GE6_LUCK
|
||||||
|
SVar:TrigPrize:DB$ Token | TokenAmount$ 2 | TokenScript$ c_a_treasure_sac | SubAbility$ DBSack | SpellDescription$ Create two Treasure tokens, then sacrifice CARDNAME and open an Attraction.
|
||||||
|
SVar:DBSack:DB$ Sacrifice | SacValid$ Self | SubAbility$ DBOpen
|
||||||
|
SVar:DBOpen:DB$ OpenAttraction
|
||||||
|
Oracle:Visit — Roll a six-sided die. Put a number of luck counters on Pick-a-Beeble equal to the result and create a Treasure token. Then if there are six or more luck counters on Pick-a-Beeble, claim the prize!\nPrize — Create two Treasure tokens, then sacrifice Pick-a-Beeble and open an Attraction.
|
||||||
8
forge-gui/res/cardsfolder/q/quick_fixer.txt
Normal file
8
forge-gui/res/cardsfolder/q/quick_fixer.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Name:Quick Fixer
|
||||||
|
ManaCost:2 B
|
||||||
|
Types:Creature Azra Employee
|
||||||
|
PT:2/3
|
||||||
|
K:Menace
|
||||||
|
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigOpenAttraction | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, open an Attraction.
|
||||||
|
SVar:TrigOpenAttraction:DB$ OpenAttraction
|
||||||
|
Oracle:Menace\nWhenever Quick Fixer deals combat damage to a player, open an Attraction.
|
||||||
7
forge-gui/res/cardsfolder/r/rad_rascal.txt
Normal file
7
forge-gui/res/cardsfolder/r/rad_rascal.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Name:Rad Rascal
|
||||||
|
ManaCost:3 R
|
||||||
|
Types:Creature Devil Employee
|
||||||
|
PT:3/3
|
||||||
|
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigOpenAttraction | TriggerDescription$ When CARDNAME enters the battlefield, open an Attraction.
|
||||||
|
SVar:TrigOpenAttraction:DB$ OpenAttraction
|
||||||
|
Oracle:When Rad Rascal enters the battlefield, open an Attraction.
|
||||||
7
forge-gui/res/cardsfolder/r/ride_guide.txt
Normal file
7
forge-gui/res/cardsfolder/r/ride_guide.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Name:Ride Guide
|
||||||
|
ManaCost:4 W
|
||||||
|
Types:Creature Human Employee
|
||||||
|
PT:4/4
|
||||||
|
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigOpenAttraction | TriggerDescription$ When CARDNAME enters the battlefield, open an Attraction.
|
||||||
|
SVar:TrigOpenAttraction:DB$ OpenAttraction
|
||||||
|
Oracle:When Ride Guide enters the battlefield, open an Attraction.
|
||||||
10
forge-gui/res/cardsfolder/r/roller_coaster.txt
Normal file
10
forge-gui/res/cardsfolder/r/roller_coaster.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Roller Coaster
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 6
|
||||||
|
Variant:B:Lights:3 6
|
||||||
|
Variant:C:Lights:4 6
|
||||||
|
Variant:D:Lights:5 6
|
||||||
|
K:Visit:TrigPump
|
||||||
|
SVar:TrigPump:DB$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +2 | SpellDescription$ Creatures you control get +2/+0 until end of turn.
|
||||||
|
Oracle:Visit — Creatures you control get +2/+0 until end of turn.
|
||||||
7
forge-gui/res/cardsfolder/s/seasoned_buttoneer.txt
Normal file
7
forge-gui/res/cardsfolder/s/seasoned_buttoneer.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Name:Seasoned Buttoneer
|
||||||
|
ManaCost:2 U
|
||||||
|
Types:Creature Vedalken Employee
|
||||||
|
PT:2/2
|
||||||
|
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigOpenAttraction | TriggerDescription$ When CARDNAME enters the battlefield, open an Attraction.
|
||||||
|
SVar:TrigOpenAttraction:DB$ OpenAttraction
|
||||||
|
Oracle:When Seasoned Buttoneer enters the battlefield, open an Attraction.
|
||||||
9
forge-gui/res/cardsfolder/s/sly_spy.txt
Normal file
9
forge-gui/res/cardsfolder/s/sly_spy.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Name:Sly Spy
|
||||||
|
ManaCost:2 B
|
||||||
|
Types:Creature Human Spy
|
||||||
|
PT:2/2
|
||||||
|
Variant:F:T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigRoll | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, roll a six-sided die. That player loses life equal to the result.
|
||||||
|
Variant:F:SVar:TrigRoll:DB$ RollDice | ResultSVar$ Result | SubAbility$ DBLoseLife
|
||||||
|
Variant:F:SVar:DBLoseLife:DB$ LoseLife | Defined$ TriggeredTarget | LifeAmount$ Result
|
||||||
|
Oracle:<Unsupported Variant>
|
||||||
|
Variant:F:Oracle:Whenever Sly Spy deals combat damage to a player, roll a six-sided die. That player loses life equal to the result.
|
||||||
12
forge-gui/res/cardsfolder/s/spinny_ride.txt
Normal file
12
forge-gui/res/cardsfolder/s/spinny_ride.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Name:Spinny Ride
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 3 6
|
||||||
|
Variant:B:Lights:2 4 6
|
||||||
|
Variant:C:Lights:2 5 6
|
||||||
|
Variant:D:Lights:3 4 6
|
||||||
|
Variant:E:Lights:3 5 6
|
||||||
|
Variant:F:Lights:4 5 6
|
||||||
|
K:Visit:TrigTap
|
||||||
|
SVar:TrigTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Choose target creature an opponent controls. | SpellDescription$ Tap target creature an opponent controls.
|
||||||
|
Oracle:Visit — Tap target creature an opponent controls.
|
||||||
11
forge-gui/res/cardsfolder/s/squirrel_squatters.txt
Normal file
11
forge-gui/res/cardsfolder/s/squirrel_squatters.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Name:Squirrel Squatters
|
||||||
|
ManaCost:3 G G
|
||||||
|
Types:Creature Squirrel
|
||||||
|
PT:4/4
|
||||||
|
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigOpenAttraction | TriggerDescription$ When CARDNAME enters the battlefield, open an Attraction.
|
||||||
|
SVar:TrigOpenAttraction:DB$ OpenAttraction
|
||||||
|
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME attacks, create a 1/1 green Squirrel creature token that's tapped and attacking for each Attraction you've visited this turn.
|
||||||
|
SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ g_1_1_squirrel | TokenOwner$ You | TokenTapped$ True | TokenAttacking$ True
|
||||||
|
SVar:X:Count$Valid Attraction.VisitedThisTurn
|
||||||
|
SVar:HasAttackEffect:TRUE
|
||||||
|
Oracle:When Squirrel Squatters enters the battlefield, open an Attraction. (Put the top card of your Attraction deck onto the battlefield.)\nWhenever Squirrel Squatters attacks, create a 1/1 green Squirrel creature token that’s tapped and attacking for each Attraction you’ve visited this turn.
|
||||||
13
forge-gui/res/cardsfolder/t/the_most_dangerous_gamer.txt
Normal file
13
forge-gui/res/cardsfolder/t/the_most_dangerous_gamer.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Name:The Most Dangerous Gamer
|
||||||
|
ManaCost:2 B G
|
||||||
|
Types:Legendary Creature Human Gamer Guest
|
||||||
|
PT:2/2
|
||||||
|
K:Deathtouch
|
||||||
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ DBOpen | TriggerDescription$ Whenever CARDNAME enters the battlefield or attacks, open an Attraction.
|
||||||
|
T:Mode$ Attacks | ValidCard$ Card.Self | Secondary$ True | Execute$ DBOpen | TriggerDescription$ Whenever CARDNAME enters the battlefield or attacks, open an Attraction.
|
||||||
|
SVar:DBOpen:DB$ OpenAttraction
|
||||||
|
T:Mode$ ChangesZone | Origin$ AttractionDeck | Destination$ Battlefield | ValidCard$ Attraction.YouCtrl | TriggerZones$ Battlefield | Execute$ DBCounter | TriggerDescription$ Whenever you open an Attraction, put a +1/+1 counter on CARDNAME.
|
||||||
|
SVar:DBCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
|
||||||
|
T:Mode$ ClaimPrize | ValidCard$ Attraction.YouCtrl | TriggerZones$ Battlefield | Execute$ DBDestroy | TriggerDescription$ Whenever you claim the prize of an Attraction, destroy target permanent.
|
||||||
|
SVar:DBDestroy:DB$ Destroy | ValidTgts$ Permanent | TgtPrompt$ Select target permanent.
|
||||||
|
Oracle:Deathtouch\nWhenever The Most Dangerous Gamer enters the battlefield or attacks, open an Attraction.\nWhenever you open an Attraction, put a +1/+1 counter on The Most Dangerous Gamer.\nWhenever you claim the prize of an Attraction, destroy target permanent.
|
||||||
12
forge-gui/res/cardsfolder/t/trash_bin.txt
Normal file
12
forge-gui/res/cardsfolder/t/trash_bin.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Name:Trash Bin
|
||||||
|
ManaCost:no cost
|
||||||
|
Types:Artifact Attraction
|
||||||
|
Variant:A:Lights:2 6
|
||||||
|
Variant:B:Lights:3 6
|
||||||
|
Variant:C:Lights:4 6
|
||||||
|
Variant:D:Lights:5 6
|
||||||
|
K:Visit:TrigMill
|
||||||
|
SVar:TrigMill:DB$ Mill | NumCards$ 2 | SubAbility$ DBReturn | SpellDescription$ Mill two cards, then return a creature card from your graveyard to your hand.
|
||||||
|
SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ChangeType$ Card.YouOwn | AtRandom$ True | Hidden$ True
|
||||||
|
DeckHas:Ability$Graveyard|Mill
|
||||||
|
Oracle:Visit — Mill two cards, then return a card at random from your graveyard to your hand. (To mill a card, a player puts the top card of their library into their graveyard.)
|
||||||
@@ -205,41 +205,141 @@ F192 R Souvenir T-Shirt @Michael Phillippi
|
|||||||
F197 U The Big Top @Kirsten Zirngibl
|
F197 U The Big Top @Kirsten Zirngibl
|
||||||
F198 C Nearby Planet @Bruce Brenneise
|
F198 C Nearby Planet @Bruce Brenneise
|
||||||
F199 R Urza's Fun House @Dmitry Burmak
|
F199 R Urza's Fun House @Dmitry Burmak
|
||||||
200 U Balloon Stand @Jakub Kasper
|
200a U Balloon Stand @Jakub Kasper $A
|
||||||
201 U Bounce Chamber @Dmitry Burmak
|
200b U Balloon Stand @Jakub Kasper $B
|
||||||
202 U Bumper Cars @Gabor Szikszai
|
200c U Balloon Stand @Jakub Kasper $C
|
||||||
F203 R Centrifuge @Greg Staples
|
200d U Balloon Stand @Jakub Kasper $D
|
||||||
204 C Clown Extruder @Marco Bucci
|
201a U Bounce Chamber @Dmitry Burmak $A
|
||||||
205 U Concession Stand @David Sladek
|
201b U Bounce Chamber @Dmitry Burmak $B
|
||||||
206 C Costume Shop @Raluca Marinescu
|
201c U Bounce Chamber @Dmitry Burmak $C
|
||||||
F207 C Cover the Spot @Jeff Miracola
|
201d U Bounce Chamber @Dmitry Burmak $D
|
||||||
F208 C Dart Throw @Gaboleps
|
202a U Bumper Cars @Gabor Szikszai $A
|
||||||
209 C Drop Tower @Dmitry Burmak
|
202b U Bumper Cars @Gabor Szikszai $B
|
||||||
|
202c U Bumper Cars @Gabor Szikszai $C
|
||||||
|
202d U Bumper Cars @Gabor Szikszai $D
|
||||||
|
202e U Bumper Cars @Gabor Szikszai $E
|
||||||
|
202f U Bumper Cars @Gabor Szikszai $F
|
||||||
|
F203a R Centrifuge @Greg Staples $A
|
||||||
|
F203b R Centrifuge @Greg Staples $B
|
||||||
|
204a C Clown Extruder @Marco Bucci $A
|
||||||
|
204b C Clown Extruder @Marco Bucci $B
|
||||||
|
204c C Clown Extruder @Marco Bucci $C
|
||||||
|
204d C Clown Extruder @Marco Bucci $D
|
||||||
|
205a U Concession Stand @David Sladek $A
|
||||||
|
205b U Concession Stand @David Sladek $B
|
||||||
|
205c U Concession Stand @David Sladek $C
|
||||||
|
205d U Concession Stand @David Sladek $D
|
||||||
|
206a C Costume Shop @Raluca Marinescu $A
|
||||||
|
206b C Costume Shop @Raluca Marinescu $B
|
||||||
|
206c C Costume Shop @Raluca Marinescu $C
|
||||||
|
206d C Costume Shop @Raluca Marinescu $D
|
||||||
|
206e C Costume Shop @Raluca Marinescu $E
|
||||||
|
206f C Costume Shop @Raluca Marinescu $F
|
||||||
|
F207a C Cover the Spot @Jeff Miracola $A
|
||||||
|
F207b C Cover the Spot @Jeff Miracola $B
|
||||||
|
F207c C Cover the Spot @Jeff Miracola $C
|
||||||
|
F207d C Cover the Spot @Jeff Miracola $D
|
||||||
|
F208a C Dart Throw @Gaboleps $A
|
||||||
|
F208b C Dart Throw @Gaboleps $B
|
||||||
|
F208c C Dart Throw @Gaboleps $C
|
||||||
|
F208d C Dart Throw @Gaboleps $D
|
||||||
|
209a C Drop Tower @Dmitry Burmak $A
|
||||||
|
209b C Drop Tower @Dmitry Burmak $B
|
||||||
|
209c C Drop Tower @Dmitry Burmak $C
|
||||||
|
209d C Drop Tower @Dmitry Burmak $D
|
||||||
|
209e C Drop Tower @Dmitry Burmak $E
|
||||||
|
209f C Drop Tower @Dmitry Burmak $F
|
||||||
210 R Ferris Wheel @Kirsten Zirngibl
|
210 R Ferris Wheel @Kirsten Zirngibl
|
||||||
211 C Foam Weapons Kiosk @Matt Gaser
|
211a C Foam Weapons Kiosk @Matt Gaser $A
|
||||||
212 C Fortune Teller @Jamroz Gary
|
211b C Foam Weapons Kiosk @Matt Gaser $B
|
||||||
F213 R Gallery of Legends @Jakub Kasper
|
211c C Foam Weapons Kiosk @Matt Gaser $C
|
||||||
F214 R Gift Shop @Matt Gaser
|
211d C Foam Weapons Kiosk @Matt Gaser $D
|
||||||
F215 U Guess Your Fate @Bruce Brenneise
|
212a C Fortune Teller @Jamroz Gary $A
|
||||||
216 R Hall of Mirrors @Vincent Christiaens
|
212b C Fortune Teller @Jamroz Gary $B
|
||||||
217 R Haunted House @Dmitry Burmak
|
212c C Fortune Teller @Jamroz Gary $C
|
||||||
218 U Information Booth @Gaboleps
|
212d C Fortune Teller @Jamroz Gary $D
|
||||||
219 C Kiddie Coaster @Marco Bucci
|
212e C Fortune Teller @Jamroz Gary $E
|
||||||
F220 R Log Flume @Marco Bucci
|
212f C Fortune Teller @Jamroz Gary $F
|
||||||
F221 R Memory Test @Setor Fiadzigbey
|
F213a R Gallery of Legends @Jakub Kasper $A
|
||||||
222 R Merry-Go-Round @Carl Critchlow
|
F213b R Gallery of Legends @Jakub Kasper $B
|
||||||
223 C Pick-a-Beeble @Dave Greco
|
F214a R Gift Shop @Matt Gaser $A
|
||||||
F224 R Push Your Luck @Sebastian Giacobino
|
F214b R Gift Shop @Matt Gaser $B
|
||||||
225 U Roller Coaster @Gabor Szikszai
|
F215a U Guess Your Fate @Bruce Brenneise $A
|
||||||
F226 U Scavenger Hunt @Jamroz Gary
|
F215b U Guess Your Fate @Bruce Brenneise $B
|
||||||
227 C Spinny Ride @Aaron J. Riley
|
F215c U Guess Your Fate @Bruce Brenneise $C
|
||||||
F228 U Squirrel Stack @Andrea Radeck
|
F215d U Guess Your Fate @Bruce Brenneise $D
|
||||||
229 R Storybook Ride @Dmitry Burmak
|
216a R Hall of Mirrors @Vincent Christiaens $A
|
||||||
F230 U The Superlatorium @Simon Dominic
|
216b R Hall of Mirrors @Vincent Christiaens $B
|
||||||
231 R Swinging Ship @Mike Burns
|
217a R Haunted House @Dmitry Burmak $A
|
||||||
232 U Trash Bin @Greg Bobrowski
|
217b R Haunted House @Dmitry Burmak $B
|
||||||
F233 U Trivia Contest @Caroline Gariba
|
218a U Information Booth @Gaboleps $A
|
||||||
234 R Tunnel of Love @Vladimir Krisetskiy
|
218b U Information Booth @Gaboleps $B
|
||||||
|
218c U Information Booth @Gaboleps $C
|
||||||
|
218d U Information Booth @Gaboleps $D
|
||||||
|
219a C Kiddie Coaster @Marco Bucci $A
|
||||||
|
219b C Kiddie Coaster @Marco Bucci $B
|
||||||
|
219c C Kiddie Coaster @Marco Bucci $C
|
||||||
|
219d C Kiddie Coaster @Marco Bucci $D
|
||||||
|
219e C Kiddie Coaster @Marco Bucci $E
|
||||||
|
219f C Kiddie Coaster @Marco Bucci $F
|
||||||
|
F220a R Log Flume @Marco Bucci $A
|
||||||
|
F220b R Log Flume @Marco Bucci $B
|
||||||
|
F221a R Memory Test @Setor Fiadzigbey $A
|
||||||
|
F221b R Memory Test @Setor Fiadzigbey $B
|
||||||
|
222a R Merry-Go-Round @Carl Critchlow $A
|
||||||
|
222b R Merry-Go-Round @Carl Critchlow $B
|
||||||
|
223a C Pick-a-Beeble @Dave Greco $A
|
||||||
|
223b C Pick-a-Beeble @Dave Greco $B
|
||||||
|
223c C Pick-a-Beeble @Dave Greco $C
|
||||||
|
223d C Pick-a-Beeble @Dave Greco $D
|
||||||
|
223e C Pick-a-Beeble @Dave Greco $E
|
||||||
|
223f C Pick-a-Beeble @Dave Greco $F
|
||||||
|
F224a R Push Your Luck @Sebastian Giacobino $A
|
||||||
|
F224b R Push Your Luck @Sebastian Giacobino $B
|
||||||
|
225a U Roller Coaster @Gabor Szikszai $A
|
||||||
|
225b U Roller Coaster @Gabor Szikszai $B
|
||||||
|
225c U Roller Coaster @Gabor Szikszai $C
|
||||||
|
225d U Roller Coaster @Gabor Szikszai $D
|
||||||
|
F226a U Scavenger Hunt @Jamroz Gary $A
|
||||||
|
F226b U Scavenger Hunt @Jamroz Gary $B
|
||||||
|
F226c U Scavenger Hunt @Jamroz Gary $C
|
||||||
|
F226d U Scavenger Hunt @Jamroz Gary $D
|
||||||
|
F226e U Scavenger Hunt @Jamroz Gary $E
|
||||||
|
F226f U Scavenger Hunt @Jamroz Gary $F
|
||||||
|
227a C Spinny Ride @Aaron J. Riley $A
|
||||||
|
227b C Spinny Ride @Aaron J. Riley $B
|
||||||
|
227c C Spinny Ride @Aaron J. Riley $C
|
||||||
|
227d C Spinny Ride @Aaron J. Riley $D
|
||||||
|
227e C Spinny Ride @Aaron J. Riley $E
|
||||||
|
227f C Spinny Ride @Aaron J. Riley $F
|
||||||
|
F228a U Squirrel Stack @Andrea Radeck $A
|
||||||
|
F228b U Squirrel Stack @Andrea Radeck $B
|
||||||
|
F228c U Squirrel Stack @Andrea Radeck $C
|
||||||
|
F228d U Squirrel Stack @Andrea Radeck $D
|
||||||
|
F228e U Squirrel Stack @Andrea Radeck $E
|
||||||
|
F228f U Squirrel Stack @Andrea Radeck $F
|
||||||
|
229a R Storybook Ride @Dmitry Burmak $A
|
||||||
|
229b R Storybook Ride @Dmitry Burmak $B
|
||||||
|
F230a U The Superlatorium @Simon Dominic $A
|
||||||
|
F230b U The Superlatorium @Simon Dominic $B
|
||||||
|
F230c U The Superlatorium @Simon Dominic $C
|
||||||
|
F230d U The Superlatorium @Simon Dominic $D
|
||||||
|
F230e U The Superlatorium @Simon Dominic $E
|
||||||
|
F230f U The Superlatorium @Simon Dominic $F
|
||||||
|
231a R Swinging Ship @Mike Burns $A
|
||||||
|
231b R Swinging Ship @Mike Burns $B
|
||||||
|
232a U Trash Bin @Greg Bobrowski $A
|
||||||
|
232b U Trash Bin @Greg Bobrowski $B
|
||||||
|
232c U Trash Bin @Greg Bobrowski $C
|
||||||
|
232d U Trash Bin @Greg Bobrowski $D
|
||||||
|
F233a U Trivia Contest @Caroline Gariba $A
|
||||||
|
F233b U Trivia Contest @Caroline Gariba $B
|
||||||
|
F233c U Trivia Contest @Caroline Gariba $C
|
||||||
|
F233d U Trivia Contest @Caroline Gariba $D
|
||||||
|
F233e U Trivia Contest @Caroline Gariba $E
|
||||||
|
F233f U Trivia Contest @Caroline Gariba $F
|
||||||
|
234a R Tunnel of Love @Vladimir Krisetskiy $A
|
||||||
|
234b R Tunnel of Love @Vladimir Krisetskiy $B
|
||||||
235 L Plains @Adam Paquette
|
235 L Plains @Adam Paquette
|
||||||
236 L Island @Adam Paquette
|
236 L Island @Adam Paquette
|
||||||
237 L Swamp @Adam Paquette
|
237 L Swamp @Adam Paquette
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ ScryfallCode=UST
|
|||||||
9 U Half-Kitten, Half-
|
9 U Half-Kitten, Half-
|
||||||
10 C Humming-
|
10 C Humming-
|
||||||
11 R Jackknight
|
11 R Jackknight
|
||||||
12a U Knight of the Kitchen Sink A
|
12a U Knight of the Kitchen Sink $A
|
||||||
12b U Knight of the Kitchen Sink B
|
12b U Knight of the Kitchen Sink $B
|
||||||
12c U Knight of the Kitchen Sink C
|
12c U Knight of the Kitchen Sink $C
|
||||||
12d U Knight of the Kitchen Sink D
|
12d U Knight of the Kitchen Sink $D
|
||||||
12e U Knight of the Kitchen Sink E
|
12e U Knight of the Kitchen Sink $E
|
||||||
12f U Knight of the Kitchen Sink F
|
12f U Knight of the Kitchen Sink $F
|
||||||
13 U Knight of the Widget
|
13 U Knight of the Widget
|
||||||
14 U Midlife Upgrade
|
14 U Midlife Upgrade
|
||||||
15 R Oddly Uneven
|
15 R Oddly Uneven
|
||||||
@@ -65,12 +65,12 @@ ScryfallCode=UST
|
|||||||
46 U Spy Eye
|
46 U Spy Eye
|
||||||
47 U Suspicious Nanny
|
47 U Suspicious Nanny
|
||||||
48 C Time Out
|
48 C Time Out
|
||||||
49a R Very Cryptic Command A
|
49a R Very Cryptic Command $A
|
||||||
49b R Very Cryptic Command B
|
49b R Very Cryptic Command $B
|
||||||
49c R Very Cryptic Command C
|
49c R Very Cryptic Command $C
|
||||||
49d R Very Cryptic Command D
|
49d R Very Cryptic Command $D
|
||||||
49e R Very Cryptic Command E
|
49e R Very Cryptic Command $E
|
||||||
49f R Very Cryptic Command F
|
49f R Very Cryptic Command $F
|
||||||
50 C Wall of Fortune
|
50 C Wall of Fortune
|
||||||
51 C Big Boa Constrictor
|
51 C Big Boa Constrictor
|
||||||
52 C capital offense
|
52 C capital offense
|
||||||
@@ -91,12 +91,12 @@ ScryfallCode=UST
|
|||||||
64 U Overt Operative
|
64 U Overt Operative
|
||||||
65 U "Rumors of My Death..."
|
65 U "Rumors of My Death..."
|
||||||
66 U Skull Saucer
|
66 U Skull Saucer
|
||||||
67a U Sly Spy A
|
67a U Sly Spy $A
|
||||||
67b U Sly Spy B
|
67b U Sly Spy $B
|
||||||
67c U Sly Spy C
|
67c U Sly Spy $C
|
||||||
67d U Sly Spy D
|
67d U Sly Spy $D
|
||||||
67e U Sly Spy E
|
67e U Sly Spy $E
|
||||||
67f U Sly Spy F
|
67f U Sly Spy $F
|
||||||
68 C Snickering Squirrel
|
68 C Snickering Squirrel
|
||||||
69 R Spike, Tournament Grinder
|
69 R Spike, Tournament Grinder
|
||||||
70 U Squirrel-Powered Scheme
|
70 U Squirrel-Powered Scheme
|
||||||
@@ -111,12 +111,12 @@ ScryfallCode=UST
|
|||||||
79 C Common Iguana
|
79 C Common Iguana
|
||||||
80 R The Countdown Is at One
|
80 R The Countdown Is at One
|
||||||
81 C Feisty Stegosaurus
|
81 C Feisty Stegosaurus
|
||||||
82a U Garbage Elemental A
|
82a U Garbage Elemental $A
|
||||||
82b U Garbage Elemental B
|
82b U Garbage Elemental $B
|
||||||
82c U Garbage Elemental C
|
82c U Garbage Elemental $C
|
||||||
82d U Garbage Elemental D
|
82d U Garbage Elemental $D
|
||||||
82e U Garbage Elemental E
|
82e U Garbage Elemental $E
|
||||||
82f U Garbage Elemental F
|
82f U Garbage Elemental $F
|
||||||
83 U Goblin Haberdasher
|
83 U Goblin Haberdasher
|
||||||
84 U Half-Orc, Half-
|
84 U Half-Orc, Half-
|
||||||
85 C Hammer Helper
|
85 C Hammer Helper
|
||||||
@@ -153,12 +153,12 @@ ScryfallCode=UST
|
|||||||
110 U Ground Pounder
|
110 U Ground Pounder
|
||||||
111 U Half-Squirrel, Half-
|
111 U Half-Squirrel, Half-
|
||||||
112 R Hydradoodle
|
112 R Hydradoodle
|
||||||
113a R Ineffable Blessing A
|
113a R Ineffable Blessing $A
|
||||||
113b R Ineffable Blessing B
|
113b R Ineffable Blessing $B
|
||||||
113c R Ineffable Blessing C
|
113c R Ineffable Blessing $C
|
||||||
113d R Ineffable Blessing D
|
113d R Ineffable Blessing $D
|
||||||
113e R Ineffable Blessing E
|
113e R Ineffable Blessing $E
|
||||||
113f R Ineffable Blessing F
|
113f R Ineffable Blessing $F
|
||||||
114 C Joyride Rigger
|
114 C Joyride Rigger
|
||||||
115 U Monkey-
|
115 U Monkey-
|
||||||
116 C Mother Kangaroo
|
116 C Mother Kangaroo
|
||||||
@@ -195,12 +195,12 @@ ScryfallCode=UST
|
|||||||
145c C Despondent Killbot
|
145c C Despondent Killbot
|
||||||
145d C Enraged Killbot
|
145d C Enraged Killbot
|
||||||
146 U Entirely Normal Armchair
|
146 U Entirely Normal Armchair
|
||||||
147a R Everythingamajig A
|
147a R Everythingamajig $A
|
||||||
147b R Everythingamajig B
|
147b R Everythingamajig $B
|
||||||
147c R Everythingamajig C
|
147c R Everythingamajig $C
|
||||||
147d R Everythingamajig D
|
147d R Everythingamajig $D
|
||||||
147e R Everythingamajig E
|
147e R Everythingamajig $E
|
||||||
147f R Everythingamajig F
|
147f R Everythingamajig $F
|
||||||
148 C Gnome-Made Engine
|
148 C Gnome-Made Engine
|
||||||
149 R Handy Dandy Clone Machine
|
149 R Handy Dandy Clone Machine
|
||||||
150 R Kindslaver
|
150 R Kindslaver
|
||||||
|
|||||||
@@ -902,14 +902,16 @@ lblColumns=Columns
|
|||||||
lblPiles=Piles:
|
lblPiles=Piles:
|
||||||
lblGroups=Groups:
|
lblGroups=Groups:
|
||||||
lblImageView=Image View
|
lblImageView=Image View
|
||||||
#CEditorVariant.java, CEditorConstructed.java
|
#CEditorVariant.java, CEditorConstructed.java, FDeckEditor.java
|
||||||
lblCatalog=Catalog
|
lblCatalog=Catalog
|
||||||
lblAdd=Add
|
lblAdd=Add
|
||||||
|
lblAddCommander=Set
|
||||||
lbltodeck=to deck
|
lbltodeck=to deck
|
||||||
lblfromdeck=from deck
|
lblfromdeck=from deck
|
||||||
lbltosideboard=to sideboard
|
lbltosideboard=to sideboard
|
||||||
lblfromsideboard=from sideboard
|
lblfromsideboard=from sideboard
|
||||||
lblascommander=as commander
|
lblascommander=as commander
|
||||||
|
lblaspartnercommander=as partner commander
|
||||||
lblasoathbreaker=as oathbreaker
|
lblasoathbreaker=as oathbreaker
|
||||||
lblassignaturespell=as signature spell
|
lblassignaturespell=as signature spell
|
||||||
lblasavatar=as avatar
|
lblasavatar=as avatar
|
||||||
@@ -917,10 +919,12 @@ lblfromschemedeck=from scheme deck
|
|||||||
lblfromplanardeck=from planar deck
|
lblfromplanardeck=from planar deck
|
||||||
lblfromconspiracydeck=from conspiracy deck
|
lblfromconspiracydeck=from conspiracy deck
|
||||||
lblfromdungeondeck=from dungeon deck
|
lblfromdungeondeck=from dungeon deck
|
||||||
|
lblfromattractiondeck=from attraction deck
|
||||||
lbltoschemedeck=to scheme deck
|
lbltoschemedeck=to scheme deck
|
||||||
lbltoplanardeck=to planar deck
|
lbltoplanardeck=to planar deck
|
||||||
lbltoconspiracydeck=to conspiracy deck
|
lbltoconspiracydeck=to conspiracy deck
|
||||||
lbltodungeondeck=to dungeon deck
|
lbltodungeondeck=to dungeon deck
|
||||||
|
lbltoattractiondeck=to attraction deck
|
||||||
lblMove=Move
|
lblMove=Move
|
||||||
#VDock.java
|
#VDock.java
|
||||||
lblDock=Dock
|
lblDock=Dock
|
||||||
@@ -1129,17 +1133,22 @@ lblChangePreferredArt=Change Preferred Art
|
|||||||
lblSelectPreferredArt=Select preferred art for
|
lblSelectPreferredArt=Select preferred art for
|
||||||
lblReplaceCard=Replace Card Variant
|
lblReplaceCard=Replace Card Variant
|
||||||
lblSelectReplacementCard=Select Replacement Card Variant for
|
lblSelectReplacementCard=Select Replacement Card Variant for
|
||||||
|
lblAddDeckSection=Add Variant Section...
|
||||||
|
lblAddDeckSectionSelect=Select Section to Add
|
||||||
lblTo=to
|
lblTo=to
|
||||||
|
lblFrom=from
|
||||||
lblAvatar=Avatar
|
lblAvatar=Avatar
|
||||||
lblCards=Cards
|
lblCards=Cards
|
||||||
lblPlanes=Planes
|
lblPlanes=Planes
|
||||||
lblSchemes=Schemes
|
lblSchemes=Schemes
|
||||||
|
lblAttractions=Attractions
|
||||||
lblToMainDeck=to Main Deck
|
lblToMainDeck=to Main Deck
|
||||||
lblHowMany=how many?
|
lblHowMany=how many?
|
||||||
lblInventory=Inventory
|
lblInventory=Inventory
|
||||||
lblCollection=Collection
|
lblCollection=Collection
|
||||||
lblCommanders=Commanders
|
lblCommanders=Commanders
|
||||||
lblOathbreakers=Oathbreakers
|
lblOathbreakers=Oathbreakers
|
||||||
|
lblConspiracies=Conspiracies
|
||||||
lblSave=Save
|
lblSave=Save
|
||||||
lblDontSave=Don''t Save
|
lblDontSave=Don''t Save
|
||||||
lblPackN=Pack {0}
|
lblPackN=Pack {0}
|
||||||
@@ -1353,6 +1362,7 @@ lblChooseOrderCardsPutIntoGraveyard=Choose order of cards to put into the gravey
|
|||||||
lblClosestToBottom=Closest to bottom
|
lblClosestToBottom=Closest to bottom
|
||||||
lblChooseOrderCardsPutIntoPlanarDeck=Choose order of cards to put into the planar deck
|
lblChooseOrderCardsPutIntoPlanarDeck=Choose order of cards to put into the planar deck
|
||||||
lblChooseOrderCardsPutIntoSchemeDeck=Choose order of cards to put into the scheme deck
|
lblChooseOrderCardsPutIntoSchemeDeck=Choose order of cards to put into the scheme deck
|
||||||
|
lblChooseOrderCardsPutIntoAttractionDeck=Choose order of cards to put into the attraction deck
|
||||||
lblChooseOrderCopiesCast=Choose order of copies to cast
|
lblChooseOrderCopiesCast=Choose order of copies to cast
|
||||||
lblChooseOrderCards=Choose card order
|
lblChooseOrderCards=Choose card order
|
||||||
lblDelveHowManyCards=Delve how many cards?
|
lblDelveHowManyCards=Delve how many cards?
|
||||||
@@ -2099,6 +2109,7 @@ lblDoYouWantRevealYourHand=Do you want to reveal your hand?
|
|||||||
lblPlayerRolledResult={0} rolled {1}
|
lblPlayerRolledResult={0} rolled {1}
|
||||||
lblIgnoredRolls=Ignored rolls: {0}
|
lblIgnoredRolls=Ignored rolls: {0}
|
||||||
lblRerollResult=Reroll {0}?
|
lblRerollResult=Reroll {0}?
|
||||||
|
lblAttractionRollResult={0} rolled to visit their Attractions. Result: {1}.
|
||||||
#RollPlanarDiceEffect.java
|
#RollPlanarDiceEffect.java
|
||||||
lblPlanarDiceResult=Planar dice result: {0}
|
lblPlanarDiceResult=Planar dice result: {0}
|
||||||
#SacrificeEffect.java
|
#SacrificeEffect.java
|
||||||
@@ -2193,6 +2204,8 @@ lblSideboardZone=sideboard
|
|||||||
lblAnteZone=ante
|
lblAnteZone=ante
|
||||||
lblSchemeDeckZone=schemedeck
|
lblSchemeDeckZone=schemedeck
|
||||||
lblPlanarDeckZone=planardeck
|
lblPlanarDeckZone=planardeck
|
||||||
|
lblAttractionDeckZone=attractiondeck
|
||||||
|
lblJunkyardZone=junkyard
|
||||||
lblSubgameZone=subgame
|
lblSubgameZone=subgame
|
||||||
lblNoneZone=none
|
lblNoneZone=none
|
||||||
#BoosterDraft.java
|
#BoosterDraft.java
|
||||||
@@ -3017,6 +3030,7 @@ lblDetails=Details
|
|||||||
lblChosenColors=Chosen colors:
|
lblChosenColors=Chosen colors:
|
||||||
lblLoyalty=Loyalty
|
lblLoyalty=Loyalty
|
||||||
lblDefense=Defense
|
lblDefense=Defense
|
||||||
|
lblLights=Lights
|
||||||
#Achievement.java
|
#Achievement.java
|
||||||
lblStandard=Standard
|
lblStandard=Standard
|
||||||
lblChaos=Chaos
|
lblChaos=Chaos
|
||||||
|
|||||||
@@ -334,6 +334,7 @@ Saga
|
|||||||
Shrine
|
Shrine
|
||||||
Shard
|
Shard
|
||||||
[ArtifactTypes]
|
[ArtifactTypes]
|
||||||
|
Attraction
|
||||||
Blood
|
Blood
|
||||||
Bobblehead:Bobbleheads
|
Bobblehead:Bobbleheads
|
||||||
Clue:Clues
|
Clue:Clues
|
||||||
|
|||||||
@@ -459,8 +459,7 @@ public class DeckImportController {
|
|||||||
// Account for any [un]foiled version
|
// Account for any [un]foiled version
|
||||||
PaperCard cardKey;
|
PaperCard cardKey;
|
||||||
if (card.isFoil())
|
if (card.isFoil())
|
||||||
cardKey = new PaperCard(card.getRules(), card.getEdition(), card.getRarity(), card.getArtIndex(),
|
cardKey = card.getUnFoiled();
|
||||||
false, card.getCollectorNumber(), card.getArtist());
|
|
||||||
else
|
else
|
||||||
cardKey = card.getFoiled();
|
cardKey = card.getFoiled();
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class CardDetailUtil {
|
public class CardDetailUtil {
|
||||||
@@ -215,6 +216,17 @@ public class CardDetailUtil {
|
|||||||
ptText.append(card.getDefense());
|
ptText.append(card.getDefense());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (card.isAttraction()) {
|
||||||
|
ptText.append(Localizer.getInstance().getMessage("lblLights")).append(": (");
|
||||||
|
Set<Integer> lights = card.getAttractionLights();
|
||||||
|
//TODO: It'd be really nice if the actual lights were drawn as symbols here. Need to look into that...
|
||||||
|
if (lights == null || lights.isEmpty())
|
||||||
|
ptText.append(Localizer.getInstance().getMessage("lblNone"));
|
||||||
|
else
|
||||||
|
ptText.append(StringUtils.join(lights, ", "));
|
||||||
|
ptText.append(")");
|
||||||
|
}
|
||||||
|
|
||||||
return ptText.toString();
|
return ptText.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
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.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.card.CardEdition;
|
import forge.card.CardEdition;
|
||||||
import forge.card.CardRarity;
|
import forge.card.CardRarity;
|
||||||
@@ -1095,8 +1097,7 @@ public class AdvancedSearch {
|
|||||||
|
|
||||||
private static abstract class FilterEvaluator<T extends InventoryItem, V> {
|
private static abstract class FilterEvaluator<T extends InventoryItem, V> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public final Filter<T> createFilter(FilterOption option, FilterOperator operator) {
|
public final Filter<T> createFilter(FilterOption option, FilterOperator operator, List<V> values) {
|
||||||
final List<V> values = getValues(option, operator);
|
|
||||||
if (values == null || values.isEmpty()) {
|
if (values == null || values.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1127,7 +1128,26 @@ public class AdvancedSearch {
|
|||||||
return new Filter<>(option, operator, caption, predicate);
|
return new Filter<>(option, operator, caption, predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final Filter<T> createFilter(FilterOption option, FilterOperator operator) {
|
||||||
|
final List<V> values = getValues(option, operator);
|
||||||
|
return createFilter(option, operator, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public final Filter<T> createFilter(FilterOption option, FilterOperator operator, String initialValueText) {
|
||||||
|
final List<V> values;
|
||||||
|
try {
|
||||||
|
values = getValuesFromString(initialValueText, option, operator);
|
||||||
|
}
|
||||||
|
catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return createFilter(option, operator, values);
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract List<V> getValues(FilterOption option, FilterOperator operator);
|
protected abstract List<V> getValues(FilterOption option, FilterOperator operator);
|
||||||
|
protected abstract List<V> getValuesFromString(String valueText, FilterOption option, FilterOperator operator);
|
||||||
protected abstract String getCaption(List<V> values, FilterOption option, FilterOperator operator);
|
protected abstract String getCaption(List<V> values, FilterOption option, FilterOperator operator);
|
||||||
protected abstract V getItemValue(T input);
|
protected abstract V getItemValue(T input);
|
||||||
|
|
||||||
@@ -1147,6 +1167,11 @@ public class AdvancedSearch {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Boolean> getValuesFromString(String valueText, FilterOption option, FilterOperator operator) {
|
||||||
|
return getValues(option, operator);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getCaption(List<Boolean> values, FilterOption option, FilterOperator operator) {
|
protected String getCaption(List<Boolean> values, FilterOption option, FilterOperator operator) {
|
||||||
return String.format(operator.formatStr, option.name);
|
return String.format(operator.formatStr, option.name);
|
||||||
@@ -1190,6 +1215,11 @@ public class AdvancedSearch {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Integer> getValuesFromString(String valueText, FilterOption option, FilterOperator operator) {
|
||||||
|
return Arrays.stream(valueText.split(";")).map(String::trim).map(Integer::parseInt).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getCaption(List<Integer> values, FilterOption option, FilterOperator operator) {
|
protected String getCaption(List<Integer> values, FilterOption option, FilterOperator operator) {
|
||||||
if (operator.valueCount == FilterValueCount.TWO) {
|
if (operator.valueCount == FilterValueCount.TWO) {
|
||||||
@@ -1218,6 +1248,11 @@ public class AdvancedSearch {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getValuesFromString(String valueText, FilterOption option, FilterOperator operator) {
|
||||||
|
return Lists.newArrayList(valueText);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getCaption(List<String> values, FilterOption option, FilterOperator operator) {
|
protected String getCaption(List<String> values, FilterOption option, FilterOperator operator) {
|
||||||
return String.format(operator.formatStr, option.name, values.get(0));
|
return String.format(operator.formatStr, option.name, values.get(0));
|
||||||
@@ -1247,6 +1282,20 @@ public class AdvancedSearch {
|
|||||||
return SGuiChoose.getChoices(message, 0, max, choices, null, toLongString);
|
return SGuiChoose.getChoices(message, 0, max, choices, null, toLongString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<V> getValuesFromString(String valueText, FilterOption option, FilterOperator operator) {
|
||||||
|
String[] values = valueText.split(";");
|
||||||
|
return choices.stream().filter((choice) -> Arrays.stream(values).anyMatch((name) -> eitherStringMatches(choice, name))).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean eitherStringMatches(V choice, String name) {
|
||||||
|
if(toLongString != null && name.equals(toLongString.apply(choice)))
|
||||||
|
return true;
|
||||||
|
if(toShortString != null)
|
||||||
|
return name.equals(toShortString.apply(choice));
|
||||||
|
return name.equals(choice.toString());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getCaption(List<V> values, FilterOption option, FilterOperator operator) {
|
protected String getCaption(List<V> values, FilterOption option, FilterOperator operator) {
|
||||||
String valuesStr;
|
String valuesStr;
|
||||||
@@ -1330,6 +1379,26 @@ public class AdvancedSearch {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Map<String, Integer>> getValuesFromString(String valueText, FilterOption option, FilterOperator operator) {
|
||||||
|
int amount = -1;
|
||||||
|
String cardName;
|
||||||
|
if(operator == FilterOperator.CONTAINS_X_COPIES_OF_CARD) {
|
||||||
|
//Take the format "2 Mountain"
|
||||||
|
String[] split = valueText.split(" ", 2);
|
||||||
|
amount = Integer.parseInt(split[0]);
|
||||||
|
cardName = split[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cardName = valueText;
|
||||||
|
Map<String, Integer> map = new HashMap<>();
|
||||||
|
map.put(cardName, amount);
|
||||||
|
|
||||||
|
List<Map<String, Integer>> values = new ArrayList<>();
|
||||||
|
values.add(map);
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getCaption(List<Map<String, Integer>> values, FilterOption option, FilterOperator operator) {
|
protected String getCaption(List<Map<String, Integer>> values, FilterOption option, FilterOperator operator) {
|
||||||
Entry<String, Integer> entry = values.get(0).entrySet().iterator().next();
|
Entry<String, Integer> entry = values.get(0).entrySet().iterator().next();
|
||||||
@@ -1381,6 +1450,40 @@ public class AdvancedSearch {
|
|||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends InventoryItem> Filter<T> getFilter(Class<? super T> type, String filterText) {
|
||||||
|
String[] words = filterText.split(" ", 3);
|
||||||
|
if(words.length < 2)
|
||||||
|
{
|
||||||
|
System.out.printf("Unable to generate filter from expression '%s'%n", filterText);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String filterValue = words.length > 2 ? words[2] : "";
|
||||||
|
|
||||||
|
FilterOption option;
|
||||||
|
try {
|
||||||
|
option = FilterOption.valueOf(words[0]);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
System.out.printf("Unable to generate filter from FilterOption '%s'%n", words[0]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(option.type != type)
|
||||||
|
{
|
||||||
|
System.out.printf("Unable to generate filter from FilterOption '%s' - filter type '%s' != option type '%s' %n", words[0], type, option.type);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterOperator operator;
|
||||||
|
try {
|
||||||
|
operator = FilterOperator.valueOf(words[1]);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
System.out.printf("Unable to generate filter from FilterOption '%s' - no matching operator '%s'%n", words[0], words[1]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Filter<T>) option.evaluator.createFilter(option, operator, filterValue);
|
||||||
|
}
|
||||||
|
|
||||||
public static class Filter<T extends InventoryItem> {
|
public static class Filter<T extends InventoryItem> {
|
||||||
private final FilterOption option;
|
private final FilterOption option;
|
||||||
private final FilterOperator operator;
|
private final FilterOperator operator;
|
||||||
|
|||||||
@@ -66,6 +66,12 @@ public enum ItemManagerConfig {
|
|||||||
null, null, 4, 0),
|
null, null, 4, 0),
|
||||||
PLANAR_DECK_EDITOR(SColumnUtil.getCatalogDefaultColumns(true), true, false, true,
|
PLANAR_DECK_EDITOR(SColumnUtil.getCatalogDefaultColumns(true), true, false, true,
|
||||||
null, null, 4, 0),
|
null, null, 4, 0),
|
||||||
|
ATTRACTION_POOL(SColumnUtil.getAttractionPoolDefaultColumns(), false, false, true,
|
||||||
|
null, null, 4, 0),
|
||||||
|
ATTRACTION_DECK_EDITOR(SColumnUtil.getCatalogDefaultColumns(true), false, false, true,
|
||||||
|
null, null, 4, 0),
|
||||||
|
ATTRACTION_DECK_EDITOR_LIMITED(SColumnUtil.getCatalogDefaultColumns(false), false, false, true,
|
||||||
|
null, null, 4, 0),
|
||||||
COMMANDER_POOL(SColumnUtil.getCatalogDefaultColumns(true), true, false, false,
|
COMMANDER_POOL(SColumnUtil.getCatalogDefaultColumns(true), true, false, false,
|
||||||
null, null, 4, 0),
|
null, null, 4, 0),
|
||||||
COMMANDER_SECTION(SColumnUtil.getCatalogDefaultColumns(true), true, false, true,
|
COMMANDER_SECTION(SColumnUtil.getCatalogDefaultColumns(true), true, false, true,
|
||||||
|
|||||||
@@ -143,6 +143,22 @@ public final class SColumnUtil {
|
|||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Map<ColumnDef, ItemColumnConfig> getAttractionPoolDefaultColumns() {
|
||||||
|
//Similar to special card pool, but show the collector number and hide the type.
|
||||||
|
List<ColumnDef> colDefs = new ArrayList<>();
|
||||||
|
colDefs.add(ColumnDef.FAVORITE);
|
||||||
|
colDefs.add(ColumnDef.NAME);
|
||||||
|
colDefs.add(ColumnDef.RARITY);
|
||||||
|
colDefs.add(ColumnDef.SET);
|
||||||
|
colDefs.add(ColumnDef.COLLECTOR_ORDER);
|
||||||
|
|
||||||
|
Map<ColumnDef, ItemColumnConfig> columns = getColumns(colDefs);
|
||||||
|
columns.get(ColumnDef.FAVORITE).setSortPriority(1);
|
||||||
|
columns.get(ColumnDef.NAME).setSortPriority(2);
|
||||||
|
columns.get(ColumnDef.COLLECTOR_ORDER).setSortPriority(3);
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
public static Map<ColumnDef, ItemColumnConfig> getSpellShopDefaultColumns() {
|
public static Map<ColumnDef, ItemColumnConfig> getSpellShopDefaultColumns() {
|
||||||
Map<ColumnDef, ItemColumnConfig> columns = getCardColumns(ColumnDef.QUANTITY, false, true, true, false, false);
|
Map<ColumnDef, ItemColumnConfig> columns = getCardColumns(ColumnDef.QUANTITY, false, true, true, false, false);
|
||||||
columns.get(ColumnDef.OWNED).setSortPriority(1);
|
columns.get(ColumnDef.OWNED).setSortPriority(1);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public final class FModel {
|
|||||||
private static GameFormat.Collection formats;
|
private static GameFormat.Collection formats;
|
||||||
private static ItemPool<PaperCard> uniqueCardsNoAlt, allCardsNoAlt, planechaseCards, archenemyCards,
|
private static ItemPool<PaperCard> uniqueCardsNoAlt, allCardsNoAlt, planechaseCards, archenemyCards,
|
||||||
brawlCommander, oathbreakerCommander, tinyLeadersCommander, commanderPool,
|
brawlCommander, oathbreakerCommander, tinyLeadersCommander, commanderPool,
|
||||||
avatarPool, conspiracyPool, dungeonPool;
|
avatarPool, conspiracyPool, dungeonPool, attractionPool;
|
||||||
|
|
||||||
public static void initialize(final IProgressBar progressBar, Function<ForgePreferences, Void> adjustPrefs) {
|
public static void initialize(final IProgressBar progressBar, Function<ForgePreferences, Void> adjustPrefs) {
|
||||||
//init version to log
|
//init version to log
|
||||||
@@ -294,6 +294,7 @@ public final class FModel {
|
|||||||
allCardsNoAlt = getAllCardsNoAlt();
|
allCardsNoAlt = getAllCardsNoAlt();
|
||||||
archenemyCards = getArchenemyCards();
|
archenemyCards = getArchenemyCards();
|
||||||
planechaseCards = getPlanechaseCards();
|
planechaseCards = getPlanechaseCards();
|
||||||
|
attractionPool = getAttractionPool();
|
||||||
if (GuiBase.getInterface().isLibgdxPort()) {
|
if (GuiBase.getInterface().isLibgdxPort()) {
|
||||||
//preload mobile Itempool
|
//preload mobile Itempool
|
||||||
uniqueCardsNoAlt = getUniqueCardsNoAlt();
|
uniqueCardsNoAlt = getUniqueCardsNoAlt();
|
||||||
@@ -389,6 +390,11 @@ public final class FModel {
|
|||||||
return dungeonPool;
|
return dungeonPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ItemPool<PaperCard> getAttractionPool() {
|
||||||
|
if (attractionPool == null)
|
||||||
|
return ItemPool.createFrom(getMagicDb().getVariantCards().getAllCards(Predicates.compose(CardRulesPredicates.Presets.IS_ATTRACTION, PaperCard.FN_GET_RULES)), PaperCard.class);
|
||||||
|
return attractionPool;
|
||||||
|
}
|
||||||
private static boolean keywordsLoaded = false;
|
private static boolean keywordsLoaded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1118,6 +1118,8 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
|||||||
case SchemeDeck:
|
case SchemeDeck:
|
||||||
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoSchemeDeck"), localizer.getMessage("lblClosestToTop"), choices, null);
|
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoSchemeDeck"), localizer.getMessage("lblClosestToTop"), choices, null);
|
||||||
break;
|
break;
|
||||||
|
case AttractionDeck:
|
||||||
|
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoAttractionDeck"), localizer.getMessage("lblClosestToTop"), choices, null);
|
||||||
case Stack:
|
case Stack:
|
||||||
choices = getGui().order(localizer.getMessage("lblChooseOrderCopiesCast"), localizer.getMessage("lblPutFirst"), choices, null);
|
choices = getGui().order(localizer.getMessage("lblChooseOrderCopiesCast"), localizer.getMessage("lblPutFirst"), choices, null);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public class DeckAIUtils {
|
|||||||
case Schemes: return localizer.getMessage("lblSchemeDeck");
|
case Schemes: return localizer.getMessage("lblSchemeDeck");
|
||||||
case Conspiracy: return /* TODO localise */ "Conspiracy";
|
case Conspiracy: return /* TODO localise */ "Conspiracy";
|
||||||
case Dungeon: return /* TODO localise */ "Dungeon";
|
case Dungeon: return /* TODO localise */ "Dungeon";
|
||||||
|
case Attractions: return /* TODO localize */ "Attractions";
|
||||||
default: return /* TODO better handling */ "UNKNOWN";
|
default: return /* TODO better handling */ "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user