mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
refactor Keywords using Chosen Values
This commit is contained in:
committed by
Michael Kamensky
parent
6c512dd451
commit
01bf10c719
@@ -276,6 +276,10 @@ public class StaticEffect {
|
||||
affectedCard.removeChangedCardKeywords(getTimestamp());
|
||||
}
|
||||
|
||||
if (hasParam("CantHaveKeyword")) {
|
||||
affectedCard.removeCantHaveKeyword(getTimestamp());
|
||||
}
|
||||
|
||||
if (addHiddenKeywords != null) {
|
||||
for (final String k : addHiddenKeywords) {
|
||||
affectedCard.removeHiddenExtrinsicKeyword(k);
|
||||
|
||||
@@ -20,7 +20,7 @@ package forge.game.ability.effects;
|
||||
import forge.card.CardType;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import java.util.List;
|
||||
|
||||
@@ -101,6 +101,10 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
||||
c.addChangedCardKeywords(keywords, removeKeywords,
|
||||
sa.hasParam("RemoveAllAbilities"), sa.hasParam("RemoveIntrinsicAbilities"), timestamp);
|
||||
|
||||
if (sa.hasParam("CantHaveKeyword")) {
|
||||
c.addCantHaveKeyword(timestamp, Keyword.setValueOf(sa.getParam("CantHaveKeyword")));
|
||||
}
|
||||
|
||||
for (final String k : hiddenKeywords) {
|
||||
c.addHiddenExtrinsicKeyword(k);
|
||||
}
|
||||
@@ -138,6 +142,8 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
||||
|
||||
c.removeChangedCardTraits(timestamp);
|
||||
|
||||
c.removeCantHaveKeyword(timestamp);
|
||||
|
||||
for (final String k : hiddenKeywords) {
|
||||
c.removeHiddenExtrinsicKeyword(k);
|
||||
}
|
||||
|
||||
@@ -127,6 +127,8 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
private final NavigableMap<Long, CardCloneStates> clonedStates = Maps.newTreeMap();
|
||||
private final NavigableMap<Long, CardCloneStates> textChangeStates = Maps.newTreeMap();
|
||||
|
||||
private final Multimap<Long, Keyword> cantHaveKeywords = MultimapBuilder.hashKeys().enumSetValues(Keyword.class).build();
|
||||
|
||||
private final Map<Long, Integer> canBlockAdditional = Maps.newTreeMap();
|
||||
private final Set<Long> canBlockAny = Sets.newHashSet();
|
||||
|
||||
@@ -232,8 +234,8 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
private String originalText = "", text = "";
|
||||
private String chosenType = "";
|
||||
private List<String> chosenColors;
|
||||
private String namedCard = "";
|
||||
private int chosenNumber;
|
||||
private String chosenName = "";
|
||||
private Integer chosenNumber;
|
||||
private Player chosenPlayer;
|
||||
private Direction chosenDirection = null;
|
||||
private String chosenMode = "";
|
||||
@@ -1445,6 +1447,9 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return currentState.getManaCost();
|
||||
}
|
||||
|
||||
public final boolean hasChosenPlayer() {
|
||||
return chosenPlayer != null;
|
||||
}
|
||||
public final Player getChosenPlayer() {
|
||||
return chosenPlayer;
|
||||
}
|
||||
@@ -1454,7 +1459,11 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
view.updateChosenPlayer(this);
|
||||
}
|
||||
|
||||
public final int getChosenNumber() {
|
||||
public final boolean hasChosenNumber() {
|
||||
return chosenNumber != null;
|
||||
}
|
||||
|
||||
public final Integer getChosenNumber() {
|
||||
return chosenNumber;
|
||||
}
|
||||
public final void setChosenNumber(final int i) {
|
||||
@@ -1472,6 +1481,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
public final String getChosenType() {
|
||||
return chosenType;
|
||||
}
|
||||
|
||||
public final void setChosenType(final String s) {
|
||||
chosenType = s;
|
||||
view.updateChosenType(this);
|
||||
@@ -1537,13 +1547,24 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
view.updateChosenMode(this);
|
||||
}
|
||||
|
||||
public boolean hasChosenName() {
|
||||
return chosenName != null;
|
||||
}
|
||||
|
||||
public String getChosenName() {
|
||||
return chosenName;
|
||||
}
|
||||
public final void setChosenName(final String s) {
|
||||
chosenName = s;
|
||||
view.updateNamedCard(this);
|
||||
}
|
||||
|
||||
// used for cards like Meddling Mage...
|
||||
public final String getNamedCard() {
|
||||
return namedCard;
|
||||
return getChosenName();
|
||||
}
|
||||
public final void setNamedCard(final String s) {
|
||||
namedCard = s;
|
||||
view.updateNamedCard(this);
|
||||
setChosenName(s);
|
||||
}
|
||||
|
||||
public final boolean getDrawnThisTurn() {
|
||||
@@ -3677,7 +3698,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
|
||||
public final void addChangedCardKeywords(final List<String> keywords, final List<String> removeKeywords,
|
||||
final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final long timestamp, final boolean updateView) {
|
||||
keywords.removeAll(getCantHaveOrGainKeyword());
|
||||
// if the key already exists - merge entries
|
||||
final KeywordsChange cks = changedCardKeywords.get(timestamp);
|
||||
if (cks != null) {
|
||||
@@ -3706,7 +3726,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
final long timestamp, final boolean updateView) {
|
||||
KeywordCollection list = new KeywordCollection();
|
||||
list.insertAll(keywords);
|
||||
list.removeAll(getCantHaveOrGainKeyword());
|
||||
// if the key already exists - merge entries
|
||||
final KeywordsChange cks = changedCardKeywords.get(timestamp);
|
||||
if (cks != null) {
|
||||
@@ -3727,22 +3746,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
}
|
||||
|
||||
public final void addChangedCardKeywords(final String[] keywords, final String[] removeKeywords,
|
||||
final boolean removeAllKeywords, final boolean removeIntrinsicKeywords, final long timestamp) {
|
||||
List<String> keywordsList = Lists.newArrayList();
|
||||
List<String> removeKeywordsList = Lists.newArrayList();
|
||||
if (keywords != null) {
|
||||
keywordsList = Lists.newArrayList(Arrays.asList(keywords));
|
||||
}
|
||||
|
||||
if (removeKeywords != null) {
|
||||
removeKeywordsList = Lists.newArrayList(Arrays.asList(removeKeywords));
|
||||
}
|
||||
|
||||
addChangedCardKeywords(keywordsList, removeKeywordsList,
|
||||
removeAllKeywords, removeIntrinsicKeywords, timestamp);
|
||||
}
|
||||
|
||||
public final KeywordsChange removeChangedCardKeywords(final long timestamp) {
|
||||
return removeChangedCardKeywords(timestamp, true);
|
||||
}
|
||||
@@ -3817,21 +3820,17 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
}
|
||||
|
||||
// remove Can't have keywords
|
||||
for (Keyword k : getCantHaveKeyword()) {
|
||||
keywords.removeAll(k);
|
||||
}
|
||||
|
||||
state.setCachedKeywords(keywords);
|
||||
}
|
||||
private void visitUnhiddenKeywords(CardState state, Visitor<KeywordInterface> visitor) {
|
||||
if (changedCardKeywords.isEmpty()) {
|
||||
// Fast path that doesn't involve temp allocations.
|
||||
for (KeywordInterface kw : state.getIntrinsicKeywords()) {
|
||||
if (!visitor.visit(kw)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (KeywordInterface kw : getUnhiddenKeywords(state)) {
|
||||
if (!visitor.visit(kw)) {
|
||||
return;
|
||||
}
|
||||
for (KeywordInterface kw : getUnhiddenKeywords(state)) {
|
||||
if (!visitor.visit(kw)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4035,14 +4034,32 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
}
|
||||
|
||||
public final List<String> getCantHaveOrGainKeyword() {
|
||||
final List<String> cantGain = Lists.newArrayList();
|
||||
for (String s : hiddenExtrinsicKeyword) {
|
||||
if (s.contains("can't have or gain")) {
|
||||
cantGain.add(s.split("can't have or gain ")[1]);
|
||||
}
|
||||
public void addCantHaveKeyword(Keyword keyword, Long timestamp) {
|
||||
cantHaveKeywords.put(timestamp, keyword);
|
||||
getView().updateCantHaveKeyword(this);
|
||||
}
|
||||
|
||||
public void addCantHaveKeyword(Long timestamp, Iterable<Keyword> keywords) {
|
||||
cantHaveKeywords.putAll(timestamp, keywords);
|
||||
getView().updateCantHaveKeyword(this);
|
||||
}
|
||||
|
||||
public boolean removeCantHaveKeyword(Long timestamp) {
|
||||
return removeCantHaveKeyword(timestamp, true);
|
||||
}
|
||||
public boolean removeCantHaveKeyword(Long timestamp, boolean updateView) {
|
||||
boolean change = !cantHaveKeywords.removeAll(timestamp).isEmpty();
|
||||
if (change && updateView) {
|
||||
getView().updateCantHaveKeyword(this);
|
||||
updateKeywords();
|
||||
if (isToken())
|
||||
game.fireEvent(new GameEventTokenStateUpdate(this));
|
||||
}
|
||||
return cantGain;
|
||||
return change;
|
||||
}
|
||||
|
||||
public Collection<Keyword> getCantHaveKeyword() {
|
||||
return cantHaveKeywords.values();
|
||||
}
|
||||
|
||||
public final void setStaticAbilities(final List<StaticAbility> a) {
|
||||
@@ -5403,34 +5420,33 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
final String[] kws = kw.split(":");
|
||||
String characteristic = kws[1];
|
||||
|
||||
// if colorlessDamage then it does only check damage color..
|
||||
if (colorlessDamage) {
|
||||
if (characteristic.endsWith("White") || characteristic.endsWith("Blue")
|
||||
|| characteristic.endsWith("Black") || characteristic.endsWith("Red")
|
||||
|| characteristic.endsWith("Green") || characteristic.endsWith("Colorless")
|
||||
|| characteristic.endsWith("ChosenColor")) {
|
||||
characteristic += "Source";
|
||||
if (characteristic.startsWith("Player")) {
|
||||
// TODO need to handle that better in CardProperty
|
||||
if (source.getController().isValid(characteristic.split(","), getController(), this, null)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// if colorlessDamage then it does only check damage color..
|
||||
if (colorlessDamage) {
|
||||
if (characteristic.endsWith("White") || characteristic.endsWith("Blue")
|
||||
|| characteristic.endsWith("Black") || characteristic.endsWith("Red")
|
||||
|| characteristic.endsWith("Green") || characteristic.endsWith("Colorless")
|
||||
|| characteristic.endsWith("ChosenColor")) {
|
||||
characteristic += "Source";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String[] characteristics = characteristic.split(",");
|
||||
final String exception = kws.length > 3 ? kws[3] : null; // check "This effect cannot remove sth"
|
||||
if (source.isValid(characteristics, getController(), this, null)
|
||||
&& (!checkSBA || exception == null || !source.isValid(exception, getController(), this, null))) {
|
||||
return true;
|
||||
final String[] characteristics = characteristic.split(",");
|
||||
final String exception = kws.length > 3 ? kws[3] : null; // check "This effect cannot remove sth"
|
||||
if (source.isValid(characteristics, getController(), this, null)
|
||||
&& (!checkSBA || exception == null || !source.isValid(exception, getController(), this, null))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (kw.equals("Protection from colored spells")) {
|
||||
if (source.isSpell() && !source.isColorless()) {
|
||||
return true;
|
||||
}
|
||||
} else if (kw.equals("Protection from the chosen player")) {
|
||||
if (source.getController().equals(chosenPlayer)) {
|
||||
return true;
|
||||
}
|
||||
} else if (kw.equals("Protection from each converted mana cost other than the chosen number")) {
|
||||
if (source.getCMC() != chosenNumber) {
|
||||
return true;
|
||||
}
|
||||
} else if (kw.startsWith("Protection from opponent of ")) {
|
||||
final String playerName = kw.substring("Protection from opponent of ".length());
|
||||
if (source.getController().isOpponentOf(playerName)) {
|
||||
|
||||
@@ -677,7 +677,8 @@ public class CardFactoryUtil {
|
||||
if (l[0].startsWith("Number$")) {
|
||||
final String number = l[0].substring(7);
|
||||
if (number.equals("ChosenNumber")) {
|
||||
return doXMath(c.getChosenNumber(), m, c);
|
||||
int x = c.getChosenNumber() == null ? 0 : c.getChosenNumber();
|
||||
return doXMath(x, m, c);
|
||||
}
|
||||
return doXMath(Integer.parseInt(number), m, c);
|
||||
}
|
||||
@@ -1980,35 +1981,37 @@ public class CardFactoryUtil {
|
||||
final List<String> filteredkw = Lists.newArrayList();
|
||||
final Player p = host.getController();
|
||||
CardCollectionView cardlist = p.getGame().getCardsIn(zones);
|
||||
final List<String> landkw = Lists.newArrayList();
|
||||
final List<String> protectionkw = Lists.newArrayList();
|
||||
final List<String> hexproofkw = Lists.newArrayList();
|
||||
final List<String> allkw = Lists.newArrayList();
|
||||
final Set<String> landkw = Sets.newHashSet();
|
||||
final Set<String> protectionkw = Sets.newHashSet();
|
||||
final Set<String> protectionColorkw = Sets.newHashSet();
|
||||
final Set<String> hexproofkw = Sets.newHashSet();
|
||||
final Set<String> allkw = Sets.newHashSet();
|
||||
|
||||
|
||||
for (Card c : CardLists.getValidCards(cardlist, restrictions, p, host, null)) {
|
||||
for (KeywordInterface inst : c.getKeywords()) {
|
||||
final String k = inst.getOriginal();
|
||||
if (k.endsWith("walk")) {
|
||||
if (!landkw.contains(k)) {
|
||||
landkw.add(k);
|
||||
}
|
||||
landkw.add(k);
|
||||
} else if (k.startsWith("Protection")) {
|
||||
if (!protectionkw.contains(k)) {
|
||||
protectionkw.add(k);
|
||||
protectionkw.add(k);
|
||||
for(byte col : MagicColor.WUBRG) {
|
||||
final String colString = "Protection from " + MagicColor.toLongString(col).toLowerCase();
|
||||
if (k.contains(colString)) {
|
||||
protectionColorkw.add(colString);
|
||||
}
|
||||
}
|
||||
} else if (k.startsWith("Hexproof")) {
|
||||
if (!hexproofkw.contains(k)) {
|
||||
hexproofkw.add(k);
|
||||
}
|
||||
}
|
||||
if (!allkw.contains(k)) {
|
||||
allkw.add(k);
|
||||
hexproofkw.add(k);
|
||||
}
|
||||
allkw.add(k);
|
||||
}
|
||||
}
|
||||
for (String keyword : kw) {
|
||||
if (keyword.equals("Protection")) {
|
||||
filteredkw.addAll(protectionkw);
|
||||
} else if (keyword.equals("ProtectionColor")) {
|
||||
filteredkw.addAll(protectionColorkw);
|
||||
} else if (keyword.equals("Landwalk")) {
|
||||
filteredkw.addAll(landkw);
|
||||
} else if (keyword.equals("Hexproof")) {
|
||||
|
||||
@@ -319,6 +319,14 @@ public final class CardUtil {
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ColorSet getColorsYouCtrl(final Player p) {
|
||||
byte b = 0;
|
||||
for (Card c : p.getCardsIn(ZoneType.Battlefield)) {
|
||||
b |= c.determineColor().getColor();
|
||||
}
|
||||
return ColorSet.fromMask(b);
|
||||
}
|
||||
|
||||
public static CardState getFaceDownCharacteristic(Card c) {
|
||||
final CardType type = new CardType();
|
||||
type.add("Creature");
|
||||
|
||||
@@ -2,6 +2,8 @@ package forge.game.card;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.ImageKeys;
|
||||
import forge.card.*;
|
||||
import forge.card.mana.ManaCost;
|
||||
@@ -26,6 +28,7 @@ import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class CardView extends GameEntityView {
|
||||
private static final long serialVersionUID = -3624090829028979255L;
|
||||
@@ -422,6 +425,8 @@ public class CardView extends GameEntityView {
|
||||
case SchemeDeck:
|
||||
// true for now, to actually see the Scheme cards (can't see deck anyway)
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// special viewing permissions for viewer
|
||||
@@ -649,6 +654,17 @@ public class CardView extends GameEntityView {
|
||||
|
||||
}
|
||||
|
||||
Set<String> cantHaveKeyword = this.getCantHaveKeyword();
|
||||
if (cantHaveKeyword != null && !cantHaveKeyword.isEmpty()) {
|
||||
sb.append("\r\n\r\n");
|
||||
for(String k : cantHaveKeyword) {
|
||||
sb.append("CARDNAME can't have or gain ".replaceAll("CARDNAME", getName()));
|
||||
sb.append(k);
|
||||
sb.append(".");
|
||||
sb.append("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
String cloner = get(TrackableProperty.Cloner);
|
||||
if (!cloner.isEmpty()) {
|
||||
sb.append("\r\nCloned by: ").append(cloner);
|
||||
@@ -768,6 +784,18 @@ public class CardView extends GameEntityView {
|
||||
set(TrackableProperty.BlockAny, c.canBlockAny());
|
||||
}
|
||||
|
||||
Set<String> getCantHaveKeyword() {
|
||||
return get(TrackableProperty.CantHaveKeyword);
|
||||
}
|
||||
|
||||
void updateCantHaveKeyword(Card c) {
|
||||
Set<String> keywords = Sets.newTreeSet();
|
||||
for (Keyword k : c.getCantHaveKeyword()) {
|
||||
keywords.add(k.toString());
|
||||
}
|
||||
set(TrackableProperty.CantHaveKeyword, keywords);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
|
||||
@@ -287,4 +287,15 @@ public enum Keyword {
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
public static Set<Keyword> setValueOf(String value) {
|
||||
Set<Keyword> result = EnumSet.noneOf(Keyword.class);
|
||||
for (String s : value.split(" & ")) {
|
||||
Keyword k = smartValueOf(s);
|
||||
if (!UNDEFINED.equals(k)) {
|
||||
result.add(k);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,10 @@ public class KeywordCollection implements Iterable<String>, Serializable {
|
||||
return map.remove(keyword.getKeyword(), keyword);
|
||||
}
|
||||
|
||||
public boolean removeAll(Keyword kenum) {
|
||||
return !map.removeAll(kenum).isEmpty();
|
||||
}
|
||||
|
||||
public boolean removeAll(Iterable<String> keywords) {
|
||||
boolean result = false;
|
||||
for (String k : keywords) {
|
||||
@@ -164,7 +168,6 @@ public class KeywordCollection implements Iterable<String>, Serializable {
|
||||
return new Iterator<String>() {
|
||||
private final Iterator<KeywordInterface> iterator = map.values().iterator();
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
|
||||
@@ -38,6 +38,10 @@ public class PlayerProperty {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (property.startsWith("PlayerUID_")) {
|
||||
if (player.getId() != Integer.parseInt(property.split("PlayerUID_")[1])) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("YourTeam")) {
|
||||
if (!player.sameTeam(sourceController)) {
|
||||
return false;
|
||||
|
||||
@@ -165,7 +165,8 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
if (hasParam("AddKeyword") || hasParam("AddAbility")
|
||||
|| hasParam("AddTrigger") || hasParam("RemoveTriggers")
|
||||
|| hasParam("RemoveKeyword") || hasParam("AddReplacementEffects")
|
||||
|| hasParam("AddStaticAbility") || hasParam("AddSVar")) {
|
||||
|| hasParam("AddStaticAbility") || hasParam("AddSVar")
|
||||
|| hasParam("CantHaveKeyword")) {
|
||||
layers.add(StaticAbilityLayer.ABILITIES);
|
||||
}
|
||||
|
||||
@@ -181,10 +182,6 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
}
|
||||
|
||||
if (hasParam("AddHiddenKeyword")) {
|
||||
// special rule for can't have or gain
|
||||
if (getParam("AddHiddenKeyword").contains("can't have or gain")) {
|
||||
layers.add(StaticAbilityLayer.ABILITIES);
|
||||
}
|
||||
layers.add(StaticAbilityLayer.RULES);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
*/
|
||||
package forge.game.staticability;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.GameCommand;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.CardType;
|
||||
import forge.card.MagicColor;
|
||||
import forge.game.Game;
|
||||
@@ -33,6 +35,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
@@ -113,9 +116,9 @@ public final class StaticAbilityContinuous {
|
||||
String setT = "";
|
||||
int setToughness = Integer.MAX_VALUE;
|
||||
|
||||
String[] addKeywords = null;
|
||||
List<String> addKeywords = null;
|
||||
List<String> addHiddenKeywords = Lists.newArrayList();
|
||||
String[] removeKeywords = null;
|
||||
List<String> removeKeywords = null;
|
||||
String[] addAbilities = null;
|
||||
String[] addReplacements = null;
|
||||
String[] addSVars = null;
|
||||
@@ -137,6 +140,8 @@ public final class StaticAbilityContinuous {
|
||||
|
||||
boolean overwriteColors = false;
|
||||
|
||||
Set<Keyword> cantHaveKeyword = null;
|
||||
|
||||
List<Player> mayLookAt = null;
|
||||
List<Player> withFlash = null;
|
||||
|
||||
@@ -189,50 +194,96 @@ public final class StaticAbilityContinuous {
|
||||
}
|
||||
|
||||
if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("AddKeyword")) {
|
||||
addKeywords = params.get("AddKeyword").split(" & ");
|
||||
final Iterable<String> chosencolors = hostCard.getChosenColors();
|
||||
for (final String color : chosencolors) {
|
||||
for (int w = 0; w < addKeywords.length; w++) {
|
||||
addKeywords[w] = addKeywords[w].replaceAll("ChosenColor", StringUtils.capitalize(color));
|
||||
}
|
||||
}
|
||||
final String chosenType = hostCard.getChosenType();
|
||||
for (int w = 0; w < addKeywords.length; w++) {
|
||||
addKeywords[w] = addKeywords[w].replaceAll("ChosenType", chosenType);
|
||||
}
|
||||
final String chosenName = hostCard.getNamedCard().replace(",", ";");
|
||||
addKeywords = Lists.newArrayList(Arrays.asList(params.get("AddKeyword").split(" & ")));
|
||||
final List<String> newKeywords = Lists.newArrayList();
|
||||
|
||||
// update keywords with Chosen parts
|
||||
final String hostCardUID = Integer.toString(hostCard.getId()); // Protection with "doesn't remove" effect
|
||||
final String hostCardPower = Integer.toString(hostCard.getNetPower());
|
||||
for (int w = 0; w < addKeywords.length; w++) {
|
||||
if (addKeywords[w].startsWith("Protection:")) {
|
||||
String k = addKeywords[w];
|
||||
k = k.replaceAll("ChosenName", "Card.named" + chosenName);
|
||||
k = k.replace("HostCardUID", hostCardUID);
|
||||
addKeywords[w] = k;
|
||||
} else if (addKeywords[w].startsWith("CantBeBlockedBy")) {
|
||||
String k = addKeywords[w];
|
||||
k = k.replaceAll("HostCardPower", hostCardPower);
|
||||
addKeywords[w] = k;
|
||||
|
||||
final ColorSet colorsYouCtrl = CardUtil.getColorsYouCtrl(controller);
|
||||
|
||||
Iterables.removeIf(addKeywords, new Predicate<String>() {
|
||||
@Override
|
||||
public boolean apply(String input) {
|
||||
if (!hostCard.hasChosenColor() && input.contains("ChosenColor")) {
|
||||
return true;
|
||||
}
|
||||
if (!hostCard.hasChosenType() && input.contains("ChosenType")) {
|
||||
return true;
|
||||
}
|
||||
if (!hostCard.hasChosenNumber() && input.contains("ChosenNumber")) {
|
||||
return true;
|
||||
}
|
||||
if (!hostCard.hasChosenPlayer() && input.contains("ChosenPlayer")) {
|
||||
return true;
|
||||
}
|
||||
if (!hostCard.hasChosenName() && input.contains("ChosenName")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// two variants for Red vs. red in keyword
|
||||
if (input.contains("ColorsYouCtrl") || input.contains("colorsYouCtrl")) {
|
||||
for (byte color : colorsYouCtrl) {
|
||||
final String colorWord = MagicColor.toLongString(color);
|
||||
String y = input.replaceAll("ColorsYouCtrl", StringUtils.capitalize(colorWord));
|
||||
y = y.replaceAll("colorsYouCtrl", colorWord);
|
||||
newKeywords.add(y);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
addKeywords.addAll(newKeywords);
|
||||
|
||||
addKeywords = Lists.transform(addKeywords, new Function<String, String>() {
|
||||
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
if (hostCard.hasChosenColor()) {
|
||||
input = input.replaceAll("ChosenColor", StringUtils.capitalize(hostCard.getChosenColor()));
|
||||
}
|
||||
if (hostCard.hasChosenType()) {
|
||||
input = input.replaceAll("ChosenType", hostCard.getChosenType());
|
||||
}
|
||||
if (hostCard.hasChosenNumber()) {
|
||||
input = input.replaceAll("ChosenNumber", String.valueOf(hostCard.getChosenNumber()));
|
||||
}
|
||||
if (hostCard.hasChosenPlayer()) {
|
||||
Player cp = hostCard.getChosenPlayer();
|
||||
input = input.replaceAll("ChosenPlayerUID", String.valueOf(cp.getId()));
|
||||
input = input.replaceAll("ChosenPlayerName", cp.getName());
|
||||
}
|
||||
if (hostCard.hasChosenName()) {
|
||||
final String chosenName = hostCard.getChosenName().replace(",", ";");
|
||||
input = input.replaceAll("ChosenName", "Card.named" + chosenName);
|
||||
}
|
||||
input = input.replace("HostCardUID", hostCardUID);
|
||||
return input;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (params.containsKey("SharedKeywordsZone")) {
|
||||
List<ZoneType> zones = ZoneType.listValueOf(params.get("SharedKeywordsZone"));
|
||||
String[] restrictions = params.containsKey("SharedRestrictions") ? params.get("SharedRestrictions").split(",") : new String[] {"Card"};
|
||||
List<String> kw = CardFactoryUtil.sharedKeywords(Arrays.asList(addKeywords), restrictions, zones, hostCard);
|
||||
addKeywords = kw.toArray(new String[kw.size()]);
|
||||
addKeywords = CardFactoryUtil.sharedKeywords(addKeywords, restrictions, zones, hostCard);
|
||||
}
|
||||
}
|
||||
|
||||
if ((layer == StaticAbilityLayer.RULES || layer == StaticAbilityLayer.ABILITIES) && params.containsKey("AddHiddenKeyword")) {
|
||||
// can't have or gain, need to be applyed in ABILITIES1
|
||||
for (String k : params.get("AddHiddenKeyword").split(" & ")) {
|
||||
if ( (k.contains("can't have or gain")) == (layer == StaticAbilityLayer.ABILITIES))
|
||||
addHiddenKeywords.add(k);
|
||||
}
|
||||
if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("CantHaveKeyword")) {
|
||||
cantHaveKeyword = Keyword.setValueOf(params.get("CantHaveKeyword"));
|
||||
}
|
||||
|
||||
if ((layer == StaticAbilityLayer.RULES) && params.containsKey("AddHiddenKeyword")) {
|
||||
addHiddenKeywords.addAll(Arrays.asList(params.get("AddHiddenKeyword").split(" & ")));
|
||||
}
|
||||
|
||||
if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("RemoveKeyword")) {
|
||||
removeKeywords = params.get("RemoveKeyword").split(" & ");
|
||||
removeKeywords = Arrays.asList(params.get("RemoveKeyword").split(" & "));
|
||||
}
|
||||
|
||||
if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("RemoveAllAbilities")) {
|
||||
@@ -419,7 +470,7 @@ public final class StaticAbilityContinuous {
|
||||
|
||||
// add keywords
|
||||
if (addKeywords != null) {
|
||||
p.addChangedKeywords(addKeywords, removeKeywords == null ? new String[0] : removeKeywords, se.getTimestamp());
|
||||
p.addChangedKeywords(addKeywords, removeKeywords, se.getTimestamp());
|
||||
}
|
||||
|
||||
// add static abilities
|
||||
@@ -530,21 +581,44 @@ public final class StaticAbilityContinuous {
|
||||
// TODO regular keywords currently don't try to use keyword multiplier
|
||||
// (Although nothing uses it at this time)
|
||||
if ((addKeywords != null) || (removeKeywords != null) || removeAllAbilities || removeIntrinsicAbilities) {
|
||||
String[] newKeywords = null;
|
||||
List<String> newKeywords = null;
|
||||
if (addKeywords != null) {
|
||||
newKeywords = Arrays.copyOf(addKeywords, addKeywords.length);
|
||||
for (int j = 0; j < newKeywords.length; ++j) {
|
||||
if (newKeywords[j].contains("CardManaCost")) {
|
||||
if (affectedCard.getManaCost().isNoCost()) {
|
||||
newKeywords[j] = ""; // prevent a crash (varolz the scar-striped + dryad arbor)
|
||||
} else {
|
||||
newKeywords[j] = newKeywords[j].replace("CardManaCost", affectedCard.getManaCost().getShortString());
|
||||
newKeywords = Lists.newArrayList(addKeywords);
|
||||
final List<String> extraKeywords = Lists.newArrayList();
|
||||
|
||||
Iterables.removeIf(newKeywords, new Predicate<String>() {
|
||||
@Override
|
||||
public boolean apply(String input) {
|
||||
if (input.contains("CardManaCost")) {
|
||||
if (affectedCard.getManaCost().isNoCost()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (newKeywords[j].contains("ConvertedManaCost")) {
|
||||
final String costcmc = Integer.toString(affectedCard.getCMC());
|
||||
newKeywords[j] = newKeywords[j].replace("ConvertedManaCost", costcmc);
|
||||
// replace one Keyword with list of keywords
|
||||
if (input.startsWith("Protection") && input.contains("CardColors")) {
|
||||
for (Byte color : affectedCard.determineColor()) {
|
||||
extraKeywords.add(input.replace("CardColors", MagicColor.toLongString(color)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
newKeywords.addAll(extraKeywords);
|
||||
|
||||
newKeywords = Lists.transform(newKeywords, new Function<String, String>() {
|
||||
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
if (input.contains("CardManaCost")) {
|
||||
input = input.replace("CardManaCost", affectedCard.getManaCost().getShortString());
|
||||
} else if (input.contains("ConvertedManaCost")) {
|
||||
final String costcmc = Integer.toString(affectedCard.getCMC());
|
||||
input = input.replace("ConvertedManaCost", costcmc);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
affectedCard.addChangedCardKeywords(newKeywords, removeKeywords,
|
||||
@@ -682,6 +756,10 @@ public final class StaticAbilityContinuous {
|
||||
|| removeAllAbilities) {
|
||||
affectedCard.addChangedCardTraits(addedAbilities, null, addedTrigger, addedReplacementEffects, addedStaticAbility, removeAllAbilities, removeNonMana, false, hostCard.getTimestamp());
|
||||
}
|
||||
|
||||
if (cantHaveKeyword != null) {
|
||||
affectedCard.addCantHaveKeyword(hostCard.getTimestamp(), cantHaveKeyword);
|
||||
}
|
||||
}
|
||||
|
||||
if (layer == StaticAbilityLayer.TYPE && removeIntrinsicAbilities) {
|
||||
|
||||
@@ -117,6 +117,8 @@ public enum TrackableProperty {
|
||||
NonAbilityText(TrackableTypes.StringType),
|
||||
FoilIndex(TrackableTypes.IntegerType),
|
||||
|
||||
CantHaveKeyword(TrackableTypes.StringListType),
|
||||
|
||||
//Player
|
||||
IsAI(TrackableTypes.BooleanType),
|
||||
LobbyPlayerName(TrackableTypes.StringType),
|
||||
|
||||
@@ -2,6 +2,6 @@ Name:Arcane Lighthouse
|
||||
ManaCost:no cost
|
||||
Types:Land
|
||||
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
|
||||
A:AB$ AnimateAll | Cost$ 1 T | ValidCards$ Creature.OppCtrl | RemoveKeywords$ Hexproof & Shroud | HiddenKeywords$ CARDNAME can't have or gain Hexproof & CARDNAME can't have or gain Shroud | SpellDescription$ Until end of turn, creatures your opponents control lose hexproof and shroud and can't have hexproof or shroud.
|
||||
A:AB$ AnimateAll | Cost$ 1 T | ValidCards$ Creature.OppCtrl | RemoveKeywords$ Hexproof & Shroud | CantHaveKeyword$ Hexproof & Shroud | StackDescription$ SpellDescription | SpellDescription$ Until end of turn, creatures your opponents control lose hexproof and shroud and can't have hexproof or shroud.
|
||||
AI:RemoveDeck:All
|
||||
Oracle:{T}: Add {C}.\n{1}, {T}: Until end of turn, creatures your opponents control lose hexproof and shroud and can't have hexproof or shroud.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:1 R R
|
||||
Types:Enchantment Creature Human Warrior
|
||||
PT:3/2
|
||||
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddKeyword$ Trample | Description$ Creatures you control have trample.
|
||||
S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ Trample | AddHiddenKeyword$ CARDNAME can't have or gain Trample | Description$ Creatures your opponents control lose trample and can't have or gain trample.
|
||||
S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ Trample | CantHaveKeyword$ Trample | Description$ Creatures your opponents control lose trample and can't have or gain trample.
|
||||
SVar:PlayMain1:TRUE
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/archetype_of_aggression.jpg
|
||||
Oracle:Creatures you control have trample.\nCreatures your opponents control lose trample and can't have or gain trample.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:1 W W
|
||||
Types:Enchantment Creature Human Soldier
|
||||
PT:2/2
|
||||
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddKeyword$ First Strike | Description$ Creatures you control have first strike.
|
||||
S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ First Strike | AddHiddenKeyword$ CARDNAME can't have or gain First Strike | Description$ Creatures your opponents control lose first strike and can't have or gain first strike.
|
||||
S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ First Strike | CantHaveKeyword$ First Strike | Description$ Creatures your opponents control lose first strike and can't have or gain first strike.
|
||||
SVar:PlayMain1:TRUE
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/archetype_of_courage.jpg
|
||||
Oracle:Creatures you control have first strike.\nCreatures your opponents control lose first strike and can't have or gain first strike.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:6 G G
|
||||
Types:Enchantment Creature Boar
|
||||
PT:6/5
|
||||
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddKeyword$ Hexproof | Description$ Creatures you control have hexproof.
|
||||
S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ Hexproof | AddHiddenKeyword$ CARDNAME can't have or gain Hexproof | Description$ Creatures your opponents control lose hexproof and can't have or gain hexproof.
|
||||
S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ Hexproof | CantHaveKeyword$ Hexproof | Description$ Creatures your opponents control lose hexproof and can't have or gain hexproof.
|
||||
SVar:PlayMain1:TRUE
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/archetype_of_endurance.jpg
|
||||
Oracle:Creatures you control have hexproof.\nCreatures your opponents control lose hexproof and can't have or gain hexproof.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:4 B B
|
||||
Types:Enchantment Creature Gorgon
|
||||
PT:2/3
|
||||
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddKeyword$ Deathtouch | Description$ Creatures you control have deathtouch.
|
||||
S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ Deathtouch | AddHiddenKeyword$ CARDNAME can't have or gain Deathtouch | Description$ Creatures your opponents control lose deathtouch and can't have or gain deathtouch.
|
||||
S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ Deathtouch | CantHaveKeyword$ Deathtouch | Description$ Creatures your opponents control lose deathtouch and can't have or gain deathtouch.
|
||||
SVar:PlayMain1:TRUE
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/archetype_of_finality.jpg
|
||||
Oracle:Creatures you control have deathtouch.\nCreatures your opponents control lose deathtouch and can't have or gain deathtouch.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:4 U U
|
||||
Types:Enchantment Creature Human Wizard
|
||||
PT:3/2
|
||||
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddKeyword$ Flying | Description$ Creatures you control have flying.
|
||||
S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ Flying | AddHiddenKeyword$ CARDNAME can't have or gain Flying | Description$ Creatures your opponents control lose flying and can't have or gain flying.
|
||||
S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ Flying | CantHaveKeyword$ Flying | Description$ Creatures your opponents control lose flying and can't have or gain flying.
|
||||
SVar:PlayMain1:TRUE
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/archetype_of_imagination.jpg
|
||||
Oracle:Creatures you control have flying.\nCreatures your opponents control lose flying and can't have or gain flying.
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
Name:Earnest Fellowship
|
||||
ManaCost:1 W
|
||||
Types:Enchantment
|
||||
Text:Each creature has protection from its colors.
|
||||
S:Mode$ Continuous | Affected$ Creature.White | AddKeyword$ Protection from white
|
||||
S:Mode$ Continuous | Affected$ Creature.Blue | AddKeyword$ Protection from blue
|
||||
S:Mode$ Continuous | Affected$ Creature.Black | AddKeyword$ Protection from black
|
||||
S:Mode$ Continuous | Affected$ Creature.Red | AddKeyword$ Protection from red
|
||||
S:Mode$ Continuous | Affected$ Creature.Green | AddKeyword$ Protection from green
|
||||
S:Mode$ Continuous | Affected$ Creature | AddKeyword$ Protection from CardColors | Description$ Each creature has protection from its colors.
|
||||
SVar:NonStackingEffect:True
|
||||
SVar:PlayMain1:TRUE
|
||||
AI:RemoveDeck:Random
|
||||
|
||||
@@ -2,15 +2,6 @@ Name:Empty-Shrine Kannushi
|
||||
ManaCost:W
|
||||
Types:Creature Human Cleric
|
||||
PT:1/1
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from white | CheckSVar$ WHITE | SVarCompare$ GE1 | Description$ CARDNAME has protection from the colors of permanents you control.
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from blue | CheckSVar$ BLUE | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from black | CheckSVar$ BLACK | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from red | CheckSVar$ RED | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from green | CheckSVar$ GREEN | SVarCompare$ GE1
|
||||
SVar:WHITE:Count$Valid Permanent.White+YouCtrl
|
||||
SVar:BLUE:Count$Valid Permanent.Blue+YouCtrl
|
||||
SVar:BLACK:Count$Valid Permanent.Black+YouCtrl
|
||||
SVar:RED:Count$Valid Permanent.Red+YouCtrl
|
||||
SVar:GREEN:Count$Valid Permanent.Green+YouCtrl
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from colorsYouCtrl | Description$ CARDNAME has protection from the colors of permanents you control.
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/empty_shrine_kannushi.jpg
|
||||
Oracle:Empty-Shrine Kannushi has protection from the colors of permanents you control.
|
||||
|
||||
@@ -2,21 +2,5 @@ Name:Escaped Shapeshifter
|
||||
ManaCost:3 U U
|
||||
Types:Creature Shapeshifter
|
||||
PT:3/4
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Flying | CheckSVar$ FLYING | SVarCompare$ GE1 | Description$ As long as an opponent controls a creature with flying not named CARDNAME, CARDNAME has flying. The same is true for first strike, trample, and protection from any color.
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ First Strike | CheckSVar$ FIRSTSTRIKE | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Trample | CheckSVar$ TRAMPLE | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from white | CheckSVar$ WHITE | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from blue | CheckSVar$ BLUE | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from black | CheckSVar$ BLACK | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from red | CheckSVar$ RED | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from green | CheckSVar$ GREEN | SVarCompare$ GE1
|
||||
SVar:FLYING:Count$Valid Creature.withFlying+notnamed Escaped Shapeshifter+OppCtrl
|
||||
SVar:FIRSTSTRIKE:Count$Valid Creature.withFirst Strike+notnamed Escaped Shapeshifter+OppCtrl
|
||||
SVar:TRAMPLE:Count$Valid Creature.withTrample+notnamed Escaped Shapeshifter+OppCtrl
|
||||
SVar:WHITE:Count$Valid Creature.withProtection from white+notnamed Escaped Shapeshifter+OppCtrl
|
||||
SVar:BLUE:Count$Valid Creature.withProtection from blue+notnamed Escaped Shapeshifter+OppCtrl
|
||||
SVar:BLACK:Count$Valid Creature.withProtection from black+notnamed Escaped Shapeshifter+OppCtrl
|
||||
SVar:RED:Count$Valid Creature.withProtection from red+notnamed Escaped Shapeshifter+OppCtrl
|
||||
SVar:GREEN:Count$Valid Creature.withProtection from green+notnamed Escaped Shapeshifter+OppCtrl
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/escaped_shapeshifter.jpg
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Flying & First Strike & Trample & ProtectionColor | SharedKeywordsZone$ Battlefield | SharedRestrictions$ Creature.notnamed Escaped Shapeshifter+OppCtrl | Description$ As long as an opponent controls a creature with flying not named Escaped Shapeshifter, CARDNAME has flying. The same is true for first strike, trample, and protection from any color.
|
||||
Oracle:As long as an opponent controls a creature with flying not named Escaped Shapeshifter, Escaped Shapeshifter has flying. The same is true for first strike, trample, and protection from any color.
|
||||
|
||||
@@ -4,6 +4,6 @@ Types:Legendary Creature Human Warrior
|
||||
PT:6/1
|
||||
K:CARDNAME attacks each combat if able.
|
||||
K:ETBReplacement:Other:ChooseNum
|
||||
SVar:ChooseNum:DB$ ChooseNumber | Min$ 2 | Max$ 4 | Defined$ You | Random$ True
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection from each converted mana cost other than the chosen number | Description$ CARDNAME has protection from each converted mana cost other than the chosen number.
|
||||
SVar:ChooseNum:DB$ ChooseNumber | Min$ 2 | Max$ 4 | Defined$ You | Random$ True | SpellDescription$ As Haktos enters the battlefield, choose 2, 3, or 4 at random.
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection:Card.cmcNEChosenNumber:Protection from each converted mana cost other than ChosenNumber | Description$ CARDNAME has protection from each converted mana cost other than the chosen number.
|
||||
Oracle:Haktos the Unscarred attacks each combat if able.\nAs Haktos enters the battlefield, choose 2, 3, or 4 at random.\nHaktos has protection from each converted mana cost other than the chosen number.
|
||||
|
||||
@@ -3,15 +3,6 @@ ManaCost:1 W
|
||||
Types:Enchantment Aura
|
||||
K:Enchant creature
|
||||
A:SP$ Attach | Cost$ 1 W | ValidTgts$ Creature | AILogic$ Pump
|
||||
S:Mode$ Continuous | Affected$ Card.EnchantedBy | AddKeyword$ Protection:Card.White:Protection from white:Card.CardUID_HostCardUID | CheckSVar$ WHITE | SVarCompare$ GE1 | Description$ Enchanted creature has protection from the colors of permanents you control. This effect doesn't remove CARDNAME.
|
||||
S:Mode$ Continuous | Affected$ Card.EnchantedBy | AddKeyword$ Protection:Card.Blue:Protection from blue:Card.CardUID_HostCardUID | CheckSVar$ BLUE | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.EnchantedBy | AddKeyword$ Protection:Card.Black:Protection from black:Card.CardUID_HostCardUID | CheckSVar$ BLACK | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.EnchantedBy | AddKeyword$ Protection:Card.Red:Protection from red:Card.CardUID_HostCardUID | CheckSVar$ RED | SVarCompare$ GE1
|
||||
S:Mode$ Continuous | Affected$ Card.EnchantedBy | AddKeyword$ Protection:Card.Green:Protection from green:Card.CardUID_HostCardUID | CheckSVar$ GREEN | SVarCompare$ GE1
|
||||
SVar:WHITE:Count$Valid Permanent.White+YouCtrl
|
||||
SVar:BLUE:Count$Valid Permanent.Blue+YouCtrl
|
||||
SVar:BLACK:Count$Valid Permanent.Black+YouCtrl
|
||||
SVar:RED:Count$Valid Permanent.Red+YouCtrl
|
||||
SVar:GREEN:Count$Valid Permanent.Green+YouCtrl
|
||||
S:Mode$ Continuous | Affected$ Card.EnchantedBy | AddKeyword$ Protection:Card.ColorsYouCtrl:Protection from colorsYouCtrl:Card.CardUID_HostCardUID | Description$ Enchanted creature has protection from the colors of permanents you control. This effect doesn't remove CARDNAME.
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/pledge_of_loyalty.jpg
|
||||
Oracle:Enchant creature\nEnchanted creature has protection from the colors of permanents you control. This effect doesn't remove Pledge of Loyalty.
|
||||
|
||||
@@ -4,6 +4,6 @@ Types:Creature Merfolk Rogue
|
||||
PT:3/1
|
||||
K:ETBReplacement:Other:ChooseP
|
||||
SVar:ChooseP:DB$ ChoosePlayer | Defined$ You | Choices$ Player | AILogic$ Curse | SpellDescription$ As CARDNAME enters the battlefield, choose a player.
|
||||
S:Mode$ Continuous | AddKeyword$ Protection from the chosen player | Affected$ Card.Self | Description$ CARDNAME has protection from that player. (This creature can't be blocked, targeted, dealt damage, or enchanted by anything controlled by that player.)
|
||||
S:Mode$ Continuous | AddKeyword$ Protection:Player.PlayerUID_ChosenPlayerUID:Protection from ChosenPlayerName | Affected$ Card.Self | Description$ CARDNAME has protection from that player. (This creature can't be blocked, targeted, dealt damage, or enchanted by anything controlled by that player.)
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/true_name_nemesis.jpg
|
||||
Oracle:As True-Name Nemesis enters the battlefield, choose a player.\nTrue-Name Nemesis has protection from the chosen player. (This creature can't be blocked, targeted, dealt damage, or enchanted by anything controlled by that player.)
|
||||
|
||||
Reference in New Issue
Block a user