From 9af8fddaf3941616b204d16b831dc124144a38f5 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 18 Mar 2018 15:28:22 +0100 Subject: [PATCH] Hexproof: is now done as CantTarget with special shared handling --- .../src/main/java/forge/game/card/Card.java | 11 ++---- .../java/forge/game/card/CardFactoryUtil.java | 22 +++++++++++ .../main/java/forge/game/card/CardUtil.java | 2 +- .../java/forge/game/keyword/Hexproof.java | 37 +++++++++++++++++++ .../main/java/forge/game/keyword/Keyword.java | 2 +- .../forge/game/keyword/KeywordCollection.java | 4 +- .../forge/game/keyword/KeywordInstance.java | 8 ++++ .../forge/game/keyword/KeywordInterface.java | 2 + .../StaticAbilityCantTarget.java | 5 +++ .../cardsfolder/upcoming/knight_of_grace.txt | 9 +++++ .../cardsfolder/upcoming/knight_of_malice.txt | 9 +++++ 11 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/keyword/Hexproof.java create mode 100644 forge-gui/res/cardsfolder/upcoming/knight_of_grace.txt create mode 100644 forge-gui/res/cardsfolder/upcoming/knight_of_malice.txt diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 9abe79c01e7..7ebfd438f97 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -1526,6 +1526,10 @@ public class Card extends GameEntity implements Comparable { sbx.append(" (" + inst.getReminderText() + ")"); 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")) { sbLong.append(keyword).append("\r\n"); } else if (keyword.startsWith("Presence")) { @@ -4975,13 +4979,6 @@ public class Card extends GameEntity implements Comparable { result.setFalse(); } 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.": if (source.isAura()) { result.setFalse(); diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index efff0ed77b3..da4557d290c 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -1866,6 +1866,7 @@ public class CardFactoryUtil { CardCollectionView cardlist = p.getGame().getCardsIn(zones); final List landkw = Lists.newArrayList(); final List protectionkw = Lists.newArrayList(); + final List hexproofkw = Lists.newArrayList(); final List allkw = Lists.newArrayList(); for (Card c : CardLists.getValidCards(cardlist, restrictions, p, host, null)) { @@ -1879,6 +1880,10 @@ public class CardFactoryUtil { if (!protectionkw.contains(k)) { protectionkw.add(k); } + } else if (k.startsWith("Hexproof")) { + if (!hexproofkw.contains(k)) { + hexproofkw.add(k); + } } if (!allkw.contains(k)) { allkw.add(k); @@ -1890,6 +1895,8 @@ public class CardFactoryUtil { filteredkw.addAll(protectionkw); } else if (keyword.equals("Landwalk")) { filteredkw.addAll(landkw); + } else if (keyword.equals("Hexproof")) { + filteredkw.addAll(hexproofkw); } else if (allkw.contains(keyword)) { filteredkw.add(keyword); } @@ -4202,6 +4209,21 @@ public class CardFactoryUtil { effect = "Mode$ RaiseCost | ValidCard$ Card.Self | Type$ Spell | Secondary$ True" + " | Amount$ Escalate | Cost$ "+ manacost +" | EffectZone$ All" + " | 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")) { final String[] k = keyword.split(":"); final String manacost = k[1]; diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index a48988cb1ba..38dedebf364 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -61,7 +61,7 @@ public final class CardUtil { "Transmute", "Replicate", "Recover", "Suspend", "Aura swap", "Fortify", "Transfigure", "Champion", "Evoke", "Prowl", "IfReach", "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. */ public static final ImmutableList modifiableKeywordEndings = ImmutableList.builder().add( "walk", "cycling", "offering").build(); diff --git a/forge-game/src/main/java/forge/game/keyword/Hexproof.java b/forge-game/src/main/java/forge/game/keyword/Hexproof.java new file mode 100644 index 00000000000..e6a6fce1834 --- /dev/null +++ b/forge-game/src/main/java/forge/game/keyword/Hexproof.java @@ -0,0 +1,37 @@ +package forge.game.keyword; + +import java.util.Collection; + +public class Hexproof extends KeywordInstance { + 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 list) { + for (KeywordInterface i : list) { + if (i.getOriginal().equals(getOriginal())) { + return true; + } + } + return false; + } + + +} diff --git a/forge-game/src/main/java/forge/game/keyword/Keyword.java b/forge-game/src/main/java/forge/game/keyword/Keyword.java index bdf308b5677..25e9ac0280c 100644 --- a/forge-game/src/main/java/forge/game/keyword/Keyword.java +++ b/forge-game/src/main/java/forge/game/keyword/Keyword.java @@ -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."), 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."), - 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."), 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}."), diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java b/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java index a0d545fabc5..80393dc2186 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java @@ -57,7 +57,7 @@ public class KeywordCollection implements Iterable, Serializable { public boolean insert(KeywordInterface inst) { Keyword keyword = inst.getKeyword(); Collection list = map.get(keyword); - if (list.isEmpty() || !keyword.isMultipleRedundant) { + if (list.isEmpty() || !inst.redundant(list)) { list.add(inst); return true; } @@ -87,7 +87,7 @@ public class KeywordCollection implements Iterable, Serializable { boolean result = false; while (it.hasNext()) { KeywordInterface k = it.next(); - if (keyword.equals(k.getOriginal())) { + if (k.getOriginal().startsWith(keyword)) { it.remove(); result = true; } diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java b/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java index 7329dc81c9b..0e9525ca7e7 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java @@ -217,4 +217,12 @@ public abstract class KeywordInstance> implements K public String toString() { return this.getOriginal(); } + + /* (non-Javadoc) + * @see forge.game.keyword.KeywordInterface#redundant(java.util.Collection) + */ + @Override + public boolean redundant(Collection list) { + return !list.isEmpty() && keyword.isMultipleRedundant; + } } diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java b/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java index 9fbde89f4af..4830550f2b2 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java @@ -50,4 +50,6 @@ public interface KeywordInterface extends Cloneable { public Collection getStaticAbilities(); public KeywordInterface copy(final Card host, final boolean lki); + + public boolean redundant(final Collection list); } \ No newline at end of file diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantTarget.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantTarget.java index 0ce6e0150a2..7b773e928da 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantTarget.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantTarget.java @@ -84,6 +84,11 @@ public class StaticAbilityCantTarget { return false; } + if (params.containsKey("Hexproof") && (activator != null)) { + if (activator.hasKeyword("Spells and abilities you control can target hexproof creatures")) { + return false; + } + } return true; } diff --git a/forge-gui/res/cardsfolder/upcoming/knight_of_grace.txt b/forge-gui/res/cardsfolder/upcoming/knight_of_grace.txt new file mode 100644 index 00000000000..213562dfe8c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/knight_of_grace.txt @@ -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. diff --git a/forge-gui/res/cardsfolder/upcoming/knight_of_malice.txt b/forge-gui/res/cardsfolder/upcoming/knight_of_malice.txt new file mode 100644 index 00000000000..978f1ae70b9 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/knight_of_malice.txt @@ -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.