diff --git a/.gitattributes b/.gitattributes index 1a9b3cbe71a..6f752155921 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1420,6 +1420,7 @@ res/cardsfolder/c/cadaverous_bloom.txt svneol=native#text/plain res/cardsfolder/c/cadaverous_knight.txt svneol=native#text/plain res/cardsfolder/c/cage_of_hands.txt svneol=native#text/plain res/cardsfolder/c/cagemail.txt svneol=native#text/plain +res/cardsfolder/c/cairn_wanderer.txt -text res/cardsfolder/c/calciderm.txt svneol=native#text/plain res/cardsfolder/c/calciform_pools.txt svneol=native#text/plain res/cardsfolder/c/calcite_snapper.txt svneol=native#text/plain @@ -1925,6 +1926,7 @@ res/cardsfolder/c/compost.txt svneol=native#text/plain res/cardsfolder/c/compulsion.txt svneol=native#text/plain res/cardsfolder/c/compulsive_research.txt svneol=native#text/plain res/cardsfolder/c/concentrate.txt svneol=native#text/plain +res/cardsfolder/c/concerted_effort.txt -text res/cardsfolder/c/conch_horn.txt svneol=native#text/plain res/cardsfolder/c/conclave_equenaut.txt -text res/cardsfolder/c/conclave_phalanx.txt -text @@ -2414,6 +2416,7 @@ res/cardsfolder/d/death_cultist.txt svneol=native#text/plain res/cardsfolder/d/death_denied.txt svneol=native#text/plain res/cardsfolder/d/death_grasp.txt svneol=native#text/plain res/cardsfolder/d/death_hood_cobra.txt svneol=native#text/plain +res/cardsfolder/d/death_mask_duplicant.txt -text res/cardsfolder/d/death_mutation.txt svneol=native#text/plain res/cardsfolder/d/death_of_a_thousand_stings.txt svneol=native#text/plain res/cardsfolder/d/death_or_glory.txt -text diff --git a/res/cardsfolder/c/cairn_wanderer.txt b/res/cardsfolder/c/cairn_wanderer.txt new file mode 100644 index 00000000000..40a8885f372 --- /dev/null +++ b/res/cardsfolder/c/cairn_wanderer.txt @@ -0,0 +1,9 @@ +Name:Cairn Wanderer +ManaCost:4 B +Types:Creature Shapeshifter +PT:4/4 +S:Mode$ Continuous | EffectZone$ All | Affected$ Card.Self | CharacteristicDefining$ True | AddType$ AllCreatureTypes | Description$ Changeling (This card is every creature type at all times.) +S:Mode$ Continuous | EffectZone$ All | Affected$ Card.Self | CharacteristicDefining$ True | AddKeyword$ Flying & Fear & First Strike & Double Strike & Deathtouch & Haste & Landwalk & Lifelink & Protection & Reach & Trample & Shroud & Vigilance | SharedKeywordsZone$ Graveyard | SharedRestrictions$ Creature | Description$ As long as a creature card with flying is in a graveyard, CARDNAME has flying. The same is true for fear, first strike, double strike, deathtouch, haste, landwalk, lifelink, protection, reach, trample, shroud, and vigilance. +SVar:Picture:http://www.wizards.com/global/images/magic/general/cairn_wanderer.jpg +Oracle:Changeling (This card is every creature type at all times.)\nAs long as a creature card with flying is in a graveyard, Cairn Wanderer has flying. The same is true for fear, first strike, double strike, deathtouch, haste, landwalk, lifelink, protection, reach, trample, shroud, and vigilance. +SetInfo:LRW Rare \ No newline at end of file diff --git a/res/cardsfolder/c/concerted_effort.txt b/res/cardsfolder/c/concerted_effort.txt new file mode 100644 index 00000000000..43944c5ea4b --- /dev/null +++ b/res/cardsfolder/c/concerted_effort.txt @@ -0,0 +1,8 @@ +Name:Concerted Effort +ManaCost:2 W W +Types:Enchantment +T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ At the beginning of each upkeep, all creatures you control gain flying until end of turn if a creature you control has flying. The same is true for fear, first strike, double strike, landwalk, protection, trample, and vigilance. +SVar:TrigPump:AB$ PumpAll | Cost$ 0 | ValidCards$ Creature.YouCtrl | KW$ Flying & Fear & First Strike & Double Strike & Landwalk & Protection & Trample & Vigilance | SharedKeywordsZone$ Battlefield | SharedRestrictions$ Creature.YouCtrl +SVar:Picture:http://www.wizards.com/global/images/magic/general/concerted_effort.jpg +Oracle:At the beginning of each upkeep, all creatures you control gain flying until end of turn if a creature you control has flying. The same is true for fear, first strike, double strike, landwalk, protection, trample, and vigilance. +SetInfo:RAV Rare \ No newline at end of file diff --git a/res/cardsfolder/d/death_mask_duplicant.txt b/res/cardsfolder/d/death_mask_duplicant.txt new file mode 100644 index 00000000000..a31f92962d8 --- /dev/null +++ b/res/cardsfolder/d/death_mask_duplicant.txt @@ -0,0 +1,12 @@ +Name:Death-Mask Duplicant +ManaCost:7 +Types:Artifact Creature Shapeshifter +PT:5/5 +A:AB$ ChangeZone | PrecostDesc$ Imprint - | Cost$ 1 | Imprint$ True | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Creature.YouOwn | SpellDescription$ Exile target creature card from your graveyard. +S:Mode$ Continuous | EffectZone$ All | Affected$ Card.Self | CharacteristicDefining$ True | AddKeyword$ Flying & Fear & First Strike & Double Strike & Haste & Landwalk & Protection & Trample | SharedKeywordsZone$ Exile | SharedRestrictions$ Card.IsImprinted | Description$ As long as a card exiled with CARDNAME has flying, CARDNAME has flying. The same is true for fear, first strike, double strike, haste, landwalk, protection, and trample. +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Execute$ TrigForget | Static$ True +SVar:TrigForget:DB$ Cleanup | ClearImprinted$ True +SVar:RemAIDeck:True +SVar:Picture:http://www.wizards.com/global/images/magic/general/death_mask_duplicant.jpg +Oracle:Imprint - {1}: Exile target creature card from your graveyard.\nAs long as a card exiled with Death-Mask Duplicant has flying, Death-Mask Duplicant has flying. The same is true for fear, first strike, double strike, haste, landwalk, protection, and trample. +SetInfo:DST Uncommon \ No newline at end of file diff --git a/src/main/java/forge/card/ability/effects/PumpAllEffect.java b/src/main/java/forge/card/ability/effects/PumpAllEffect.java index 1d1a2d510b5..0d6ea958029 100644 --- a/src/main/java/forge/card/ability/effects/PumpAllEffect.java +++ b/src/main/java/forge/card/ability/effects/PumpAllEffect.java @@ -9,11 +9,70 @@ import forge.Command; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.ability.SpellAbilityEffect; +import forge.card.cardfactory.CardFactoryUtil; import forge.card.spellability.SpellAbility; import forge.game.player.Player; import forge.game.zone.ZoneType; public class PumpAllEffect extends SpellAbilityEffect { + private void applyPumpAll(final SpellAbility sa, final List list, final int a, + final int d, final List keywords, final ArrayList affectedZones) { + + for (final Card tgtC : list) { + + // only pump things in the affected zones. + boolean found = false; + for (final ZoneType z : affectedZones) { + if (tgtC.isInZone(z)) { + found = true; + break; + } + } + if (!found) { + continue; + } + + tgtC.addTempAttackBoost(a); + tgtC.addTempDefenseBoost(d); + + for (int i = 0; i < keywords.size(); i++) { + tgtC.addExtrinsicKeyword(keywords.get(i)); + if (keywords.get(i).equals("Suspend")) { + tgtC.setSuspend(true); + } + } + + if (sa.hasParam("RememberAllPumped")) { + sa.getSourceCard().addRemembered(tgtC); + } + + if (!sa.hasParam("Permanent")) { + // If not Permanent, remove Pumped at EOT + final Command untilEOT = new Command() { + private static final long serialVersionUID = 5415795460189457660L; + + @Override + public void execute() { + tgtC.addTempAttackBoost(-1 * a); + tgtC.addTempDefenseBoost(-1 * d); + + if (keywords.size() > 0) { + for (int i = 0; i < keywords.size(); i++) { + tgtC.removeExtrinsicKeyword(keywords.get(i)); + } + } + } + }; + if (sa.hasParam("UntilUntaps")) { + sa.getSourceCard().addUntapCommand(untilEOT); + } else if (sa.hasParam("UntilEndOfCombat")) { + Singletons.getModel().getGame().getEndOfCombat().addUntil(untilEOT); + } else { + Singletons.getModel().getGame().getEndOfTurn().addUntil(untilEOT); + } + } + } + } @Override protected String getStackDescription(SpellAbility sa) { final StringBuilder sb = new StringBuilder(); @@ -65,65 +124,16 @@ public class PumpAllEffect extends SpellAbilityEffect { list = AbilityUtils.filterListByType(list, valid, sa); - final List keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList(); + List keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList(); final int a = AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumAtt"), sa); final int d = AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumDef"), sa); - final String remember = sa.getParam("RememberAllPumped"); - for (final Card tgtC : list) { - - // only pump things in the affected zones. - boolean found = false; - for (final ZoneType z : affectedZones) { - if (tgtC.isInZone(z)) { - found = true; - break; - } - } - if (!found) { - continue; - } - - tgtC.addTempAttackBoost(a); - tgtC.addTempDefenseBoost(d); - - for (int i = 0; i < keywords.size(); i++) { - tgtC.addExtrinsicKeyword(keywords.get(i)); - if (keywords.get(i).equals("Suspend")) { - tgtC.setSuspend(true); - } - } - - if (remember != null) { - sa.getSourceCard().addRemembered(tgtC); - } - - if (!sa.hasParam("Permanent")) { - // If not Permanent, remove Pumped at EOT - final Command untilEOT = new Command() { - private static final long serialVersionUID = 5415795460189457660L; - - @Override - public void execute() { - tgtC.addTempAttackBoost(-1 * a); - tgtC.addTempDefenseBoost(-1 * d); - - if (keywords.size() > 0) { - for (int i = 0; i < keywords.size(); i++) { - tgtC.removeExtrinsicKeyword(keywords.get(i)); - } - } - } - }; - if (sa.hasParam("UntilUntaps")) { - sa.getSourceCard().addUntapCommand(untilEOT); - } else if (sa.hasParam("UntilEndOfCombat")) { - Singletons.getModel().getGame().getEndOfCombat().addUntil(untilEOT); - } else { - Singletons.getModel().getGame().getEndOfTurn().addUntil(untilEOT); - } - } + if (sa.hasParam("SharedKeywordsZone")) { + List zones = ZoneType.listValueOf(sa.getParam("SharedKeywordsZone")); + String[] restrictions = sa.hasParam("SharedRestrictions") ? sa.getParam("SharedRestrictions").split(",") : new String[] {"Card"}; + keywords = CardFactoryUtil.sharedKeywords(sa.getParam("KW").split(" & "), restrictions, zones, sa.getSourceCard()); } + this.applyPumpAll(sa, list, a, d, keywords, affectedZones); } // pumpAllResolve() } diff --git a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java index a44c0dfb466..bd69df415ba 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java @@ -2170,6 +2170,53 @@ public class CardFactoryUtil { return true; } + + /** + *

+ * sharedKeywords. + *

+ * + * @param kw + * a {@link forge.CardList} object. + * @return a List. + */ + public static List sharedKeywords(final String[] kw, final String[] restrictions, + final List zones, final Card host) { + final List filteredkw = new ArrayList(); + final Player p = host.getController(); + List cardlist = new ArrayList(p.getGame().getCardsIn(zones)); + final List landkw = new ArrayList(); + final List protectionkw = new ArrayList(); + final List allkw = new ArrayList(); + + cardlist = CardLists.getValidCards(cardlist, restrictions, p, host); + for (Card c : cardlist) { + for (String k : c.getKeyword()) { + if (k.endsWith("walk")) { + if (!landkw.contains(k)) { + landkw.add(k); + } + } else if (k.startsWith("Protection")) { + if (!protectionkw.contains(k)) { + protectionkw.add(k); + } + } + if (!allkw.contains(k)) { + allkw.add(k); + } + } + } + for (String keyword : kw) { + if (keyword.equals("Protection")) { + filteredkw.addAll(protectionkw); + } else if (keyword.equals("Landwalk")) { + filteredkw.addAll(landkw); + } else if (allkw.contains(keyword)) { + filteredkw.add(keyword); + } + } + return filteredkw; + } /** *

diff --git a/src/main/java/forge/card/staticability/StaticAbilityContinuous.java b/src/main/java/forge/card/staticability/StaticAbilityContinuous.java index 99107790b03..cf353085aca 100644 --- a/src/main/java/forge/card/staticability/StaticAbilityContinuous.java +++ b/src/main/java/forge/card/staticability/StaticAbilityContinuous.java @@ -160,6 +160,12 @@ public class StaticAbilityContinuous { addKeywords[w] = addKeywords[w].replaceAll("ChosenName", "Card.named" + chosenName); } } + if (params.containsKey("SharedKeywordsZone")) { + List zones = ZoneType.listValueOf(params.get("SharedKeywordsZone")); + String[] restrictions = params.containsKey("SharedRestrictions") ? params.get("SharedRestrictions").split(",") : new String[] {"Card"}; + List kw = CardFactoryUtil.sharedKeywords(addKeywords, restrictions, zones, hostCard); + addKeywords = kw.toArray(new String[kw.size()]); + } } if (params.containsKey("AddHiddenKeyword")) {