Text for CantBeBlockedBy is composed in runtime

This commit is contained in:
Maxmtg
2013-06-21 18:14:52 +00:00
parent da1645c353
commit fa443cea0d
32 changed files with 137 additions and 52 deletions

View File

@@ -1,7 +1,6 @@
Name:Amrou Kithkin
ManaCost:W W
Types:Creature Kithkin
Text:CARDNAME can't be blocked by creatures with power 3 or greater.
PT:1/1
K:CantBeBlockedBy Creature.powerGE3
SVar:Picture:http://www.wizards.com/global/images/magic/general/amrou_kithkin.jpg

View File

@@ -1,7 +1,6 @@
Name:Amrou Seekers
ManaCost:2 W
Types:Creature Kithkin Rebel
Text:CARDNAME can't be blocked except by artifact creatures and/or white creatures.
PT:2/2
K:CantBeBlockedBy Creature.nonArtifact+nonWhite
SVar:Picture:http://www.wizards.com/global/images/magic/general/amrou_seekers.jpg

View File

@@ -2,7 +2,7 @@ Name:Arctic Foxes
ManaCost:1 W
Types:Creature Fox
PT:1/1
S:Mode$ Continuous | Affected$ Card.Self | AddHiddenKeyword$ CantBeBlockedBy Creature.powerGE2 | CheckSVar$ X | SVarCompare$ GE1 | Description$ Creatures with power 2 or greater can't block CARDNAME as long as defending player controls a snow land.
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ CantBeBlockedBy Creature.powerGE2 | CheckSVar$ X | SVarCompare$ GE1 | Description$ Creatures with power 2 or greater can't block CARDNAME as long as defending player controls a snow land.
SVar:X:Count$Valid Land.Snow+DefenderCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/arctic_foxes.jpg
Oracle:Creatures with power 2 or greater can't block Arctic Foxes as long as defending player controls a snow land.

View File

@@ -1,7 +1,7 @@
Name:Argothian Pixies
ManaCost:1 G
Types:Creature Faerie
Text:Prevent all damage that would be dealt to CARDNAME by artifact creatures.\r\nCARDNAME can't be blocked by artifact creatures.
Text:Prevent all damage that would be dealt to CARDNAME by artifact creatures.
PT:2/1
K:PreventAllDamageBy Artifact.Creature
K:CantBeBlockedBy Artifact.Creature

View File

@@ -1,7 +1,6 @@
Name:Barrenton Cragtreads
ManaCost:2 WU WU
Types:Creature Kithkin Scout
Text:Barrenton Cragtreads can't be blocked by red creatures.
PT:3/3
K:CantBeBlockedBy Creature.Red
SVar:Picture:http://www.wizards.com/global/images/magic/general/barrenton_cragtreads.jpg

View File

@@ -1,7 +1,6 @@
Name:Basalt Golem
ManaCost:5
Types:Artifact Creature Golem
Text:CARDNAME can't be blocked by artifact creatures.
PT:2/4
K:CantBeBlockedBy Artifact.Creature
T:Mode$ Blocks | ValidCard$ Creature | ValidBlocked$ Card.Self | DelayedTrigger$ TrigEndCombat | TriggerDescription$ Whenever CARDNAME becomes blocked by a creature, that creature's controller sacrifices it at end of combat. If the player does, he or she puts a 0/2 colorless Wall artifact creature token with defender onto the battlefield.

View File

@@ -1,7 +1,7 @@
Name:Bower Passage
ManaCost:1 G
Types:Enchantment
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddHiddenKeyword$ CantBeBlockedBy Creature.withFlying | Description$ Creatures you control can't be blocked by creatures with flying.
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddKeyword$ CantBeBlockedBy Creature.withFlying | Description$ Creatures with flying can't block creatures you control.
SVar:NonStackingEffect:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/bower_passage.jpg
Oracle:Creatures with flying can't block creatures you control.

View File

@@ -1,7 +1,6 @@
Name:Clockwork Steed
ManaCost:4
Types:Artifact Creature Horse
Text:CARDNAME can't be blocked by artifact creatures.
PT:0/3
K:etbCounter:P1P0:4
K:CantBeBlockedBy Artifact.Creature

View File

@@ -1,7 +1,6 @@
Name:Clockwork Swarm
ManaCost:4
Types:Artifact Creature Insect
Text:CARDNAME can't be blocked by Walls.
PT:0/3
K:etbCounter:P1P0:4
K:CantBeBlockedBy Creature.Wall

View File

@@ -1,7 +1,6 @@
Name:Deathcult Rogue
ManaCost:1 UB UB
Types:Creature Human Rogue
Text:CARDNAME can't be blocked except by Rogues.
PT:2/2
K:CantBeBlockedBy Creature.nonRogue
SVar:Picture:http://www.wizards.com/global/images/magic/general/deathcult_rogue.jpg

View File

@@ -1,7 +1,7 @@
Name:Dread Charge
ManaCost:3 B
Types:Sorcery
A:SP$ PumpAll | Cost$ 3 B | ValidCards$ Creature.Black+YouCtrl | KW$ HIDDEN CantBeBlockedBy Creature.nonBlack | SpellDescription$ Until end of turn, black creatures you control can be blocked only by black creatures.
A:SP$ PumpAll | Cost$ 3 B | ValidCards$ Creature.Black+YouCtrl | KW$ CantBeBlockedBy Creature.nonBlack | SpellDescription$ Until end of turn, black creatures you control can be blocked only by black creatures.
SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/dread_charge.jpg
Oracle:Until end of turn, black creatures you control can be blocked only by black creatures.

View File

@@ -1,7 +1,6 @@
Name:Dread Warlock
ManaCost:1 B B
Types:Creature Human Wizard
Text:CARDNAME can't be blocked except by black creatures.
PT:2/2
K:CantBeBlockedBy Creature.nonBlack
SVar:Picture:http://www.wizards.com/global/images/magic/general/dread_warlock.jpg

View File

@@ -3,7 +3,7 @@ ManaCost:R
Types:Enchantment Aura
K:Enchant creature
A:SP$ Attach | Cost$ R | ValidTgts$ Creature | AILogic$ Pump
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddHiddenKeyword$ CantBeBlockedBy Creature.withFlying | Description$ Enchanted creature gets +2/+0 and can't be blocked by creatures with flying.
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddKeyword$ CantBeBlockedBy Creature.withFlying | Description$ Enchanted creature gets +2/+0 and can't be blocked by creatures with flying.
SVar:Picture:http://www.wizards.com/global/images/magic/general/dust_corona.jpg
Oracle:Enchant creature\nEnchanted creature gets +2/+0 and can't be blocked by creatures with flying.
SetInfo:PLC Common

View File

@@ -2,7 +2,7 @@ Name:Greater Stone Spirit
ManaCost:4 R R
Types:Creature Elemental Spirit
PT:4/4
S:Mode$ Continuous | Affected$ Card.Self | AddHiddenKeyword$ CantBeBlockedBy Creature.withFlying | Description$ CARDNAME can't be blocked by creatures with flying.
K:CantBeBlockedBy Creature.withFlying
A:AB$ Pump | Cost$ 2 R | ValidTgts$ Creature | NumDef$ 2 | SubAbility$ DBAnimate | SpellDescription$ Until end of turn, target creature gets +0/+2 and gains "R: This creature gets +1/+0 until end of turn."
SVar:DBAnimate:DB$ Animate | Defined$ Targeted | Abilities$ Pump
SVar:Pump:AB$ Pump | Cost$ R | NumAtt$ 1 | SpellDescription$ This creature gets +1/+0 until end of turn.

View File

@@ -1,7 +1,6 @@
Name:Manta Ray
ManaCost:1 U U
Types:Creature Fish
Text:CARDNAME can't be blocked except by blue creatures.
PT:3/3
K:CARDNAME can't attack unless defending player controls an Island.
K:CantBeBlockedBy Creature.nonBlue

View File

@@ -1,7 +1,6 @@
Name:Orchard Spirit
ManaCost:2 G
Types:Creature Spirit
Text:CARDNAME can't be blocked except by creatures with flying or reach.
PT:2/2
K:CantBeBlockedBy Creature.withoutFlying+withoutReach
SVar:Picture:http://www.wizards.com/global/images/magic/general/orchard_spirit.jpg

View File

@@ -1,7 +1,6 @@
Name:Prowling Nightstalker
ManaCost:3 B
Types:Creature Nightstalker
Text:CARDNAME can't be blocked except by black creatures.
PT:2/2
K:CantBeBlockedBy Creature.nonBlack
SVar:Picture:http://www.wizards.com/global/images/magic/general/prowling_nightstalker.jpg

View File

@@ -1,7 +1,6 @@
Name:Sacred Knight
ManaCost:3 W
Types:Creature Human Knight
Text:CARDNAME can't be blocked by black and/or red creatures.
PT:3/2
K:CantBeBlockedBy Creature.Black,Creature.Red
SVar:Picture:http://www.wizards.com/global/images/magic/general/sacred_knight.jpg

View File

@@ -1,7 +1,6 @@
Name:Signal Pest
ManaCost:1
Types:Artifact Creature Pest
Text:CARDNAME can't be blocked except by creatures with flying or reach.
PT:0/1
K:CantBeBlockedBy Creature.withoutFlying+withoutReach
T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigBattleCry | TriggerDescription$ Battle cry (Whenever this creature attacks, each other attacking creature gets +1/+0 until end of turn.)

View File

@@ -1,7 +1,6 @@
Name:Silhana Ledgewalker
ManaCost:1 G
Types:Creature Elf Rogue
Text:CARDNAME can't be blocked except by creatures with flying.
PT:1/1
K:CantBeBlockedBy Creature.withoutFlying
K:Hexproof

View File

@@ -1,7 +1,6 @@
Name:Skirk Shaman
ManaCost:1 R R
Types:Creature Goblin Shaman
Text:Skirk Shaman can't be blocked except by artifact creatures and/or red creatures.
PT:2/2
K:CantBeBlockedBy Creature.nonArtifact+nonRed
SVar:Picture:http://resources.wizards.com/magic/cards/plc/en-us/card131011.jpg

View File

@@ -1,7 +1,6 @@
Name:Sneaky Homunculus
ManaCost:1 U
Types:Creature Homunculus Illusion
Text:CARDNAME can't block or be blocked by creatures with power 2 or greater.
PT:1/1
K:CantBeBlockedBy Creature.powerGE2
K:CantBlock Creature.powerGE2

View File

@@ -1,7 +1,6 @@
Name:Stone Spirit
ManaCost:4 R
Types:Creature Elemental Spirit
Text:CARDNAME can't be blocked by creatures with flying.
PT:4/3
K:CantBeBlockedBy Creature.withFlying
SVar:Picture:http://www.wizards.com/global/images/magic/general/stone_spirit.jpg

View File

@@ -1,7 +1,6 @@
Name:Stromkirk Noble
ManaCost:R
Types:Creature Vampire
Text:CARDNAME can't be blocked by Humans.
PT:1/1
K:CantBeBlockedBy Creature.Human
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, put a +1/+1 counter on it.

View File

@@ -3,7 +3,7 @@ ManaCost:1 G
Types:Enchantment Aura
K:Enchant creature
A:SP$ Attach | Cost$ 1 G | ValidTgts$ Creature | AILogic$ Pump
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 1 | AddToughness$ 1 | AddHiddenKeyword$ CantBeBlockedBy Creature.withoutFlying | Description$ Enchanted creature gets +1/+1 and can't be blocked except by creatures with flying.
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ CantBeBlockedBy Creature.withoutFlying | Description$ Enchanted creature gets +1/+1 and can't be blocked except by creatures with flying.
SVar:Picture:http://www.wizards.com/global/images/magic/general/treetop_bracers.jpg
Oracle:Enchant creature (Target a creature as you cast this. This card enters the battlefield attached to that creature.)\nEnchanted creature gets +1/+1 and can't be blocked except by creatures with flying.
SetInfo:10E Common

View File

@@ -1,7 +1,6 @@
Name:Treetop Rangers
ManaCost:2 G
Types:Creature Elf
Text:CARDNAME can't be blocked except by creatures with flying.
PT:2/2
K:CantBeBlockedBy Creature.withoutFlying
SVar:Picture:http://www.wizards.com/global/images/magic/general/treetop_rangers.jpg

View File

@@ -1,7 +1,6 @@
Name:Treetop Scout
ManaCost:G
Types:Creature Elf Scout
Text:CARDNAME can't be blocked except by creatures with flying.
PT:1/1
K:CantBeBlockedBy Creature.withoutFlying
SVar:Picture:http://www.wizards.com/global/images/magic/general/treetop_scout.jpg

View File

@@ -1,7 +1,6 @@
Name:Vindictive Mob
ManaCost:4 B B
Types:Creature Human Berserker
Text:CARDNAME can't be blocked by Saprolings.
PT:5/5
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSac | TriggerDescription$ When CARDNAME enters the battlefield, sacrifice a creature.
SVar:TrigSac:AB$Sacrifice | Cost$ 0 | Defined$ You | SacValid$ Creature

View File

@@ -1,7 +1,6 @@
Name:Zuo Ci, the Mocking Sage
ManaCost:1 G G
Types:Legendary Creature Human Advisor
Text:CARDNAME can't be blocked by creatures with horsemanship.
PT:1/2
K:CantBeBlockedBy Creature.withHorsemanship
K:Hexproof

View File

@@ -34,11 +34,15 @@ import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import forge.CardPredicates.Presets;
import forge.Constant.CardTypes;
import forge.card.CardCharacteristics;
import forge.card.CardDb;
import forge.card.CardRarity;
@@ -2123,30 +2127,107 @@ public class Card extends GameEntity implements Comparable<Card> {
// keyword parsing takes care of adding a proper description
continue;
} else if (keyword.startsWith("CantBeBlockedBy")) {
String expression = keyword.split(" ", 2)[1];
boolean hasNon = expression.contains("non") || expression.contains("without");
sbLong.append(this.getName()).append(" cannot be blocked ");
if( hasNon ) sbLong.append("except ");
sbLong.append("by ");
String[] parts = TextUtil.split(expression, '.');
List<String> partsAfterCreatures = new ArrayList<String>();
for(String part : parts) {
if( part.equalsIgnoreCase("creature"))
sbLong.append(this.getName()).append(" can't be blocked ");
boolean negative = true;
List<String> subs = Lists.newArrayList(TextUtil.split(keyword.split(" ", 2)[1], ','));
List<List<String>> subsAnd = Lists.newArrayList();
List<String> orClauses = new ArrayList<String>();
for( int iOr = 0; iOr < subs.size(); iOr++ ) {
String expession = subs.get(iOr);
List<String> parts = Lists.newArrayList(expession.split("[.+]"));
for(int p = 0; p < parts.size(); p++) {
String part = parts.get(p);
if( part.equalsIgnoreCase("creature")) {
parts.remove(p--);
continue;
if(part.startsWith("with"))
{
int cutIdx = part.startsWith("without") ? 7 : 4;
partsAfterCreatures.add(StringUtils.capitalize(part.substring(cutIdx)));
}
else
sbLong.append(part.toLowerCase()).append(" ");
// based on suppossition that each expression has at least 1 predicate except 'creature'
negative &= part.contains("non") || part.contains("without");
}
subsAnd.add(parts);
}
final boolean allNegative = negative;
if( allNegative ) sbLong.append("except ");
sbLong.append("by ");
final Function<Pair<Boolean, String>, String> withToString = new Function<Pair<Boolean, String>, String>() {
@Override
public String apply(Pair<Boolean, String> inp) {
boolean useNon = inp.getKey().booleanValue() == allNegative;
return (useNon ? "*NO* " : "" ) + inp.getRight();
}
};
for( int iOr = 0; iOr < subsAnd.size(); iOr++ ) {
List<String> andOperands = subsAnd.get(iOr);
List<Pair<Boolean, String>> prependedAdjectives = Lists.newArrayList();
List<Pair<Boolean, String>> postponedAdjectives = Lists.newArrayList();
String creatures = null;
for(String part : andOperands) {
boolean positive = true;
if (part.startsWith("non")) {
part = part.substring(3);
positive = false;
}
if(part.startsWith("with")) {
positive = !part.startsWith("without");
postponedAdjectives.add(Pair.of(positive, part.substring(positive ? 4 : 7)));
} else if ( part.startsWith("power") ) {
int kwLength = 5;
String opName = Expressions.operatorName(part.substring(kwLength, kwLength + 2));
String operand = part.substring(kwLength + 2);
postponedAdjectives.add(Pair.of(true, "power" + opName + operand));
} else if ( forge.card.CardType.isACreatureType(part)) {
creatures = StringUtils.capitalize(Lang.getPlural(part)) + ( creatures == null ? "" : " or " + creatures );
} else {
prependedAdjectives.add(Pair.of(positive, part.toLowerCase()));
}
}
StringBuilder sbShort = new StringBuilder();
if ( allNegative ) {
boolean isFirst = true;
for(Pair<Boolean, String> pre : prependedAdjectives) {
if ( isFirst ) isFirst = false;
else sbShort.append(" and/or ");
boolean useNon = pre.getKey().booleanValue() == allNegative;
if( useNon ) sbShort.append("non-");
sbShort.append(pre.getValue()).append(" ").append(creatures == null ? "creatures" : creatures);
}
if (prependedAdjectives.isEmpty())
sbShort.append(creatures == null ? "creatures" : creatures);
if(!postponedAdjectives.isEmpty()) {
if( !prependedAdjectives.isEmpty() ) {
sbShort.append(" and/or creatures");
}
sbShort.append(" with ");
sbShort.append(Lang.joinHomogenous(postponedAdjectives, withToString, allNegative ? "or" : "and"));
}
} else {
for(Pair<Boolean, String> pre : prependedAdjectives) {
boolean useNon = pre.getKey().booleanValue() == allNegative;
if( useNon ) sbShort.append("non-");
sbShort.append(pre.getValue()).append(" ");
}
sbShort.append(creatures == null ? "creatures" : creatures);
if(!postponedAdjectives.isEmpty()) {
sbShort.append(" with ");
sbShort.append(Lang.joinHomogenous(postponedAdjectives, withToString, allNegative ? "or" : "and"));
}
}
sbLong.append("creatures");
if( !partsAfterCreatures.isEmpty() ) {
sbLong.append(" with ");
sbLong.append(Lang.joinHomogenous(partsAfterCreatures, null, hasNon ? "or" : "and"));
orClauses.add(sbShort.toString());
}
sbLong.append(StringUtils.join(orClauses, " or "));
}
else {

View File

@@ -64,4 +64,26 @@ public abstract class Expressions {
return false;
}
public static String operatorName(final String comp) {
// should this function be somewhere else?
// leftSide COMPARED to rightSide:
if (comp.contains("LT")) {
return " less than ";
} else if (comp.contains("LE")) {
return " less or equal to ";
} else if (comp.contains("EQ")) {
return " equal to ";
} else if (comp.contains("GT")) {
return " greater than ";
} else if (comp.contains("GE")) {
return " greater or equal to ";
} else if (comp.contains("NE")) {
return " not equal to ";
} else if (comp.contains("M2")) {
return " is modulo 2 equal to"; // should not show this to players
}
return " ? ";
}
} // end class AllZoneUtil

View File

@@ -61,14 +61,18 @@ public class Lang {
return subjects.size() > 1 ? verb : verb + "s";
}
public static String getPlural(String noun) {
return noun + ( noun.endsWith("s") || noun.endsWith("x") ? "es" : "s");
}
public static <T> String nounWithAmount(int cnt, String noun) {
String suffix = cnt <= 1 ? "" : ( noun.endsWith("s") || noun.endsWith("x") ? "es" : "s");
String countedForm = cnt <= 1 ? noun : getPlural(noun);
final String strCount;
if( cnt == 1 )
strCount = startsWithVowel(noun) ? "an " : "a ";
else
strCount = String.valueOf(cnt) + " ";
return strCount + noun + suffix;
return strCount + countedForm;
}
/**