diff --git a/forge-core/src/main/java/forge/card/CardType.java b/forge-core/src/main/java/forge/card/CardType.java index e3713cf1f1b..ebc12e29266 100644 --- a/forge-core/src/main/java/forge/card/CardType.java +++ b/forge-core/src/main/java/forge/card/CardType.java @@ -188,6 +188,21 @@ public final class CardType implements Comparable, CardTypeView { return supertypes.remove(st); } + public boolean remove(final String str) { + boolean changed = false; + if (CardType.isASupertype(str) && supertypes.remove(stringToSupertype.get(str))) { + changed = true; + } else if (CardType.isACardType(str) && coreTypes.remove(stringToCoreType.get(str))) { + changed = true; + } else if (subtypes.remove(str)) { + changed = true; + } + if (changed) { + calculatedType = null; + } + return changed; + } + public boolean setCreatureTypes(Collection ctypes) { // if it isn't a creature then this has no effect if (!isCreature() && !isTribal()) { diff --git a/forge-game/src/main/java/forge/game/CardTraitBase.java b/forge-game/src/main/java/forge/game/CardTraitBase.java index b59c994a80c..59c47275a5d 100644 --- a/forge-game/src/main/java/forge/game/CardTraitBase.java +++ b/forge-game/src/main/java/forge/game/CardTraitBase.java @@ -46,6 +46,11 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView { protected Map sVars = Maps.newHashMap(); + protected Map intrinsicChangedTextColors = Maps.newHashMap(); + protected Map intrinsicChangedTextTypes = Maps.newHashMap(); + protected Map changedTextColors = Maps.newHashMap(); + protected Map changedTextTypes = Maps.newHashMap(); + /** Keys of descriptive (text) parameters. */ private static final ImmutableList descriptiveKeys = ImmutableList.builder() .add("Description", "SpellDescription", "StackDescription", "TriggerDescription").build(); @@ -53,6 +58,11 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView { private static final ImmutableList mutableKeys = ImmutableList.builder() .add("AddAbility").build(); + /** + * Keys that should not changed + */ + private static final ImmutableList noChangeKeys = ImmutableList.builder() + .add("TokenScript", "LegacyImage", "TokenImage").build(); /** * Sets the temporary. * @@ -449,9 +459,15 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView { } public void changeText() { + // copy changed text words into card trait there + this.changedTextColors = getHostCard().getChangedTextColorWords(); + this.changedTextTypes = getHostCard().getChangedTextTypeWords(); + for (final String key : this.mapParams.keySet()) { final String value = this.originalMapParams.get(key), newValue; - if (descriptiveKeys.contains(key)) { + if (noChangeKeys.contains(key)) { + continue; + } else if (descriptiveKeys.contains(key)) { // change descriptions differently newValue = AbilityUtils.applyDescriptionTextChangeEffects(value, this); } else if (mutableKeys.contains(key)) { @@ -515,4 +531,53 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView { public Set getSVars() { return sVars.keySet(); } + + public Map getChangedTextColors() { + return _combineChangedMap(intrinsicChangedTextColors, changedTextColors); + } + public Map getChangedTextTypes() { + return _combineChangedMap(intrinsicChangedTextTypes, changedTextTypes); + } + + private Map _combineChangedMap(Map input, Map output) { + // no need to do something, just return hash + if (input.isEmpty()) { + return output; + } + if (output.isEmpty()) { + return input; + } + // magic combine them + Map result = Maps.newHashMap(output); + for (Map.Entry e : input.entrySet()) { + String value = e.getValue(); + result.put(e.getKey(), output.containsKey(value) ? output.get(value) : value); + } + return result; + } + + public void changeTextIntrinsic(Map colorMap, Map typeMap) { + intrinsicChangedTextColors = colorMap; + intrinsicChangedTextTypes = typeMap; + for (final String key : this.mapParams.keySet()) { + final String value = this.originalMapParams.get(key), newValue; + if (noChangeKeys.contains(key)) { + continue; + } else if (descriptiveKeys.contains(key)) { + // change descriptions differently + newValue = AbilityUtils.applyTextChangeEffects(value, true, colorMap, typeMap); + }else if (this.getHostCard().hasSVar(value)) { + // don't change literal SVar names! + continue; + } else { + newValue = AbilityUtils.applyTextChangeEffects(value, false, colorMap, typeMap); + } + + if (newValue != null) { + this.mapParams.put(key, newValue); + } + } + // this does overwrite the original MapParams + this.originalMapParams = Maps.newHashMap(this.mapParams); + } } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 3401a5d6a80..ab4032c090a 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -1724,12 +1724,18 @@ public class AbilityUtils { } private static final String applyTextChangeEffects(final String def, final Card card, final boolean isDescriptive) { + return applyTextChangeEffects(def, isDescriptive, + card.getChangedTextColorWords(), card.getChangedTextTypeWords()); + } + + public static final String applyTextChangeEffects(final String def, final boolean isDescriptive, + Map colorMap, Map typeMap) { if (StringUtils.isEmpty(def)) { return def; } String replaced = def; - for (final Entry e : card.getChangedTextColorWords().entrySet()) { + for (final Entry e : colorMap.entrySet()) { final String key = e.getKey(); String value; if (key.equals("Any")) { @@ -1750,7 +1756,7 @@ public class AbilityUtils { replaced = replaced.replaceAll("(?)" + key, value); } } - for (final Entry e : card.getChangedTextTypeWords().entrySet()) { + for (final Entry e : typeMap.entrySet()) { final String key = e.getKey(); final String pkey = CardType.getPluralType(key); final String pvalue = getReplacedText(pkey, CardType.getPluralType(e.getValue()), isDescriptive); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 6fc7ffde41c..f56315a4a29 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -3640,6 +3640,12 @@ public class Card extends GameEntity implements Comparable { } } + public final void removeIntrinsicKeyword(final KeywordInterface s) { + if (currentState.removeIntrinsicKeyword(s)) { + currentState.getView().updateKeywords(this, currentState); + } + } + public Collection getExtrinsicKeyword() { return extrinsicKeyword.getValues(); } diff --git a/forge-game/src/main/java/forge/game/card/CardChangedWords.java b/forge-game/src/main/java/forge/game/card/CardChangedWords.java index d62f15dde32..ae8719dee69 100644 --- a/forge-game/src/main/java/forge/game/card/CardChangedWords.java +++ b/forge-game/src/main/java/forge/game/card/CardChangedWords.java @@ -7,8 +7,6 @@ import java.util.SortedMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; -import forge.card.CardType; - public final class CardChangedWords { private final SortedMap map = Maps.newTreeMap(); @@ -68,14 +66,9 @@ public final class CardChangedWords { // the actual change (b->c) resultCache.put(ccw.getOriginalWord(), ccw.getNewWord()); - - // possible plural form - final String singular = CardType.getPluralType(ccw.getOriginalWord()); - if (!singular.equals(ccw.getOriginalWord())) { - resultCache.put(singular, ccw.getNewWord()); - } } + // TODO should that be removed? for (final String key : ImmutableList.copyOf(resultCache.keySet())) { if (!key.equals("Any")) { resultCache.put(key.toLowerCase(), resultCache.get(key).toLowerCase()); diff --git a/forge-game/src/main/java/forge/game/card/CardState.java b/forge-game/src/main/java/forge/game/card/CardState.java index 483077cae84..5372ddfa18e 100644 --- a/forge-game/src/main/java/forge/game/card/CardState.java +++ b/forge-game/src/main/java/forge/game/card/CardState.java @@ -231,6 +231,9 @@ public class CardState extends GameObject { public final boolean removeIntrinsicKeyword(final String s) { return intrinsicKeywords.remove(s); } + public final boolean removeIntrinsicKeyword(final KeywordInterface s) { + return intrinsicKeywords.remove(s); + } public final FCollectionView getSpellAbilities() { FCollection newCol = new FCollection(manaAbilities); @@ -539,7 +542,7 @@ public class CardState extends GameObject { intrinsicKeywords.insert(inst); } } - + public void updateChangedText() { final List allAbs = ImmutableList.builder() .addAll(manaAbilities) @@ -554,4 +557,19 @@ public class CardState extends GameObject { } } } + + public void changeTextIntrinsic(Map colorMap, Map typeMap) { + final List allAbs = ImmutableList.builder() + .addAll(manaAbilities) + .addAll(nonManaAbilities) + .addAll(triggers) + .addAll(replacementEffects) + .addAll(staticAbilities) + .build(); + for (final CardTraitBase ctb : allAbs) { + if (ctb.isIntrinsic()) { + ctb.changeTextIntrinsic(colorMap, typeMap); + } + } + } } diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index 0f959f598d5..8db0b965455 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -281,6 +281,8 @@ public final class CardUtil { newCopy.setChangedCardKeywords(in.getChangedCardKeywords()); newCopy.setChangedCardTypes(in.getChangedCardTypesMap()); + newCopy.copyChangedTextFrom(in); + newCopy.setMeldedWith(in.getMeldedWith()); newCopy.setTimestamp(in.getTimestamp()); diff --git a/forge-game/src/main/java/forge/game/card/token/TokenInfo.java b/forge-game/src/main/java/forge/game/card/token/TokenInfo.java index d54b7b4256f..86f319baf80 100644 --- a/forge-game/src/main/java/forge/game/card/token/TokenInfo.java +++ b/forge-game/src/main/java/forge/game/card/token/TokenInfo.java @@ -12,6 +12,7 @@ import forge.game.Game; import forge.game.card.Card; import forge.game.card.CardFactory; import forge.game.card.CardFactoryUtil; +import forge.game.card.CardUtil; import forge.game.keyword.KeywordInterface; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -20,6 +21,8 @@ import forge.item.PaperToken; import java.util.List; import java.util.Map; +import org.apache.commons.lang3.StringUtils; + public class TokenInfo { final String name; final String imageName; @@ -229,10 +232,121 @@ public class TokenInfo { String edition = host.getSetCode(); PaperToken token = StaticData.instance().getAllTokens().getToken(script, edition); - // TODO add Card Text Change from SpellAbility - if (token != null) { - return Card.fromPaperCard(token, null, game); + final Card result = Card.fromPaperCard(token, null, game); + + // update Token with CardTextChanges + Map colorMap = sa.getChangedTextColors(); + Map typeMap = sa.getChangedTextTypes(); + if (!colorMap.isEmpty()) { + if (!result.isColorless()) { + // change Token Colors + byte color = CardUtil.getColors(result).getColor(); + + for (final Map.Entry e : colorMap.entrySet()) { + byte v = MagicColor.fromName(e.getValue()); + // Any used by Swirl the Mists + if ("Any".equals(e.getKey())) { + for (final byte c : MagicColor.WUBRG) { + // try to replace color flips + if ((color & c) != 0) { + color &= ~c; + color |= v; + } + } + } else { + byte c = MagicColor.fromName(e.getKey()); + // try to replace color flips + if ((color & c) != 0) { + color &= ~c; + color |= v; + } + } + } + + result.setColor(color); + } + } + if (!typeMap.isEmpty()) { + String oldName = result.getName(); + + CardType type = new CardType(result.getType()); + String joinedName = StringUtils.join(type.getSubtypes(), " "); + final boolean nameGenerated = oldName.equals(joinedName); + boolean typeChanged = false; + + if (!Iterables.isEmpty(type.getSubtypes())) { + for (final Map.Entry e : typeMap.entrySet()) { + if (type.hasSubtype(e.getKey())) { + type.remove(e.getKey()); + type.add(e.getValue()); + typeChanged = true; + } + } + } + + if (typeChanged) { + result.setType(type); + + // update generated Name + if (nameGenerated) { + result.setName(StringUtils.join(type.getSubtypes(), " ")); + } + } + } + + // replace Intrinsic Keyword + List toRemove = Lists.newArrayList(); + List toAdd = Lists.newArrayList(); + for (final KeywordInterface k : result.getCurrentState().getIntrinsicKeywords()) { + final String o = k.getOriginal(); + // only Modifiable should go there + if (!CardUtil.isKeywordModifiable(o)) { + continue; + } + String r = new String(o); + // replace types + for (final Map.Entry e : typeMap.entrySet()) { + final String key = e.getKey(); + final String pkey = CardType.getPluralType(key); + final String value = e.getValue(); + final String pvalue = CardType.getPluralType(e.getValue()); + r = r.replaceAll(pkey, pvalue); + r = r.replaceAll(key, value); + } + // replace color words + for (final Map.Entry e : colorMap.entrySet()) { + final String vName = e.getValue(); + final String vCaps = StringUtils.capitalize(vName); + final String vLow = vName.toLowerCase(); + if ("Any".equals(e.getKey())) { + for (final byte c : MagicColor.WUBRG) { + final String cName = MagicColor.toLongString(c); + final String cNameCaps = StringUtils.capitalize(cName); + final String cNameLow = cName.toLowerCase(); + r = r.replaceAll(cNameCaps, vCaps); + r = r.replaceAll(cNameLow, vLow); + } + } else { + final String cName = e.getKey(); + final String cNameCaps = StringUtils.capitalize(cName); + final String cNameLow = cName.toLowerCase(); + r = r.replaceAll(cNameCaps, vCaps); + r = r.replaceAll(cNameLow, vLow); + } + } + if (!r.equals(o)) { + toRemove.add(k); + toAdd.add(r); + } + } + for (final KeywordInterface k : toRemove) { + result.getCurrentState().removeIntrinsicKeyword(k); + } + result.addIntrinsicKeywords(toAdd); + + result.getCurrentState().changeTextIntrinsic(colorMap, typeMap); + return result; } return null; diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java b/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java index f00b65cf76d..98623cf6d72 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java @@ -96,6 +96,10 @@ public class KeywordCollection implements Iterable, Serializable { return result; } + public boolean remove(KeywordInterface keyword) { + return map.remove(keyword.getKeyword(), keyword); + } + public boolean removeAll(Iterable keywords) { boolean result = false; for (String k : keywords) { diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index afa679d269d..9eddd745b4a 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -1709,6 +1709,31 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } } + /* (non-Javadoc) + * @see forge.game.CardTraitBase#changeTextIntrinsic(java.util.Map, java.util.Map) + */ + @Override + public void changeTextIntrinsic(Map colorMap, Map typeMap) { + super.changeTextIntrinsic(colorMap, typeMap); + + if (subAbility != null) { + // if the parent of the subability is not this, + // then there might be a loop + if (subAbility.getParent() == this) { + subAbility.changeTextIntrinsic(colorMap, typeMap); + } + } + for (AbilitySub sa : additionalAbilities.values()) { + sa.changeTextIntrinsic(colorMap, typeMap); + } + + for (List list : additionalAbilityLists.values()) { + for (AbilitySub sa : list) { + sa.changeTextIntrinsic(colorMap, typeMap); + } + } + } + @Override public void setIntrinsic(boolean i) { super.setIntrinsic(i); diff --git a/forge-game/src/main/java/forge/game/trigger/Trigger.java b/forge-game/src/main/java/forge/game/trigger/Trigger.java index 8b39080b26f..a3dd3f3fa93 100644 --- a/forge-game/src/main/java/forge/game/trigger/Trigger.java +++ b/forge-game/src/main/java/forge/game/trigger/Trigger.java @@ -169,11 +169,7 @@ public abstract class Trigger extends TriggerReplacementBase { if (!desc.contains("ABILITY")) { return desc; } - SpellAbility sa = getOverridingAbility(); - if (sa == null && this.mapParams.containsKey("Execute")) { - sa = AbilityFactory.getAbility(state, this.mapParams.get("Execute")); - setOverridingAbility(sa); - } + SpellAbility sa = ensureAbility(); return replaceAbilityText(desc, sa); @@ -583,4 +579,48 @@ public abstract class Trigger extends TriggerReplacementBase { throw new RuntimeException("Trigger : clone() error, " + ex); } } + + + /* (non-Javadoc) + * @see forge.game.CardTraitBase#changeText() + */ + @Override + public void changeText() { + if (!isIntrinsic()) { + return; + } + super.changeText(); + + ensureAbility(); + + if (getOverridingAbility() != null) { + getOverridingAbility().changeText(); + } + } + + /* (non-Javadoc) + * @see forge.game.CardTraitBase#changeTextIntrinsic(java.util.Map, java.util.Map) + */ + @Override + public void changeTextIntrinsic(Map colorMap, Map typeMap) { + if (!isIntrinsic()) { + return; + } + super.changeTextIntrinsic(colorMap, typeMap); + + ensureAbility(); + + if (getOverridingAbility() != null) { + getOverridingAbility().changeTextIntrinsic(colorMap, typeMap); + } + } + + private SpellAbility ensureAbility() { + SpellAbility sa = getOverridingAbility(); + if (sa == null && hasParam("Execute")) { + sa = AbilityFactory.getAbility(getHostCard(), getParam("Execute")); + setOverridingAbility(sa); + } + return sa; + } } diff --git a/forge-gui/res/cardsfolder/b/brood_keeper.txt b/forge-gui/res/cardsfolder/b/brood_keeper.txt index 6a8fbb1b53f..d2a5d38bd28 100644 --- a/forge-gui/res/cardsfolder/b/brood_keeper.txt +++ b/forge-gui/res/cardsfolder/b/brood_keeper.txt @@ -3,8 +3,7 @@ ManaCost:3 R Types:Creature Human Shaman PT:2/3 T:Mode$ Attached | ValidSource$ Aura | ValidTarget$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever an Aura becomes attached to CARDNAME, create a 2/2 red Dragon creature token with flying. It has "{R}: This creature gets +1/+0 until end of turn." -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenName$ Dragon | TokenTypes$ Creature,Dragon | TokenOwner$ You | TokenColors$ Red | TokenPower$ 2 | TokenToughness$ 2 | TokenImage$ r 2 2 dragon M15 | TokenKeywords$ Flying | TokenAbilities$ BroodPump -SVar:BroodPump:AB$ Pump | Cost$ R | NumAtt$ +1 | SpellDescription$ CARDNAME gets +1/+0 until end of turn. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenOwner$ You | LegacyImage$ r 2 2 dragon M15 | TokenScript$ r_2_2_dragon_flying_firebreathing SVar:EnchantMe:Multiple SVar:Picture:http://www.wizards.com/global/images/magic/general/brood_keeper.jpg Oracle:Whenever an Aura becomes attached to Brood Keeper, create a 2/2 red Dragon creature token with flying. It has "{R}: This creature gets +1/+0 until end of turn." diff --git a/forge-gui/res/cardsfolder/d/dragon_egg.txt b/forge-gui/res/cardsfolder/d/dragon_egg.txt index d863c2e64c3..547e764ae7c 100644 --- a/forge-gui/res/cardsfolder/d/dragon_egg.txt +++ b/forge-gui/res/cardsfolder/d/dragon_egg.txt @@ -4,8 +4,7 @@ Types:Creature Dragon Egg PT:0/2 K:Defender T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, create a 2/2 red Dragon creature token with flying. It has "{R}: This creature gets +1/+0 until end of turn". -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenName$ Dragon | TokenTypes$ Creature,Dragon | TokenOwner$ You | TokenColors$ Red | TokenPower$ 2 | TokenToughness$ 2 | TokenImage$ r 2 2 dragon M14 | TokenKeywords$ Flying | TokenAbilities$ DragonPump -SVar:DragonPump:AB$ Pump | Cost$ R | NumAtt$ +1 | SpellDescription$ CARDNAME gets +1/+0 until end of turn. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenOwner$ You | LegacyImage$ r 2 2 dragon M14 | TokenScript$ r_2_2_dragon_flying_firebreathing SVar:SacMe:4 DeckHas:Ability$Token SVar:Picture:http://www.wizards.com/global/images/magic/general/dragon_egg.jpg diff --git a/forge-gui/res/cardsfolder/h/hunted_horror.txt b/forge-gui/res/cardsfolder/h/hunted_horror.txt index e6dbae0909c..91e6970860d 100644 --- a/forge-gui/res/cardsfolder/h/hunted_horror.txt +++ b/forge-gui/res/cardsfolder/h/hunted_horror.txt @@ -4,6 +4,6 @@ Types:Creature Horror PT:7/7 K:Trample T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, target opponent creates two 3/3 green Centaur creature tokens with protection from black. -SVar:TrigToken:DB$ Token | TokenAmount$ 2 | TokenName$ Centaur | TokenImage$ g 3 3 centaur pro black | TokenTypes$ Creature,Centaur | ValidTgts$ Opponent | TokenOwner$ Targeted | TokenColors$ Green | TokenPower$ 3 | TokenToughness$ 3 | TokenKeywords$ Protection from black +SVar:TrigToken:DB$ Token | TokenAmount$ 2 | TokenScript$ g_3_3_centaur_pro_black | ValidTgts$ Opponent | TokenOwner$ Targeted | LegacyImage$ g 3 3 centaur pro black SVar:Picture:http://www.wizards.com/global/images/magic/general/hunted_horror.jpg -Oracle:Trample\nWhen Hunted Horror enters the battlefield, target opponent creates two 3/3 green Centaur creature tokens with protection from black. \ No newline at end of file +Oracle:Trample\nWhen Hunted Horror enters the battlefield, target opponent creates two 3/3 green Centaur creature tokens with protection from black. diff --git a/forge-gui/res/cardsfolder/j/jedit_ojanen_of_efrava.txt b/forge-gui/res/cardsfolder/j/jedit_ojanen_of_efrava.txt index b101c2d6aac..401d85ea186 100644 --- a/forge-gui/res/cardsfolder/j/jedit_ojanen_of_efrava.txt +++ b/forge-gui/res/cardsfolder/j/jedit_ojanen_of_efrava.txt @@ -5,7 +5,7 @@ PT:5/5 K:Forestwalk T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME attacks or blocks, create a 2/2 green Cat Warrior creature token with forestwalk. T:Mode$ Blocks | ValidCard$ Card.Self | Execute$ TrigToken | Secondary$ True | TriggerDescription$ Whenever CARDNAME attacks or blocks, create a 2/2 green Cat Warrior creature token with forestwalk. -SVar:TrigToken:DB$Token | TokenAmount$ 1 | TokenName$ Cat Warrior | TokenTypes$ Creature,Cat,Warrior | TokenOwner$ You | TokenColors$ Green | TokenPower$ 2 | TokenToughness$ 2 | TokenKeywords$ Forestwalk +SVar:TrigToken:DB$Token | TokenAmount$ 1 | TokenOwner$ You | TokenScript$ g_2_2_cat_warrior_forestwalk SVar:HasAttackEffect:TRUE SVar:HasBlockEffect:TRUE SVar:Picture:http://www.wizards.com/global/images/magic/general/jedit_ojanen_of_efrava.jpg diff --git a/forge-gui/res/cardsfolder/k/kalonian_twingrove.txt b/forge-gui/res/cardsfolder/k/kalonian_twingrove.txt index d8431f601fe..09459f1ef24 100644 --- a/forge-gui/res/cardsfolder/k/kalonian_twingrove.txt +++ b/forge-gui/res/cardsfolder/k/kalonian_twingrove.txt @@ -5,8 +5,7 @@ PT:*/* S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAMEs power and toughness are each equal to the number of Forests you control. SVar:X:Count$Valid Forest.YouCtrl T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a green Treefolk Warrior creature token with "This creature's power and toughness are each equal to the number of Forests you control." -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenName$ Treefolk Warrior | TokenTypes$ Creature,Treefolk,Warrior | TokenOwner$ You | TokenColors$ Green | TokenPower$ X | TokenToughness$ X | TokenStaticAbilities$ TokenPT | TokenSVars$ X | References$ X | TokenImage$ g x x treefolk warrior -SVar:TokenPT:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAME's power and toughness are each equal to the number of Forests you control. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_x_x_treefolk_warrior | LegacyImage$ g x x treefolk warrior SVar:BuffedBy:Forest SVar:NeedsToPlayVar:X GE3 SVar:Picture:http://www.wizards.com/global/images/magic/general/kalonian_twingrove.jpg diff --git a/forge-gui/res/cardsfolder/l/lord_windgrace.txt b/forge-gui/res/cardsfolder/l/lord_windgrace.txt index d6223fe7aed..c6521a3129f 100644 --- a/forge-gui/res/cardsfolder/l/lord_windgrace.txt +++ b/forge-gui/res/cardsfolder/l/lord_windgrace.txt @@ -8,6 +8,6 @@ SVar:DBDraw2:DB$ Draw | NumCards$ 1 | ConditionDefined$ Remembered | ConditionPr SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True A:AB$ ChangeZone | Cost$ SubCounter<3/LOYALTY> | Planeswalker$ True | Origin$ Graveyard | Destination$ Battlefield | TargetMin$ 0 | TargetMax$ 2 | TgtPrompt$ Choose target land card in your graveyard | ValidTgts$ Land.YouCtrl | SpellDescription$ Return up to two target land cards from your graveyard to the battlefield. A:AB$ Destroy | Cost$ SubCounter<11/LOYALTY> | Planeswalker$ True | Ultimate$ True | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | TargetMin$ 0 | TargetMax$ 6 | SubAbility$ DBToken | SpellDescription$ Destroy up to six target nonland permanents, then create six 2/2 green Cat Warrior creature tokens with forestwalk. -SVar:DBToken:DB$ Token | TokenAmount$ 6 | TokenName$ Cat Warrior | TokenTypes$ Creature,Cat,Warrior | TokenOwner$ You | TokenColors$ Green | TokenPower$ 2 | TokenToughness$ 2 | TokenKeywords$ Forestwalk | TokenImage$ g 2 2 cat warrior c18 +SVar:DBToken:DB$ Token | TokenAmount$ 6 | TokenOwner$ You | TokenScript$ g_2_2_cat_warrior_forestwalk | LegacyImage$ g 2 2 cat warrior c18 K:CARDNAME can be your commander. Oracle:[+2]: Discard a card, then draw a card. If a land card is discarded this way, draw an additional card.\n[-3]: Return up to two target land cards from your graveyard to the battlefield.\n[-11]: Destroy up to six target nonland permanents, then create six 2/2 green Cat Warrior creature tokens with forestwalk.\nLord Windgrace can be your commander. diff --git a/forge-gui/res/cardsfolder/m/mitotic_slime.txt b/forge-gui/res/cardsfolder/m/mitotic_slime.txt index 74bc80efade..74bb2ec3300 100644 --- a/forge-gui/res/cardsfolder/m/mitotic_slime.txt +++ b/forge-gui/res/cardsfolder/m/mitotic_slime.txt @@ -3,8 +3,6 @@ ManaCost:4 G Types:Creature Ooze PT:4/4 T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigTokenSenior | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, create two 2/2 green Ooze creature tokens. They have "When this creature dies, create two 1/1 green Ooze creature tokens." -SVar:TrigTokenSenior:DB$Token | TokenImage$ g 2 2 ooze | TokenName$ Ooze | TokenTypes$ Creature,Ooze | TokenColors$ Green | TokenOwner$ You | TokenPower$ 2 | TokenToughness$ 2 | TokenAmount$ 2 | TokenTriggers$ TriggerJunior | TokenSVars$ TrigTokenJunior -SVar:TriggerJunior:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigTokenJunior | TriggerController$ TriggeredCardController | TriggerDescription$ When this creature dies, create two 1/1 green Ooze creature tokens. -SVar:TrigTokenJunior:DB$Token | TokenImage$ g 1 1 ooze | TokenName$ Ooze | TokenTypes$ Creature,Ooze | TokenColors$ Green | TokenOwner$ You | TokenPower$ 1 | TokenToughness$ 1 | TokenAmount$ 2 +SVar:TrigTokenSenior:DB$Token | LegacyImage$ g 2 2 ooze | TokenScript$ g_2_2_ooze_mitotic | TokenOwner$ You | TokenAmount$ 2 SVar:Picture:http://www.wizards.com/global/images/magic/general/mitotic_slime.jpg Oracle:When Mitotic Slime dies, create two 2/2 green Ooze creature tokens. They have "When this creature dies, create two 1/1 green Ooze creature tokens." diff --git a/forge-gui/res/cardsfolder/n/nesting_dragon.txt b/forge-gui/res/cardsfolder/n/nesting_dragon.txt index c72018dc457..9b079008b9d 100644 --- a/forge-gui/res/cardsfolder/n/nesting_dragon.txt +++ b/forge-gui/res/cardsfolder/n/nesting_dragon.txt @@ -4,8 +4,5 @@ Types:Creature Dragon PT:5/4 K:Flying T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Landfall Whenever a land enters the battlefield under your control, create a 0/2 red Dragon creature token with defender and "When this creature dies, create a 2/2 red Dragon creature token with flying and '{R}: This creature gets +1/+0 until end of turn.'" -SVar:TrigToken:DB$ Token | TokenImage$ r 0 2 dragon egg | TokenAmount$ 1 | TokenName$ Dragon Egg | TokenTypes$ Creature,Dragon,Egg | TokenOwner$ You | TokenColors$ Red | TokenPower$ 0 | TokenToughness$ 2 | TokenKeywords$ Defender | TokenTriggers$ TriggerDies | TokenSVars$ TrigToken2,DragonPump -SVar:TriggerDies:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken2 | TriggerController$ TriggeredCardController | TriggerDescription$ When this creature dies, create a 2/2 red Dragon creature token with flying and "{R}: This creature gets +1/+0 until end of turn." -SVar:TrigToken2:DB$Token | TokenImage$ r 2 2 dragon c18 | TokenAmount$ 1 | TokenName$ Dragon | TokenTypes$ Creature,Dragon | TokenOwner$ You | TokenColors$ Red | TokenPower$ 2 | TokenToughness$ 2 | TokenKeywords$ Flying | TokenAbilities$ DragonPump -SVar:DragonPump:AB$ Pump | Cost$ R | NumAtt$ +1 | SpellDescription$ CARDNAME gets +1/+0 until end of turn. -Oracle:Flying\nLandfall — Whenever a land enters the battlefield under your control, create a 0/2 red Dragon Egg creature token with defender and "When this creature dies, create a 2/2 red Dragon creature token with flying and '{R}: This creature gets +1/+0 until end of turn.'" \ No newline at end of file +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenOwner$ You | LegacyImage$ r 0 2 dragon egg | TokenScript$ r_0_2_dragon_egg_defender_hatches_dragon +Oracle:Flying\nLandfall — Whenever a land enters the battlefield under your control, create a 0/2 red Dragon Egg creature token with defender and "When this creature dies, create a 2/2 red Dragon creature token with flying and '{R}: This creature gets +1/+0 until end of turn.'" diff --git a/forge-gui/res/cardsfolder/r/reef_worm.txt b/forge-gui/res/cardsfolder/r/reef_worm.txt index 02a9657a8c4..bf72bb1a6ed 100644 --- a/forge-gui/res/cardsfolder/r/reef_worm.txt +++ b/forge-gui/res/cardsfolder/r/reef_worm.txt @@ -2,11 +2,7 @@ Name:Reef Worm ManaCost:3 U Types:Creature Worm PT:0/1 -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigTokenFish | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, create a 3/3 blue Fish creature token with "When this creature dies, create a 6/6 blue Whale creature token with 'When this creature dies, create a 9/9 blue Kraken creature token.'" -SVar:TrigTokenFish:DB$Token | TokenImage$ u 3 3 fish | TokenName$ Fish | TokenTypes$ Creature,Fish | TokenColors$ Blue | TokenOwner$ You | TokenPower$ 3 | TokenToughness$ 3 | TokenAmount$ 1 | TokenTriggers$ TriggerWhale | TokenSVars$ TrigTokenWhale,TriggerKraken,TrigTokenKraken -SVar:TriggerWhale:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigTokenWhale | TriggerController$ TriggeredCardController | TriggerDescription$ When this creature dies, create a 6/6 blue Whale creature token with "When this creature dies, create a 9/9 blue Kraken creature token." -SVar:TrigTokenWhale:DB$Token | TokenImage$ u 6 6 whale | TokenName$ Whale | TokenTypes$ Creature,Whale | TokenColors$ Blue | TokenOwner$ You | TokenPower$ 6 | TokenToughness$ 6 | TokenAmount$ 1 | TokenTriggers$ TriggerKraken | TokenSVars$ TrigTokenKraken -SVar:TriggerKraken:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigTokenKraken | TriggerController$ TriggeredCardController | TriggerDescription$ When this creature dies, create a 9/9 blue Kraken creature token. -SVar:TrigTokenKraken:DB$Token | TokenImage$ u 9 9 kraken c14 | TokenName$ Kraken | TokenTypes$ Creature,Kraken | TokenColors$ Blue | TokenOwner$ You | TokenPower$ 9 | TokenToughness$ 9 | TokenAmount$ 1 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, create a 3/3 blue Fish creature token with "When this creature dies, create a 6/6 blue Whale creature token with 'When this creature dies, create a 9/9 blue Kraken creature token.'" +SVar:TrigToken:DB$Token | LegacyImage$ u 3 3 fish | TokenScript$ u_3_3_fish_hatches_whale | TokenOwner$ You | TokenAmount$ 1 SVar:Picture:http://www.wizards.com/global/images/magic/general/reef_worm.jpg -Oracle:When Reef Worm dies, create a 3/3 blue Fish creature token with "When this creature dies, create a 6/6 blue Whale creature token with 'When this creature dies, create a 9/9 blue Kraken creature token.'" \ No newline at end of file +Oracle:When Reef Worm dies, create a 3/3 blue Fish creature token with "When this creature dies, create a 6/6 blue Whale creature token with 'When this creature dies, create a 9/9 blue Kraken creature token.'" diff --git a/forge-gui/res/cardsfolder/r/riftmarked_knight.txt b/forge-gui/res/cardsfolder/r/riftmarked_knight.txt index ea73c94620e..b924807b1b8 100644 --- a/forge-gui/res/cardsfolder/r/riftmarked_knight.txt +++ b/forge-gui/res/cardsfolder/r/riftmarked_knight.txt @@ -6,6 +6,6 @@ K:Flanking K:Protection from black K:Suspend:3:1 W W T:Mode$ CounterRemoved | ValidCard$ Card.Self | TriggerZones$ Exile | CounterType$ TIME | Execute$ TrigToken | IsPresent$ Card.Self+counters_GE1_TIME | PresentZone$ Exile | PresentCompare$ EQ0 | TriggerDescription$ When the last time counter is removed from CARDNAME while it's exiled, create a 2/2 black Knight creature token with flanking, protection from white, and haste. -SVar:TrigToken:DB$Token | TokenAmount$ 1 | TokenName$ Knight | TokenTypes$ Creature,Knight | TokenOwner$ You | TokenColors$ Black | TokenPower$ 2 | TokenToughness$ 2 | TokenKeywords$ Flanking<>Protection from white<>Haste +SVar:TrigToken:DB$Token | TokenAmount$ 1 | TokenOwner$ You | TokenScript$ b_2_2_knight_flanking_pro_white_haste SVar:Picture:http://www.wizards.com/global/images/magic/general/riftmarked_knight.jpg Oracle:Protection from black, flanking (Whenever a creature without flanking blocks this creature, the blocking creature gets -1/-1 until end of turn.)\nSuspend 3—{1}{W}{W} (Rather than cast this card from your hand, you may pay {1}{W}{W} and exile it with three time counters on it. At the beginning of your upkeep, remove a time counter. When the last is removed, cast it without paying its mana cost. It has haste.)\nWhen the last time counter is removed from Riftmarked Knight while it's exiled, create a 2/2 black Knight creature token with flanking, protection from white, and haste. diff --git a/forge-gui/res/tokenscripts/b_2_2_knight_flanking_pro_white_haste.txt b/forge-gui/res/tokenscripts/b_2_2_knight_flanking_pro_white_haste.txt new file mode 100644 index 00000000000..14e11cae9df --- /dev/null +++ b/forge-gui/res/tokenscripts/b_2_2_knight_flanking_pro_white_haste.txt @@ -0,0 +1,9 @@ +Name:Knight +ManaCost:no cost +Types:Creature Knight +Colors:black +PT:2/2 +K:Flanking +K:Protection from white +K:Haste +Oracle:Flanking\nProtection from white\nHaste diff --git a/forge-gui/res/tokenscripts/g_1_1_ooze.txt b/forge-gui/res/tokenscripts/g_1_1_ooze.txt new file mode 100644 index 00000000000..86dc06b7011 --- /dev/null +++ b/forge-gui/res/tokenscripts/g_1_1_ooze.txt @@ -0,0 +1,6 @@ +Name:Ooze +ManaCost:no cost +Types:Creature Ooze +Colors:green +PT:1/1 +Oracle: diff --git a/forge-gui/res/tokenscripts/g_2_2_cat_warrior_forestwalk.txt b/forge-gui/res/tokenscripts/g_2_2_cat_warrior_forestwalk.txt new file mode 100644 index 00000000000..9db0d4debe9 --- /dev/null +++ b/forge-gui/res/tokenscripts/g_2_2_cat_warrior_forestwalk.txt @@ -0,0 +1,7 @@ +Name:Cat Warrior +ManaCost:no cost +Types:Creature Cat Warrior +Colors:green +PT:2/2 +K:Forestwalk +Oracle:Forestwalk diff --git a/forge-gui/res/tokenscripts/g_2_2_ooze_mitotic.txt b/forge-gui/res/tokenscripts/g_2_2_ooze_mitotic.txt new file mode 100644 index 00000000000..f4c5e1023a2 --- /dev/null +++ b/forge-gui/res/tokenscripts/g_2_2_ooze_mitotic.txt @@ -0,0 +1,8 @@ +Name:Ooze +ManaCost:no cost +Types:Creature Ooze +Colors:green +PT:2/2 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigTokenJunior | TriggerController$ TriggeredCardController | TriggerDescription$ When this creature dies, create two 1/1 green Ooze creature tokens. +SVar:TrigTokenJunior:DB$Token | LegacyImage$ g 1 1 ooze | TokenScript$ g_1_1_ooze | TokenOwner$ You | TokenAmount$ 2 +Oracle:When this creature dies, create two 1/1 green Ooze creature tokens. diff --git a/forge-gui/res/tokenscripts/g_3_3_centaur_pro_black.txt b/forge-gui/res/tokenscripts/g_3_3_centaur_pro_black.txt new file mode 100644 index 00000000000..d2788e73c36 --- /dev/null +++ b/forge-gui/res/tokenscripts/g_3_3_centaur_pro_black.txt @@ -0,0 +1,7 @@ +Name:Centaur +ManaCost:no cost +Types:Creature Centaur +Colors:green +PT:3/3 +K:Protection from black +Oracle:Protection from black diff --git a/forge-gui/res/tokenscripts/g_x_x_treefolk_warrior.txt b/forge-gui/res/tokenscripts/g_x_x_treefolk_warrior.txt new file mode 100644 index 00000000000..0332f619efd --- /dev/null +++ b/forge-gui/res/tokenscripts/g_x_x_treefolk_warrior.txt @@ -0,0 +1,9 @@ +Name:Treefolk Warrior +ManaCost:no cost +Types:Creature Treefolk Warrior +Colors:green +PT:*/* +S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAMEs power and toughness are each equal to the number of Forests you control. +SVar:X:Count$Valid Forest.YouCtrl +SVar:BuffedBy:Forest +Oracle:CARDNAMEs power and toughness are each equal to the number of Forests you control. diff --git a/forge-gui/res/tokenscripts/r_0_2_dragon_egg_defender_hatches_dragon.txt b/forge-gui/res/tokenscripts/r_0_2_dragon_egg_defender_hatches_dragon.txt new file mode 100644 index 00000000000..d07b22f0e4f --- /dev/null +++ b/forge-gui/res/tokenscripts/r_0_2_dragon_egg_defender_hatches_dragon.txt @@ -0,0 +1,10 @@ +Name:Dragon Egg +ManaCost:no cost +Types:Creature Dragon Egg +Colors:red +PT:0/2 +K:Defender +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, create a 2/2 red Dragon creature token with flying. It has "{R}: This creature gets +1/+0 until end of turn". +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenOwner$ You | LegacyImage$ r 2 2 dragon c18 | TokenScript$ r_2_2_dragon_flying_firebreathing +SVar:SacMe:4 +Oracle:Defender\nWhen Dragon Egg dies, create a 2/2 red Dragon creature token with flying. It has "{R}: This creature gets +1/+0 until end of turn." diff --git a/forge-gui/res/tokenscripts/r_2_2_dragon_flying_firebreathing.txt b/forge-gui/res/tokenscripts/r_2_2_dragon_flying_firebreathing.txt new file mode 100644 index 00000000000..b3f28088d16 --- /dev/null +++ b/forge-gui/res/tokenscripts/r_2_2_dragon_flying_firebreathing.txt @@ -0,0 +1,8 @@ +Name:Dragon +ManaCost:no cost +Types:Creature Dragon +Colors:red +PT:2/2 +K:Flying +A:AB$ Pump | Cost$ R | NumAtt$ +1 | SpellDescription$ CARDNAME gets +1/+0 until end of turn. +Oracle:Flying\n{R}: This creature gets +1/+0 until end of turn. diff --git a/forge-gui/res/tokenscripts/u_3_3_fish_hatches_whale.txt b/forge-gui/res/tokenscripts/u_3_3_fish_hatches_whale.txt new file mode 100644 index 00000000000..b3cee428e2b --- /dev/null +++ b/forge-gui/res/tokenscripts/u_3_3_fish_hatches_whale.txt @@ -0,0 +1,8 @@ +Name:Fish +ManaCost:no cost +Types:Creature Fish +Colors:blue +PT:3/3 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerController$ TriggeredCardController | TriggerDescription$ When this creature dies, create a 6/6 blue Whale creature token with "When this creature dies, create a 9/9 blue Kraken creature token." +SVar:TrigToken:DB$Token | LegacyImage$ u 6 6 whale | TokenScript$ u_6_6_whale_hatches_kraken | TokenOwner$ You | TokenAmount$ 1 +Oracle:When this creature dies, create a 6/6 blue Whale creature token with "When this creature dies, create a 9/9 blue Kraken creature token.". diff --git a/forge-gui/res/tokenscripts/u_6_6_whale_hatches_kraken.txt b/forge-gui/res/tokenscripts/u_6_6_whale_hatches_kraken.txt new file mode 100644 index 00000000000..fb642b54f94 --- /dev/null +++ b/forge-gui/res/tokenscripts/u_6_6_whale_hatches_kraken.txt @@ -0,0 +1,8 @@ +Name:Whale +ManaCost:no cost +Types:Creature Whale +Colors:blue +PT:6/6 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerController$ TriggeredCardController | TriggerDescription$ When this creature dies, create a 9/9 blue Kraken creature token. +SVar:TrigToken:DB$Token | LegacyImage$ u 9 9 kraken | TokenScript$ u_9_9_kraken | TokenOwner$ You | TokenAmount$ 1 +Oracle:When this creature dies, create a 9/9 blue Kraken creature token. diff --git a/forge-gui/res/tokenscripts/u_9_9_kraken.txt b/forge-gui/res/tokenscripts/u_9_9_kraken.txt new file mode 100644 index 00000000000..e824b538c68 --- /dev/null +++ b/forge-gui/res/tokenscripts/u_9_9_kraken.txt @@ -0,0 +1,6 @@ +Name:Kraken +ManaCost:no cost +Types:Creature Kraken +Colors:blue +PT:9/9 +Oracle: