Hexproof: is now done as CantTarget with special shared handling

This commit is contained in:
Hanmac
2018-03-18 15:28:22 +01:00
parent b81fba82a5
commit 9af8fddaf3
11 changed files with 100 additions and 11 deletions

View File

@@ -1526,6 +1526,10 @@ public class Card extends GameEntity implements Comparable<Card> {
sbx.append(" (" + inst.getReminderText() + ")"); sbx.append(" (" + inst.getReminderText() + ")");
sbLong.append(sbx).append("\r\n"); sbLong.append(sbx).append("\r\n");
} }
} else if (keyword.startsWith("Hexproof:")) {
final String k[] = keyword.split(":");
sbLong.append("Hexproof from ").append(k[2])
.append(" (").append(inst.getReminderText()).append(")").append("\r\n");
} else if (keyword.endsWith(".") && !keyword.startsWith("Haunt")) { } else if (keyword.endsWith(".") && !keyword.startsWith("Haunt")) {
sbLong.append(keyword).append("\r\n"); sbLong.append(keyword).append("\r\n");
} else if (keyword.startsWith("Presence")) { } else if (keyword.startsWith("Presence")) {
@@ -4975,13 +4979,6 @@ public class Card extends GameEntity implements Comparable<Card> {
result.setFalse(); result.setFalse();
} }
break; break;
case "Hexproof":
if (sa.getActivatingPlayer().getOpponents().contains(getController())) {
if (!sa.getActivatingPlayer().hasKeyword("Spells and abilities you control can target hexproof creatures")) {
result.setFalse();
}
}
break;
case "CARDNAME can't be enchanted.": case "CARDNAME can't be enchanted.":
if (source.isAura()) { if (source.isAura()) {
result.setFalse(); result.setFalse();

View File

@@ -1866,6 +1866,7 @@ public class CardFactoryUtil {
CardCollectionView cardlist = p.getGame().getCardsIn(zones); CardCollectionView cardlist = p.getGame().getCardsIn(zones);
final List<String> landkw = Lists.newArrayList(); final List<String> landkw = Lists.newArrayList();
final List<String> protectionkw = Lists.newArrayList(); final List<String> protectionkw = Lists.newArrayList();
final List<String> hexproofkw = Lists.newArrayList();
final List<String> allkw = Lists.newArrayList(); final List<String> allkw = Lists.newArrayList();
for (Card c : CardLists.getValidCards(cardlist, restrictions, p, host, null)) { for (Card c : CardLists.getValidCards(cardlist, restrictions, p, host, null)) {
@@ -1879,6 +1880,10 @@ public class CardFactoryUtil {
if (!protectionkw.contains(k)) { if (!protectionkw.contains(k)) {
protectionkw.add(k); protectionkw.add(k);
} }
} else if (k.startsWith("Hexproof")) {
if (!hexproofkw.contains(k)) {
hexproofkw.add(k);
}
} }
if (!allkw.contains(k)) { if (!allkw.contains(k)) {
allkw.add(k); allkw.add(k);
@@ -1890,6 +1895,8 @@ public class CardFactoryUtil {
filteredkw.addAll(protectionkw); filteredkw.addAll(protectionkw);
} else if (keyword.equals("Landwalk")) { } else if (keyword.equals("Landwalk")) {
filteredkw.addAll(landkw); filteredkw.addAll(landkw);
} else if (keyword.equals("Hexproof")) {
filteredkw.addAll(hexproofkw);
} else if (allkw.contains(keyword)) { } else if (allkw.contains(keyword)) {
filteredkw.add(keyword); filteredkw.add(keyword);
} }
@@ -4202,6 +4209,21 @@ public class CardFactoryUtil {
effect = "Mode$ RaiseCost | ValidCard$ Card.Self | Type$ Spell | Secondary$ True" effect = "Mode$ RaiseCost | ValidCard$ Card.Self | Type$ Spell | Secondary$ True"
+ " | Amount$ Escalate | Cost$ "+ manacost +" | EffectZone$ All" + " | Amount$ Escalate | Cost$ "+ manacost +" | EffectZone$ All"
+ " | Description$ " + sb.toString() + " (" + inst.getReminderText() + ")"; + " | Description$ " + sb.toString() + " (" + inst.getReminderText() + ")";
} else if (keyword.startsWith("Hexproof")) {
final StringBuilder sbDesc = new StringBuilder("Hexproof");
final StringBuilder sbValid = new StringBuilder();
if (!keyword.equals("Hexproof")) {
final String k[] = keyword.split(":");
sbDesc.append(" from ").append(k[2]);
sbValid.append("| ValidSource$ ").append(k[1]);
}
effect = "Mode$ CantTarget | Hexproof$ True | ValidCard$ Card.Self | Secondary$ True"
+ sbValid.toString() + " | Activator$ Opponent | Description$ "
+ sbDesc.toString() + " (" + inst.getReminderText() + ")";
} else if (keyword.startsWith("Strive")) { } else if (keyword.startsWith("Strive")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
final String manacost = k[1]; final String manacost = k[1];

View File

@@ -61,7 +61,7 @@ public final class CardUtil {
"Transmute", "Replicate", "Recover", "Suspend", "Aura swap", "Transmute", "Replicate", "Recover", "Suspend", "Aura swap",
"Fortify", "Transfigure", "Champion", "Evoke", "Prowl", "IfReach", "Fortify", "Transfigure", "Champion", "Evoke", "Prowl", "IfReach",
"Reinforce", "Unearth", "Level up", "Miracle", "Overload", "Reinforce", "Unearth", "Level up", "Miracle", "Overload",
"Scavenge", "Bestow", "Outlast", "Dash", "Renown", "Surge", "Emerge").build(); "Scavenge", "Bestow", "Outlast", "Dash", "Renown", "Surge", "Emerge", "Hexproof:").build();
/** List of keyword endings of keywords that could be modified by text changes. */ /** List of keyword endings of keywords that could be modified by text changes. */
public static final ImmutableList<String> modifiableKeywordEndings = ImmutableList.<String>builder().add( public static final ImmutableList<String> modifiableKeywordEndings = ImmutableList.<String>builder().add(
"walk", "cycling", "offering").build(); "walk", "cycling", "offering").build();

View File

@@ -0,0 +1,37 @@
package forge.game.keyword;
import java.util.Collection;
public class Hexproof extends KeywordInstance<Hexproof> {
private String type = "";
@Override
protected void parse(String details) {
if (!details.isEmpty()) {
type = details.split(":")[1];
}
}
@Override
protected String formatReminderText(String reminderText) {
if (type.isEmpty()) {
return "This can't be the target of spells or abilities your opponents control.";
}
return String.format(reminderText, type);
}
/* (non-Javadoc)
* @see forge.game.keyword.KeywordInstance#redundant(java.util.Collection)
*/
@Override
public boolean redundant(Collection<KeywordInterface> list) {
for (KeywordInterface i : list) {
if (i.getOriginal().equals(getOriginal())) {
return true;
}
}
return false;
}
}

View File

@@ -74,7 +74,7 @@ public enum Keyword {
GRAVESTORM(SimpleKeyword.class, false, "When you cast this spell, copy it for each permanent that was put into a graveyard from the battlefield this turn. You may choose new targets for the copies."), GRAVESTORM(SimpleKeyword.class, false, "When you cast this spell, copy it for each permanent that was put into a graveyard from the battlefield this turn. You may choose new targets for the copies."),
HASTE(SimpleKeyword.class, true, "This creature can attack and {T} as soon as it comes under your control."), HASTE(SimpleKeyword.class, true, "This creature can attack and {T} as soon as it comes under your control."),
HAUNT(SimpleKeyword.class, false, "When this is put into a graveyard, exile it haunting target creature."), HAUNT(SimpleKeyword.class, false, "When this is put into a graveyard, exile it haunting target creature."),
HEXPROOF(SimpleKeyword.class, true, "This can't be the target of spells or abilities your opponents control."), HEXPROOF(Hexproof.class, false, "This can't be the target of %s spells or abilities your opponents control."),
HIDEAWAY(SimpleKeyword.class, false, "This land enters the battlefield tapped. When it does, look at the top four cards of your library, exile one face down, then put the rest on the bottom of your library."), HIDEAWAY(SimpleKeyword.class, false, "This land enters the battlefield tapped. When it does, look at the top four cards of your library, exile one face down, then put the rest on the bottom of your library."),
HORSEMANSHIP(SimpleKeyword.class, true, "This creature can't be blocked except by creatures with horsemanship."), HORSEMANSHIP(SimpleKeyword.class, true, "This creature can't be blocked except by creatures with horsemanship."),
IMPROVISE(SimpleKeyword.class, true, "Your artifacts can help cast this spell. Each artifact you tap after you're done activating mana abilities pays for {1}."), IMPROVISE(SimpleKeyword.class, true, "Your artifacts can help cast this spell. Each artifact you tap after you're done activating mana abilities pays for {1}."),

View File

@@ -57,7 +57,7 @@ public class KeywordCollection implements Iterable<String>, Serializable {
public boolean insert(KeywordInterface inst) { public boolean insert(KeywordInterface inst) {
Keyword keyword = inst.getKeyword(); Keyword keyword = inst.getKeyword();
Collection<KeywordInterface> list = map.get(keyword); Collection<KeywordInterface> list = map.get(keyword);
if (list.isEmpty() || !keyword.isMultipleRedundant) { if (list.isEmpty() || !inst.redundant(list)) {
list.add(inst); list.add(inst);
return true; return true;
} }
@@ -87,7 +87,7 @@ public class KeywordCollection implements Iterable<String>, Serializable {
boolean result = false; boolean result = false;
while (it.hasNext()) { while (it.hasNext()) {
KeywordInterface k = it.next(); KeywordInterface k = it.next();
if (keyword.equals(k.getOriginal())) { if (k.getOriginal().startsWith(keyword)) {
it.remove(); it.remove();
result = true; result = true;
} }

View File

@@ -217,4 +217,12 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
public String toString() { public String toString() {
return this.getOriginal(); return this.getOriginal();
} }
/* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#redundant(java.util.Collection)
*/
@Override
public boolean redundant(Collection<KeywordInterface> list) {
return !list.isEmpty() && keyword.isMultipleRedundant;
}
} }

View File

@@ -50,4 +50,6 @@ public interface KeywordInterface extends Cloneable {
public Collection<StaticAbility> getStaticAbilities(); public Collection<StaticAbility> getStaticAbilities();
public KeywordInterface copy(final Card host, final boolean lki); public KeywordInterface copy(final Card host, final boolean lki);
public boolean redundant(final Collection<KeywordInterface> list);
} }

View File

@@ -84,6 +84,11 @@ public class StaticAbilityCantTarget {
return false; return false;
} }
if (params.containsKey("Hexproof") && (activator != null)) {
if (activator.hasKeyword("Spells and abilities you control can target hexproof creatures")) {
return false;
}
}
return true; return true;
} }

View File

@@ -0,0 +1,9 @@
Name:Knight of Grace
ManaCost:1 W
Types:Creature Human Knight
PT:2/2
K:First Strike
K:Hexproof:Card.Black:black
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 1 | IsPresent$ Permanent.Black | Description$ CARDNAME gets +1/+0 as long as any player controls a black permanent.
SVar:Picture:http://www.wizards.com/global/images/magic/general/knight_of_grace.jpg
Oracle:First strike\nHexproof from black (This creature can't be the target of black spells or abilities your opponents control.)\nKnight of Grace gets +1/+0 as long as any player controls a black permanent.

View File

@@ -0,0 +1,9 @@
Name:Knight of Malice
ManaCost:1 B
Types:Creature Human Knight
PT:2/2
K:First Strike
K:Hexproof:Card.White:white
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 1 | IsPresent$ Permanent.White | Description$ CARDNAME gets +1/+0 as long as any player controls a white permanent.
SVar:Picture:http://www.wizards.com/global/images/magic/general/knight_of_malice.jpg
Oracle:First strike\nHexproof from white (This creature can't be the target of white spells or abilities your opponents control.)\nKnight of Malice gets +1/+0 as long as any player controls a white permanent.