mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
CardFactoryUtil: make CantBeBlockedBy into new StaticAbility
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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 ") + ".";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")) {
|
||||||
|
|||||||
Reference in New Issue
Block a user