diff --git a/forge-core/src/main/java/forge/CardStorageReader.java b/forge-core/src/main/java/forge/CardStorageReader.java index 3911b078599..5dec2273b39 100644 --- a/forge-core/src/main/java/forge/CardStorageReader.java +++ b/forge-core/src/main/java/forge/CardStorageReader.java @@ -101,7 +101,6 @@ public class CardStorageReader { } this.charset = Charset.forName(CardStorageReader.DEFAULT_CHARSET_NAME); - } // CardReader() private List loadCardsInRange(final List files, final int from, final int to) { diff --git a/forge-core/src/main/java/forge/StaticData.java b/forge-core/src/main/java/forge/StaticData.java index 7c327565010..2e5f5c76da7 100644 --- a/forge-core/src/main/java/forge/StaticData.java +++ b/forge-core/src/main/java/forge/StaticData.java @@ -139,7 +139,7 @@ public class StaticData { variantCards.initialize(false, false, enableUnknownCards); } - if (this.tokenReader != null){ + if (this.tokenReader != null) { final Map tokens = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); for (CardRules card : this.tokenReader.loadCards()) { diff --git a/forge-core/src/main/java/forge/card/CardRules.java b/forge-core/src/main/java/forge/card/CardRules.java index 71a36a2fe45..ce0e8600e22 100644 --- a/forge-core/src/main/java/forge/card/CardRules.java +++ b/forge-core/src/main/java/forge/card/CardRules.java @@ -22,6 +22,7 @@ import java.util.*; import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import forge.card.mana.IParserManaCost; @@ -385,6 +386,12 @@ public final class CardRules implements ICardCharacteristics { private int deltaHand; private int deltaLife; + private List tokens; + + public List getTokens() { + return tokens; + } + public int getHand() { return deltaHand; } public int getLife() { return deltaLife; } public void setVanguardProperties(String pt) { @@ -420,6 +427,8 @@ public final class CardRules implements ICardCharacteristics { private String handLife = null; private String normalizedName = ""; + private List tokens = Lists.newArrayList(); + // fields to build CardAiHints private boolean removedFromAIDecks = false; private boolean removedFromRandomDecks = false; @@ -453,6 +462,7 @@ public final class CardRules implements ICardCharacteristics { this.meldWith = ""; this.partnerWith = ""; this.normalizedName = ""; + this.tokens = Lists.newArrayList(); } /** @@ -474,6 +484,7 @@ public final class CardRules implements ICardCharacteristics { result.setNormalizedName(this.normalizedName); result.meldWith = this.meldWith; result.partnerWith = this.partnerWith; + result.tokens = tokens.isEmpty() ? Collections.emptyList() : tokens; if (StringUtils.isNotBlank(handLife)) result.setVanguardProperties(handLife); return result; @@ -506,6 +517,18 @@ public final class CardRules implements ICardCharacteristics { String key = colonPos > 0 ? line.substring(0, colonPos) : line; String value = colonPos > 0 ? line.substring(1+colonPos).trim() : null; + if (value != null) { + int tokIdx = value.indexOf("TokenScript$"); + if (tokIdx > 0) { + String tokenParam = value.substring(tokIdx + 12).trim(); + int endIdx = tokenParam.indexOf("|"); + if (endIdx > 0) { + tokenParam = tokenParam.substring(0, endIdx).trim(); + } + this.tokens.addAll(Arrays.asList(tokenParam.split(","))); + } + } + switch (key.charAt(0)) { case 'A': if ("A".equals(key)) { diff --git a/forge-core/src/main/java/forge/card/DeckHints.java b/forge-core/src/main/java/forge/card/DeckHints.java index 541313dd043..c9140947f1f 100644 --- a/forge-core/src/main/java/forge/card/DeckHints.java +++ b/forge-core/src/main/java/forge/card/DeckHints.java @@ -12,7 +12,9 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; +import forge.StaticData; import forge.item.PaperCard; +import forge.token.TokenDb; import forge.util.PredicateString.StringOp; import forge.util.collect.FCollection; @@ -27,6 +29,8 @@ public class DeckHints { * Enum of types of DeckHints. */ public enum Type { + /** extra logic */ + MODIFIER, /** The Ability */ ABILITY, /** The Color. */ @@ -42,7 +46,8 @@ public class DeckHints { } private boolean valid = false; - public List> filters = null; + private boolean tokens = true; + private List> filters = null; /** * Construct a DeckHints from the SVar string. @@ -56,6 +61,12 @@ public class DeckHints { for (String piece : pieces) { Pair pair = parseHint(piece.trim()); if (pair != null) { + if (pair.getKey() == Type.MODIFIER) { + if (pair.getRight().contains("NoToken")) { + tokens = false; + } + continue; + } if (filters == null) { filters = new ArrayList<>(); } @@ -196,7 +207,32 @@ public class DeckHints { } private Iterable getMatchingItems(Iterable source, Predicate predicate, Function fn) { - return Iterables.filter(source, Predicates.compose(predicate, fn)); + // TODO should token generators be counted differently for their potential? + return Iterables.filter(source, Predicates.compose(tokens ? rulesWithTokens(predicate) : predicate, fn)); + } + + public static Predicate rulesWithTokens(final Predicate predicate) { + final TokenDb tdb; + if (StaticData.instance() != null) { + // not available on some test setups + tdb = StaticData.instance().getAllTokens(); + } else { + tdb = null; + } + return new Predicate() { + @Override + public boolean apply(final CardRules card) { + if (predicate.apply(card)) { + return true; + } + for (String tok : card.getTokens()) { + if (tdb != null && tdb.containsRule(tok) && predicate.apply(tdb.getToken(tok).getRules())) { + return true; + } + } + return false; + } + }; } } diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 992463cbe3c..bbe548d05b1 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -75,7 +75,7 @@ public enum TrackableProperty { ChosenMode(TrackableTypes.StringType), ChosenSector(TrackableTypes.StringType), Sector(TrackableTypes.StringListType), - DraftAction(TrackableTypes.StringType), + DraftAction(TrackableTypes.StringListType), ClassLevel(TrackableTypes.IntegerType), RingLevel(TrackableTypes.IntegerType), CurrentRoom(TrackableTypes.StringType), diff --git a/forge-gui/res/cardsfolder/c/charix_the_raging_isle.txt b/forge-gui/res/cardsfolder/c/charix_the_raging_isle.txt index 8def63a21ae..f55189d54e3 100644 --- a/forge-gui/res/cardsfolder/c/charix_the_raging_isle.txt +++ b/forge-gui/res/cardsfolder/c/charix_the_raging_isle.txt @@ -3,7 +3,7 @@ ManaCost:2 U U Types:Legendary Creature Leviathan Crab PT:0/17 S:Mode$ RaiseCost | ValidTarget$ Card.Self | Activator$ Opponent | Type$ Spell | Amount$ 2 | Description$ Spells your opponents cast that target CARDNAME cost {2} more to cast. -A:AB$ Pump | Cost$ 3 | NumAtt$ +X | NumDef$ -X | SpellDescription$ CARDNAME gets +X/-X until end of turn, where X is the number of Islands you control. +A:AB$ Pump | Cost$ 3 | NumAtt$ +X | NumDef$ -X | SpellDescription$ NICKNAME gets +X/-X until end of turn, where X is the number of Islands you control. SVar:X:Count$Valid Island.YouCtrl DeckHints:Type$Island Oracle:Spells your opponents cast that target Charix, the Raging Isle cost {2} more to cast.\n{3}: Charix gets +X/-X until end of turn, where X is the number of Islands you control. diff --git a/forge-gui/res/cardsfolder/e/ecological_appreciation.txt b/forge-gui/res/cardsfolder/e/ecological_appreciation.txt index 5c2aa5095d1..58dcf4dc3b0 100644 --- a/forge-gui/res/cardsfolder/e/ecological_appreciation.txt +++ b/forge-gui/res/cardsfolder/e/ecological_appreciation.txt @@ -1,7 +1,7 @@ Name:Ecological Appreciation ManaCost:X 2 G Types:Sorcery -A:SP$ ChangeZone | Origin$ Library,Graveyard | Destination$ Library | ChangeType$ Creature.cmcLEX | ChangeNum$ 4 | DifferentNames$ True | Shuffle$ False | RememberChanged$ True | Reveal$ True | AILogic$ Intuition | SubAbility$ DBChangeZone1 | StackDescription$ SpellDescription | SpellDescription$ Search your library and graveyard for up to four creature cards with different names that each have mana value X or less and reveal them. +A:SP$ ChangeZone | Origin$ Library,Graveyard | Destination$ Library | ChangeType$ Creature.cmcLEX | ChangeNum$ 4 | DifferentNames$ True | Shuffle$ False | RememberChanged$ True | Reveal$ True | SubAbility$ DBChangeZone1 | StackDescription$ SpellDescription | SpellDescription$ Search your library and graveyard for up to four creature cards with different names that each have mana value X or less and reveal them. SVar:DBChangeZone1:DB$ ChangeZone | Origin$ Library | Destination$ Library | ChangeType$ Card.IsRemembered | Chooser$ Opponent | ChangeNum$ 2 | Mandatory$ True | NoLooking$ True | ForgetChanged$ True | Shuffle$ False | SelectPrompt$ Select two cards to shuffle into the library | SubAbility$ DBChangeZone2 | StackDescription$ SpellDescription | SpellDescription$ An opponent chooses two of those cards. Shuffle the chosen cards into your library SVar:DBChangeZone2:DB$ ChangeZoneAll | Origin$ Library | Destination$ Battlefield | ChangeType$ Card.IsRemembered | Shuffle$ True | SubAbility$ DBCleanup | StackDescription$ SpellDescription | SpellDescription$ and put the rest onto the battlefield. SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBExileSelf diff --git a/forge-gui/res/cardsfolder/r/rivaz_of_the_claw.txt b/forge-gui/res/cardsfolder/r/rivaz_of_the_claw.txt index 8a567d3483f..1288a0f60fc 100644 --- a/forge-gui/res/cardsfolder/r/rivaz_of_the_claw.txt +++ b/forge-gui/res/cardsfolder/r/rivaz_of_the_claw.txt @@ -9,6 +9,6 @@ T:Mode$ SpellCast | ValidCard$ Dragon.wasCastFromYourGraveyard | ValidActivating SVar:DBAnimate:DB$ Animate | Defined$ TriggeredCard | Duration$ Permanent | Triggers$ TrigDieExile SVar:TrigDieExile:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When this creature dies, exile it. SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Exile -DeckNeeds:Type$Dragon +DeckNeeds:Type$Dragon & Modifier$NoToken DeckHas:Ability$Graveyard Oracle:Menace\n{T}: Add two mana in any combination of colors. Spend this mana only to cast Dragon creature spells.\nOnce during each of your turns, you may cast a Dragon creature spell from your graveyard.\nWhenever you cast a Dragon creature spell from your graveyard, it gains "When this creature dies, exile it."