CardFactoryUtil: make CantBeBlockedBy into new StaticAbility

This commit is contained in:
Hanmac
2018-02-17 15:25:51 +01:00
parent 3f1d6238ff
commit a829dc72bc
3 changed files with 117 additions and 140 deletions

View File

@@ -18,7 +18,6 @@
package forge.game.card; package forge.game.card;
import com.esotericsoftware.minlog.Log; import com.esotericsoftware.minlog.Log;
import com.google.common.base.Function;
import com.google.common.collect.*; import com.google.common.collect.*;
import forge.GameCommand; import forge.GameCommand;
import forge.ImageKeys; import forge.ImageKeys;
@@ -1537,6 +1536,7 @@ public class Card extends GameEntity implements Comparable<Card> {
sbLong.append(keyword).append("\r\n"); sbLong.append(keyword).append("\r\n");
} else if (keyword.startsWith("Strive") || keyword.startsWith("Escalate") } else if (keyword.startsWith("Strive") || keyword.startsWith("Escalate")
|| keyword.startsWith("ETBReplacement") || keyword.startsWith("ETBReplacement")
|| keyword.startsWith("CantBeBlockedBy ")
|| keyword.equals("CARDNAME enters the battlefield tapped.") || keyword.equals("CARDNAME enters the battlefield tapped.")
|| keyword.startsWith("UpkeepCost")) { || keyword.startsWith("UpkeepCost")) {
} else if (keyword.startsWith("Provoke") || keyword.startsWith("Ingest") || keyword.equals("Unleash") } else if (keyword.startsWith("Provoke") || keyword.startsWith("Ingest") || keyword.equals("Unleash")
@@ -1595,12 +1595,9 @@ public class Card extends GameEntity implements Comparable<Card> {
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu")
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")) { || keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")) {
// keyword parsing takes care of adding a proper description // keyword parsing takes care of adding a proper description
} else if (keyword.startsWith("CantBeBlockedBy")) { } else if (keyword.startsWith("CantBeBlockedByAmount")) {
sbLong.append(getName()).append(" can't be blocked "); sbLong.append(getName()).append(" can't be blocked ");
if (keyword.startsWith("CantBeBlockedByAmount"))
sbLong.append(getTextForKwCantBeBlockedByAmount(keyword)); sbLong.append(getTextForKwCantBeBlockedByAmount(keyword));
else
sbLong.append(getTextForKwCantBeBlockedByType(keyword));
} else if (keyword.startsWith("CantBlock")) { } else if (keyword.startsWith("CantBlock")) {
sbLong.append(getName()).append(" can't block "); sbLong.append(getName()).append(" can't block ");
if (keyword.contains("CardUID")) { if (keyword.contains("CardUID")) {
@@ -1657,112 +1654,6 @@ public class Card extends GameEntity implements Comparable<Card> {
return byClause + Lang.nounWithNumeral(cnt, isLT ? "or more creature" : "creature"); return byClause + Lang.nounWithNumeral(cnt, isLT ? "or more creature" : "creature");
} }
private static String getTextForKwCantBeBlockedByType(final String keyword) {
boolean negative = true;
final List<String> subs = Lists.newArrayList(TextUtil.split(keyword.split(" ", 2)[1], ','));
final List<List<String>> subsAnd = Lists.newArrayList();
final List<String> orClauses = Lists.newArrayList();
for (final String expession : subs) {
final List<String> parts = Lists.newArrayList(expession.split("[.+]"));
for (int p = 0; p < parts.size(); p++) {
final String part = parts.get(p);
if (part.equalsIgnoreCase("creature")) {
parts.remove(p--);
continue;
}
// 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;
final String byClause = allNegative ? "except by " : "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() == allNegative;
return (useNon ? "*NO* " : "") + inp.getRight();
}
};
for (final List<String> andOperands : subsAnd) {
final List<Pair<Boolean, String>> prependedAdjectives = Lists.newArrayList();
final 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("powerLEX")) {// Kraken of the Straits
postponedAdjectives.add(Pair.of(true, "power less than the number of islands you control"));
} 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 (CardType.isACreatureType(part)) {
if (creatures != null && CardType.isACreatureType(creatures)) { // e.g. Kor Castigator
creatures = StringUtils.capitalize(Lang.getPlural(part)) + creatures;
} else {
creatures = StringUtils.capitalize(Lang.getPlural(part)) + (creatures == null ? "" : " or " + creatures);
}
// Kor Castigator and other similar creatures with composite subtype Eldrazi Scion in their text
creatures = TextUtil.fastReplace(creatures, "Scions or Eldrazis", "Eldrazi Scions");
} 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() == 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() == 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"));
}
}
orClauses.add(sbShort.toString());
}
return byClause + StringUtils.join(orClauses, " or ") + ".";
}
// get the text of the abilities of a card // get the text of the abilities of a card
public String getAbilityText() { public String getAbilityText() {
return getAbilityText(currentState); return getAbilityText(currentState);

View File

@@ -17,6 +17,7 @@
*/ */
package forge.game.card; package forge.game.card;
import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -51,10 +52,12 @@ import forge.game.trigger.TriggerHandler;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.Aggregates; import forge.util.Aggregates;
import forge.util.Expressions;
import forge.util.Lang; import forge.util.Lang;
import forge.util.TextUtil; import forge.util.TextUtil;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -4226,6 +4229,11 @@ public class CardFactoryUtil {
} else if (keyword.equals("Undaunted")) { } else if (keyword.equals("Undaunted")) {
effect = "Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Secondary$ True" effect = "Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Secondary$ True"
+ "| Amount$ Undaunted | EffectZone$ All | Description$ Undaunted (" + inst.getReminderText() + ")"; + "| Amount$ Undaunted | EffectZone$ All | Description$ Undaunted (" + inst.getReminderText() + ")";
} else if (keyword.startsWith("CantBeBlockedBy ")) {
final String[] k = keyword.split(" ", 2);
effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ " + k[1]
+ " | Description$ CARDNAME can't be blocked " + getTextForKwCantBeBlockedByType(keyword);
} }
if (effect != null) { if (effect != null) {
@@ -4277,4 +4285,110 @@ public class CardFactoryUtil {
return as; return as;
// ETBReplacementMove(sa.getHostCard(), null)); // ETBReplacementMove(sa.getHostCard(), null));
} }
private static String getTextForKwCantBeBlockedByType(final String keyword) {
boolean negative = true;
final List<String> subs = Lists.newArrayList(TextUtil.split(keyword.split(" ", 2)[1], ','));
final List<List<String>> subsAnd = Lists.newArrayList();
final List<String> orClauses = Lists.newArrayList();
for (final String expession : subs) {
final List<String> parts = Lists.newArrayList(expession.split("[.+]"));
for (int p = 0; p < parts.size(); p++) {
final String part = parts.get(p);
if (part.equalsIgnoreCase("creature")) {
parts.remove(p--);
continue;
}
// 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;
final String byClause = allNegative ? "except by " : "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() == allNegative;
return (useNon ? "*NO* " : "") + inp.getRight();
}
};
for (final List<String> andOperands : subsAnd) {
final List<Pair<Boolean, String>> prependedAdjectives = Lists.newArrayList();
final 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("powerLEX")) {// Kraken of the Straits
postponedAdjectives.add(Pair.of(true, "power less than the number of islands you control"));
} 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 (CardType.isACreatureType(part)) {
if (creatures != null && CardType.isACreatureType(creatures)) { // e.g. Kor Castigator
creatures = StringUtils.capitalize(Lang.getPlural(part)) + creatures;
} else {
creatures = StringUtils.capitalize(Lang.getPlural(part)) + (creatures == null ? "" : " or " + creatures);
}
// Kor Castigator and other similar creatures with composite subtype Eldrazi Scion in their text
creatures = TextUtil.fastReplace(creatures, "Scions or Eldrazis", "Eldrazi Scions");
} 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() == 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() == 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"));
}
}
orClauses.add(sbShort.toString());
}
return byClause + StringUtils.join(orClauses, " or ") + ".";
}
} }

View File

@@ -1005,34 +1005,6 @@ public class CombatUtil {
} }
} }
// TODO remove it later to replace it with CantBlockBy above
for (KeywordInterface inst1 : attacker.getKeywords()) {
String k = inst1.getOriginal();
if (k.startsWith("CantBeBlockedBy ")) {
final String[] n = k.split(" ", 2);
final String[] restrictions = n[1].split(",");
if (blocker.isValid(restrictions, attacker.getController(), attacker, null)) {
boolean stillblock = false;
//Dragon Hunter check
if (n[1].contains("withoutReach") && blocker.hasStartOfKeyword("IfReach")) {
for (KeywordInterface inst2 : blocker.getKeywords()) {
String k2 = inst2.getOriginal();
if (k2.startsWith("IfReach")) {
String n2[] = k2.split(":");
if (attacker.getType().hasCreatureType(n2[1])) {
stillblock = true;
break;
}
}
}
}
if (!stillblock) {
return false;
}
}
}
}
for (KeywordInterface inst : blocker.getKeywords()) { for (KeywordInterface inst : blocker.getKeywords()) {
String keyword = inst.getOriginal(); String keyword = inst.getOriginal();
if (keyword.startsWith("CantBlockCardUID")) { if (keyword.startsWith("CantBlockCardUID")) {