From 3d070ce6fa4614e83f9baeda76236f4660ec9970 Mon Sep 17 00:00:00 2001 From: jendave Date: Sat, 6 Aug 2011 03:04:35 +0000 Subject: [PATCH] - Cathartic Adept should tap when using its ability. - Added Arid Mesa, Marsh Flats, Misty Rainforest, Scalding Tarn, Verdant Catacombs, Bloodstained Mire, Flooded Strand, Polluted Delta, Windswept Heath, Wooded Foothills. - Added Rob's abPumpTgt keyword, including a bunch of new cards supported by it. --- res/card-pictures.txt | 9 + res/cards.txt | 174 +++++++- res/main.properties | 2 +- src/forge/CardFactory.java | 622 +++++++++++++++++++++++++++ src/forge/CardFactoryUtil.java | 12 +- src/forge/CardFactory_Creatures.java | 29 +- src/forge/CardFactory_Lands.java | 125 +++++- src/forge/CombatUtil.java | 2 +- src/forge/ReadCard.java | 1 + 9 files changed, 956 insertions(+), 20 deletions(-) diff --git a/res/card-pictures.txt b/res/card-pictures.txt index 5df51aa3307..928956893b8 100644 --- a/res/card-pictures.txt +++ b/res/card-pictures.txt @@ -18,6 +18,15 @@ forest.jpg http://resources.wizards.com/magic/cards/unh/en-us/card73946.jpg forest1.jpg http://gatherer.wizards.com/handlers/image.ashx?type=card&multiverseid=2748 forest2.jpg http://gatherer.wizards.com/handlers/image.ashx?type=card&multiverseid=587 forest3.jpg http://gatherer.wizards.com/handlers/image.ashx?type=card&multiverseid=586 +wooded_foothills.jpg http://www.wizards.com/global/images/magic/general/wooded_foothills.jpg +windswept_heath.jpg http://www.wizards.com/global/images/magic/general/windswept_heath.jpg +polluted_delta.jpg http://www.wizards.com/global/images/magic/general/polluted_delta.jpg +flooded_strand.jpg http://www.wizards.com/global/images/magic/general/flooded_strand.jpg +bloodstaind_mire.jpg http://www.wizards.com/global/images/magic/general/bloodstained_mire.jpg +verdant_catacombs.jpg http://www.wizards.com/global/images/magic/general/verdant_catacombs.jpg +scalding_tarn.jpg http://www.wizards.com/global/images/magic/general/scalding_tarn.jpg +marsh_flats.jpg http://www.wizards.com/global/images/magic/general/marsh_flats.jpg +arid_mesa.jpg http://www.wizards.com/global/images/magic/general/arid_mesa.jpg serpent_generator.jpg http://www.wizards.com/global/images/magic/general/serpent_generator.jpg pit_scorpion.jpg http://www.wizards.com/global/images/magic/general/pit_scorpion.jpg bridge_from_below.jpg http://www.wizards.com/global/images/magic/general/bridge_from_below.jpg diff --git a/res/cards.txt b/res/cards.txt index d88a331d3e5..609a517cc84 100644 --- a/res/cards.txt +++ b/res/cards.txt @@ -1,3 +1,169 @@ +Feral Animist +1 R G +Creature Goblic Shaman +no text +2/1 +abPump 3:Count$CardPower/+0:Feral Animist gets +X/+0 until end of turn, where X is its power. + +Fire-Belly Changeling +1 R +Creature Shapeshifter +Play this ability no more than twice each turn. +1/1 +abPump R:+1/+0 + +Kitsune Loreweaver +1 W +Creature Fox Cleric +no text +2/1 +abPump 1 W:+0/Count$NumCardsInYourHand:Kitsune Loreweaver get +0/+X until end of turn, where X is the number of cards in your hand. + +Knight of the Skyward Eye +1 W +Creature Human Knight +Play this ability only once each turn. +2/2 +abPump 3 G:+3/+3 + +Power Armor +4 +Artifact +Domain +abPumpTgt 3 T:Count$Domain/Count$Domain:Target creature gets +1/+1 until end of turn for each basic land type among lands you control. + +Seedcradle Witch +GW +Creature Elf Shaman +no text +1/1 +abPumpTgt 2 G W:+3/+3:Drawback$UntapTgt:Target creature gets +3/+3 until end of turn. Untap that creature. + +Stormcloud Djinn +4 U +Creature Djinn +no text +3/3 +Flying +This creature can block only creatures with flying. +abPump R R:+2/+0:Drawback$DamageYou/1:Stormcloud Djinn gets +2/+0 until end of turn and deals 1 damage to you. + +Timberwatch Elf +2 G +Creature Elf +no text +1/2 +abPumpTgt T:Count$TypeOnBattlefield.Elf/Count$TypeOnBattlefield.Elf:Target creature get +X/+X until end of turn, where X is the number of Elves on the battlefield. + +Wandering Goblins +2 R +Creature Goblin Warrior +Domain +0/3 +abPump 3:Count$Domain/+0:Wandering Goblins gets +1/+0 until end of turn for each basic land type among lands you control. + +Rappelling Scouts +2 W W +Creature Human Rebel Scout +no text +1/4 +Flying +abPumpTgt 2 W:Protection from white +abPumpTgt 2 W:Protection from blue +abPumpTgt 2 W:Protection from black +abPumpTgt 2 W:Protection from red +abPumpTgt 2 W:Protection from green + +Jodah's Avenger +5 U +Creature Shapeshifter +no text +4/4 +abPump 0:-1/-1/Double Strike +abPump 0:-1/-1/Protection from red +abPump 0:-1/-1/Vigilance +abPump 0:-1/-1/Shadow + +Wormwood Dryad +2 G +Creature Dryad +no text +3/1 +abPump G:Forestwalk:Drawback$DamageYou/1:Wormwood Dryad gains forestwalk until end of turn and deals 1 damage to you. +abPump B:Swampwalk:Drawback$DamageYou/1:Wormwood Dryad gains swampwalk until end of turn and deals 1 damage to you. + +Wormwood Treefolk +3 G G +Creature Treefolk +no text +4/4 +abPump G G:Forestwalk:Drawback$DamageYou/2:Wormwood Dryad gains forestwalk until end of turn and deals 2 damage to you. +abPump B B:Swampwalk:Drawback$DamageYou/2:Wormwood Dryad gains swampwalk until end of turn and deals 2 damage to you. + +Auriok Bladewarden +1 W +Creature Human Soldier +no text +1/1 +abPumpTgt T:Count$CardPower/Count$CardPower:Target creature get +X/+X until end of turn, where X is Auriok Bladewarden's power. + +Boreal Centaur +1 G +Snow Creature Centaur Warrior +Play this ability only once each turn. +2/2 +abPump S:+1/+1 + +Wooded Foothills +no cost +Land +no text + +Windswept Heath +no cost +Land +no text + +Polluted Delta +no cost +Land +no text + +Flooded Strand +no cost +Land +no text + +Bloodstained Mire +no cost +Land +no text + +Verdant Catacombs +no cost +Land +no text + +Scalding Tarn +no cost +Land +no text + +Misty Rainforest +no cost +Land +no text + +Marsh Flats +no cost +Land +no text + +Arid Mesa +no cost +Land +no text + Serpent Generator 6 Artifact @@ -1119,6 +1285,7 @@ no cost Land no text tap: add 1 +abPumpTgt R G T:+1/+1/Trample Goblin Burrows no cost @@ -6060,6 +6227,7 @@ no text 4/4 Changeling Protection from black +abPump 2 G G:Count$CardPower/Count$CardPower:Chameleon Colossus gets +X/+X until end of turn, where X is its power. Rakdos Pit Dragon 2 R R @@ -14260,12 +14428,6 @@ Creature Hound (NOTE: " and becomes color of your choice." not implemented.) 2/2 -Timberwatch Elf -2 G -Creature Elf -no text -1/2 - Wirewood Elf 1 G Creature Elf diff --git a/res/main.properties b/res/main.properties index 2bb0a7a4739..82b5366cdbe 100644 --- a/res/main.properties +++ b/res/main.properties @@ -1,6 +1,6 @@ program/mail=mtgerror@yahoo.com program/forum=http://www.slightlymagic.net/forum/viewforum.php?f=26 -program/version=Forge -- official beta: 09/12/14, SVN revision: 214 +program/version=Forge -- official beta: 09/12/14, SVN revision: 215 tokens--file=AllTokens.txt diff --git a/src/forge/CardFactory.java b/src/forge/CardFactory.java index 330e657bd21..cee587e154d 100644 --- a/src/forge/CardFactory.java +++ b/src/forge/CardFactory.java @@ -817,6 +817,628 @@ public class CardFactory implements NewConstants { a1.setBeforePayMana(new Input_PayManaCost(a1)); } //if (should RegenerateMe) } //while - card has more RegenerateMe - Jungle Troll has two Regenerate keywords + + while(hasKeyword(card, "abPump") != -1) + { + int n = hasKeyword(card, "abPump"); + if(n != -1) + { + String parse = card.getKeyword().get(n).toString(); + card.removeIntrinsicKeyword(parse); + + String k[] = parse.split(":"); + + final boolean Tgt[] = {false}; + Tgt[0] = k[0].contains("Tgt"); + + String tmpCost; + if (Tgt[0]) + tmpCost = k[0].substring(9); + else + tmpCost = k[0].substring(6); + + boolean tapCost = false; + boolean tapOnlyCost = false; + + if (tmpCost.contains("T")) + { + tapCost = true; + tmpCost = tmpCost.replace("T", ""); + tmpCost = tmpCost.trim(); + if (tmpCost.length() == 0) + tapOnlyCost = true; + } + final String manaCost = tmpCost.trim(); + + final int NumAttack[] = {-1138}; + final String AttackX[] = {"none"}; + final int NumDefense[] = {-1138}; + final String DefenseX[] = {"none"}; + final String Keyword[] = {"none"}; + + String ptk[] = k[1].split("/"); + + if (ptk.length == 1) + Keyword[0] = ptk[0]; + + if (ptk.length >= 2) + { + if (ptk[0].length() <= 3) + NumAttack[0] = Integer.parseInt(ptk[0].replace("+", "")); + else + if (ptk[0].startsWith("Count$")) + { + String kk[] = ptk[0].split("\\$"); + AttackX[0] = kk[1].replace("\\", "/"); + } + + if (ptk[1].length() <= 3) + NumDefense[0] = Integer.parseInt(ptk[1].replace("+", "")); + else + if (ptk[1].startsWith("Count$")) + { + String kk[] = ptk[1].split("\\$"); + DefenseX[0] = kk[1].replace("\\", "/"); + } + } + + if (ptk.length == 3) + Keyword[0] = ptk[2]; + + final String DrawBack[] = {"none"}; + final String spDesc[] = {"none"}; + final String stDesc[] = {"none"}; + String d = new String("none"); + + if ((AttackX[0].equals("none") && !(NumAttack[0] == -1138)) && (DefenseX[0].equals("none") && !(NumDefense[0] == -1138)) && Keyword[0].equals("none")) + { + // pt boost + if (Tgt[0] == true) + d = "Target creature gets "; + else + d = cardName + " gets "; + + if (NumAttack[0] > 0 || (NumAttack[0] == 0 && NumDefense[0] > 0)) // +0/+1 + d = d + "+"; + else if (NumAttack[0] < 0 || (NumAttack[0] == 0 && NumDefense[0] < 0)) // -0/-1 + d = d + "-"; + + d = d + Math.abs(NumAttack[0]) + "/"; + + if (NumDefense[0] > 0 || (NumDefense[0] == 0 && NumAttack[0] > 0)) // +1/+0 + d = d + "+"; + else if (NumDefense[0] < 0 || (NumDefense[0] == 0 && NumAttack[0] < 0)) // -1/-0 + d = d + "-"; + + d = d + Math.abs(NumDefense[0]) + " until end of turn."; + } + if ((AttackX[0].equals("none") && NumAttack[0] == -1138) && (DefenseX[0].equals("none") && NumDefense[0] == -1138) && !Keyword[0].equals("none")) + { + // k boost + if (Tgt[0] == true) + d = "Target creature gains "; + else + d = cardName + " gains "; + + d = d + Keyword[0] + " until end of turn."; + } + if ((AttackX[0].equals("none") && !(NumAttack[0] == -1138)) && (DefenseX[0].equals("none") && !(NumDefense[0] == -1138)) && !Keyword[0].equals("none")) + { + // ptk boost + if (Tgt[0] == true) + d = "Target creature gets "; + else + d = cardName + " gets "; + + if (NumAttack[0] > 0 || (NumAttack[0] == 0 && NumDefense[0] > 0)) // +0/+1 + d = d + "+"; + else if (NumAttack[0] < 0 || (NumAttack[0] == 0 && NumDefense[0] < 0)) // -0/-1 + d = d + "-"; + + d = d + Math.abs(NumAttack[0]) + "/"; + + if (NumDefense[0] > 0 || (NumDefense[0] == 0 && NumAttack[0] > 0)) // +1/+0 + d = d + "+"; + else if (NumDefense[0] < 0 || (NumDefense[0] == 0 && NumAttack[0] < 0)) // -1/-0 + d = d + "-"; + + d = d + Math.abs(NumDefense[0]); + + d = d + " and gains " + Keyword[0] + " until end of turn."; + } + + if (k.length > 2) + { + if (k[2].contains("Drawback$")) + { + String kk[] = k[2].split("\\$"); + DrawBack[0] = kk[1]; + if (k.length > 3) + d = k[3]; + } + else + if (k.length > 2) + d = k[2]; + } + + if (!d.equals("none")) + { + if (tapOnlyCost == true) + spDesc[0] = "Tap: " + d; + else if (tapCost == true) + spDesc[0] = manaCost + ", tap: " + d; + else + spDesc[0] = manaCost + ": " + d; + + stDesc[0] = d; + } + + if (! tapCost) + { + final SpellAbility ability = new Ability_Activated(card, manaCost) + { + private static final long serialVersionUID = -1118592153328758083L; + + private int defense; + private String keyword; + + private int getNumAttack() + { + if (NumAttack[0] != -1138) + return NumAttack[0]; + + if (! AttackX[0].equals("none")) + return CardFactoryUtil.xCount(card, AttackX[0]); + + return 0; + } + private int getNumDefense() + { + if (NumDefense[0] != -1138) + return NumDefense[0]; + + if (! DefenseX[0].equals("none")) + return CardFactoryUtil.xCount(card, DefenseX[0]); + + return 0; + } + + public boolean canPlayAI() + { + defense = getNumDefense(); + keyword = Keyword[0]; + + if (AllZone.Phase.getPhase().equals(Constant.Phase.Main2)) + return false; + + if (Tgt[0] == false) + { + setTargetCard(card); + + if ((card.getNetDefense() + defense > 0) && + (! card.getKeyword().contains(keyword))) + if (card.hasSickness() && keyword.equals("Haste")) + return true; + else if ((card.hasSickness() && (! keyword.equals("Haste"))) || + ((! card.hasSickness()) && keyword.equals("Haste"))) + return false; + else + { + Random r = new Random(); + if (r.nextFloat() <= Math.pow(.6667, card.getAbilityUsed())) + return CardFactoryUtil.AI_doesCreatureAttack(card); + } + } + + CardList list = getCreatures(); + if (!list.isEmpty()) + { + boolean goodt = false; + Card t = new Card(); + while (goodt == false && !list.isEmpty()) // loop until we find a target that is best and won't die when targeted or until no more creatures + { + t = CardFactoryUtil.AI_getBestCreature(list); + if ((t.getNetDefense() + defense) > 0) // handle negative defense pumps + goodt = true; + else + list.remove(t); + } + if (goodt == true) + { + Random r = new Random(); + if (r.nextFloat() <= Math.pow(.6667, card.getAbilityUsed())) + { + setTargetCard(t); + return true; + } + } + } + + return false; + } + + public boolean canPlay() + { + return (CardFactoryUtil.canUseAbility(card)) && + (AllZone.GameAction.isCardInPlay(card)) && + (! card.isFaceDown()); + } + + private CardList getCreatures() + { + CardList list = new CardList(AllZone.Computer_Play.getCards()); + list = list.filter(new CardListFilter() + { + public boolean addCard(Card c) + { + if (c.isCreature()) + { + if (c.hasSickness() && keyword.equals("Haste")) // AI_doesCreatureAttack would have prevented the effect from granting haste, because it assumes the creature would already have it + return CardFactoryUtil.canTarget(card, c); + + return (CardFactoryUtil.AI_doesCreatureAttack(c)) && + (CardFactoryUtil.canTarget(card, c)) && + (!keyword.equals("none") && !c.getKeyword().contains(keyword)) && + (! (! c.hasSickness()) && keyword.equals("Haste")); // if creature doesn't have sickness, the haste keyword won't help + } + return false; + } + }); + // list.remove(card); // if mana-only cost, allow self-target + return list; + }//getCreatures() + public void resolve() + { + if(AllZone.GameAction.isCardInPlay(getTargetCard()) && CardFactoryUtil.canTarget(card, getTargetCard()) ) + { + final Card[] creature = new Card[1]; + if (Tgt[0] == true) + creature[0] = getTargetCard(); + else + creature[0] = card; + + final int a = getNumAttack(); + final int d = getNumDefense(); + + final Command EOT = new Command() + { + private static final long serialVersionUID = -8840812331316327448L; + + public void execute() + { + if(AllZone.GameAction.isCardInPlay(creature[0])) + { + creature[0].addTempAttackBoost(-1 * a); + creature[0].addTempDefenseBoost(-1 * d); + if (! Keyword[0].equals("none")) + creature[0].removeExtrinsicKeyword(Keyword[0]); + } + + } + }; + + creature[0].addTempAttackBoost(a); + creature[0].addTempDefenseBoost(d); + if (! Keyword[0].equals("none")) + creature[0].addExtrinsicKeyword(Keyword[0]); + + card.setAbilityUsed(card.getAbilityUsed()+1); + AllZone.EndOfTurn.addUntil(EOT); + + if (! DrawBack[0].equals("none")) + CardFactoryUtil.doDrawBack(DrawBack[0], 0, card.getController(), AllZone.GameAction.getOpponent(card.getController()), null, card, creature[0]); + + }//if (card is in play) + }//resolve() + };//SpellAbility + + ability.setDescription(spDesc[0]); + ability.setStackDescription(stDesc[0]); + + if (Tgt[0] == true) + ability.setBeforePayMana(CardFactoryUtil.input_targetCreature(ability)); + else + ability.setTargetCard(card); + card.addSpellAbility(ability); + } + if (tapOnlyCost) + { + final SpellAbility ability = new Ability_Tap(card) + { + private static final long serialVersionUID = 5252594757468128739L; + + private int defense; + private String keyword; + + private int getNumAttack() + { + if (NumAttack[0] != -1138) + return NumAttack[0]; + + if (! AttackX[0].equals("none")) + return CardFactoryUtil.xCount(card, AttackX[0]); + + return 0; + } + private int getNumDefense() + { + if (NumDefense[0] != -1138) + return NumDefense[0]; + + if (! DefenseX[0].equals("none")) + return CardFactoryUtil.xCount(card, DefenseX[0]); + + return 0; + } + + public boolean canPlayAI() + { + defense = getNumDefense(); + keyword = Keyword[0]; + + if(CardFactoryUtil.AI_doesCreatureAttack(card)) + return false; + + if (AllZone.Phase.getPhase().equals(Constant.Phase.Main2)) + return false; + + CardList list = getCreatures(); + if (!list.isEmpty()) + { + boolean goodt = false; + Card t = new Card(); + while (goodt == false && !list.isEmpty()) + { + t = CardFactoryUtil.AI_getBestCreature(list); + if ((t.getNetDefense() + defense) > 0) + goodt = true; + else + list.remove(t); + } + if (goodt == true) + { + setTargetCard(t); + return true; + } + } + + return false; + } + public boolean canPlay() + { + boolean sick = true; + + if (!card.hasSickness() || !card.isCreature()) + sick = false; + + if (card.isUntapped() && AllZone.GameAction.isCardInPlay(card) + && !sick && !card.isFaceDown()) + return true; + else + return false; + } + + CardList getCreatures() + { + CardList list = new CardList(AllZone.Computer_Play.getCards()); + list = list.filter(new CardListFilter() + { + public boolean addCard(Card c) + { + if (c.isCreature()) + { + if (c.hasSickness() && keyword.equals("Haste")) + return CardFactoryUtil.canTarget(card, c); + + return (CardFactoryUtil.AI_doesCreatureAttack(c)) && + (CardFactoryUtil.canTarget(card, c)) && + (!keyword.equals("none") && !c.getKeyword().contains(keyword)) && + (! (! c.hasSickness()) && keyword.equals("Haste")); + } + return false; + } + }); + list.remove(card); + return list; + }//getCreature() + + public void resolve() + { + if(AllZone.GameAction.isCardInPlay(getTargetCard()) && CardFactoryUtil.canTarget(card, getTargetCard())) + { + final Card[] creature = new Card[1]; + if (Tgt[0] == true) + creature[0] = getTargetCard(); + else + creature[0] = card; + + final int a = getNumAttack(); + final int d = getNumDefense(); + + final Command EOT = new Command() + { + private static final long serialVersionUID = 2134353417588894452L; + + public void execute() + { + if(AllZone.GameAction.isCardInPlay(creature[0])) + { + creature[0].addTempAttackBoost(-1 * a); + creature[0].addTempDefenseBoost(-1 * d); + if (! Keyword[0].equals("none")) + creature[0].removeExtrinsicKeyword(Keyword[0]); + } + } + }; + + creature[0].addTempAttackBoost(a); + creature[0].addTempDefenseBoost(d); + if (! Keyword[0].equals("none")) + creature[0].addExtrinsicKeyword(Keyword[0]); + + AllZone.EndOfTurn.addUntil(EOT); + + if (! DrawBack[0].equals("none")) + CardFactoryUtil.doDrawBack(DrawBack[0], 0, card.getController(), AllZone.GameAction.getOpponent(card.getController()), null, card, creature[0]); + }//if (card is in play) + }//resolve() + };//SpellAbility + + ability.setDescription(spDesc[0]); + ability.setStackDescription(stDesc[0]); + + if (Tgt[0] == true) + ability.setBeforePayMana(CardFactoryUtil.input_targetCreature(ability)); + else + ability.setTargetCard(card); + card.addSpellAbility(ability); + } + if (! tapOnlyCost && tapCost) + { + final SpellAbility ability = new Ability_Tap(card, manaCost) + { + private static final long serialVersionUID = 7593387152288440603L; + + private int defense; + private String keyword; + + private int getNumAttack() + { + if (NumAttack[0] != -1138) + return NumAttack[0]; + + if (! AttackX[0].equals("none")) + return CardFactoryUtil.xCount(card, AttackX[0]); + + return 0; + } + private int getNumDefense() + { + if (NumDefense[0] != -1138) + return NumDefense[0]; + + if (! DefenseX[0].equals("none")) + return CardFactoryUtil.xCount(card, DefenseX[0]); + + return 0; + } + + public boolean canPlayAI() + { + defense = getNumDefense(); + keyword = Keyword[0]; + if(CardFactoryUtil.AI_doesCreatureAttack(card)) + return false; + + if (AllZone.Phase.getPhase().equals(Constant.Phase.Main2)) + return false; + + CardList list = getCreatures(); + if (!list.isEmpty()) + { + boolean goodt = false; + Card t = new Card(); + while (goodt == false && !list.isEmpty()) + { + t = CardFactoryUtil.AI_getBestCreature(list); + if ((t.getNetDefense() + defense) > 0) + goodt = true; + else + list.remove(t); + } + if (goodt == true) + { + setTargetCard(t); + return true; + } + } + + return false; + } + public boolean canPlay() + { + boolean sick = true; + + if (!card.hasSickness() || !card.isCreature()) + sick = false; + + if (card.isUntapped() && AllZone.GameAction.isCardInPlay(card) && + !sick && !card.isFaceDown()) + return true; + else + return false; + } + CardList getCreatures() + { + CardList list = new CardList(AllZone.Computer_Play.getCards()); + list = list.filter(new CardListFilter() + { + public boolean addCard(Card c) + { + if (c.hasSickness() && keyword.equals("Hsste")) + return CardFactoryUtil.canTarget(card, c); + + return (CardFactoryUtil.AI_doesCreatureAttack(c)) && + (CardFactoryUtil.canTarget(card, c)) && + (!keyword.equals("none") && !c.getKeyword().contains(keyword)) && + (! (! c.hasSickness()) && keyword.equals("Haste")); + } + }); + list.remove(card); + return list; + }//getCreature() + public void resolve() + { + if(AllZone.GameAction.isCardInPlay(getTargetCard()) && CardFactoryUtil.canTarget(card ,getTargetCard())) + { + final Card[] creature = new Card[1]; + if (Tgt[0] == true) + creature[0] = getTargetCard(); + else + creature[0] = card; + + final int a = getNumAttack(); + final int d = getNumDefense(); + + final Command EOT = new Command() + { + private static final long serialVersionUID = 3532917180149273560L; + + public void execute() + { + if(AllZone.GameAction.isCardInPlay(creature[0])) + { + creature[0].addTempAttackBoost(-1 * a); + creature[0].addTempDefenseBoost(-1 * d); + if (! Keyword[0].equals("none")) + creature[0].removeExtrinsicKeyword(Keyword[0]); + } + } + }; + + creature[0].addTempAttackBoost(a); + creature[0].addTempDefenseBoost(d); + if (! Keyword[0].equals("none")) + creature[0].addExtrinsicKeyword(Keyword[0]); + AllZone.EndOfTurn.addUntil(EOT); + + if (! DrawBack[0].equals("none")) + CardFactoryUtil.doDrawBack(DrawBack[0], 0, card.getController(), AllZone.GameAction.getOpponent(card.getController()), null, card, creature[0]); + }//if (card is in play) + }//resolve() + };//SpellAbility + + ability.setDescription(spDesc[0]); + ability.setStackDescription(stDesc[0]); + + if (Tgt[0] == true) + ability.setBeforePayMana(CardFactoryUtil.input_targetCreature(ability)); + else + ability.setTargetCard(card); + card.addSpellAbility(ability); + } + } + }//while if (hasKeyword(card, "Untap") != -1) diff --git a/src/forge/CardFactoryUtil.java b/src/forge/CardFactoryUtil.java index 748eec95f2a..f68eb122e0c 100644 --- a/src/forge/CardFactoryUtil.java +++ b/src/forge/CardFactoryUtil.java @@ -2453,7 +2453,14 @@ public class CardFactoryUtil else return doXMath(Integer.parseInt(sq[2]), m); // not Hellbent - + // Count$CardPower + if (sq[0].contains("CardPower")) + return doXMath(c.getNetAttack(), m); + // Count$CardToughness + if (sq[0].contains("CardToughness")) + return doXMath(c.getNetDefense(), m); + + //Generic Zone-based counting // Count$QualityAndZones.Subquality @@ -2725,6 +2732,9 @@ public class CardFactoryUtil if (d[0].contains("Draw")) for (int i=0; i < X; i++) AllZone.GameAction.drawCard(dbPlayer); + + if (d[0].contains("UntapTgt")) + TgtC.untap(); if (d[0].contains("GenToken")) // placeholder for effect X = X + 0; diff --git a/src/forge/CardFactory_Creatures.java b/src/forge/CardFactory_Creatures.java index c3132dcf183..42769fb4e7e 100644 --- a/src/forge/CardFactory_Creatures.java +++ b/src/forge/CardFactory_Creatures.java @@ -7288,7 +7288,8 @@ public class CardFactory_Creatures { }//*************** END ************ END ************************** - + /* + //*************** START *********** START ************************** else if(cardName.equals("Timberwatch Elf")) { @@ -7368,9 +7369,7 @@ public class CardFactory_Creatures { ability.setBeforePayMana(CardFactoryUtil.input_targetCreature_NoCost_TapAbility(ability)); }//*************** END ************ END ************************** - - - + */ //*************** START *********** START ************************** else if(cardName.equals("Mad Auntie")) @@ -18318,7 +18317,7 @@ public class CardFactory_Creatures { //*************** START *********** START ************************** else if(cardName.equals("Cathartic Adept")) { - final SpellAbility a1 = new Ability_Tap(card, "0") + final Ability_Tap a1 = new Ability_Tap(card) { private static final long serialVersionUID = 2359247592519063187L; @@ -18342,8 +18341,6 @@ public class CardFactory_Creatures { } } - - public boolean canPlayAI() { String player = getTargetPlayer(); @@ -18352,11 +18349,23 @@ public class CardFactory_Creatures { return libList.size() > 0; } };//SpellAbility - card.addSpellAbility(a1); + + //not sure why, but this card doesn't seem to want to tap: + final Command tap = new Command() + { + private static final long serialVersionUID = -6290276896549170403L; + + public void execute() + { + card.tap(); + } + }; + a1.setDescription("tap: Target player puts the top card of his or her library into his or her graveyard."); a1.setStackDescription("Player puts the top card of his or her library into his or her graveyard"); - a1.setBeforePayMana(new Input_PayManaCost(a1)); - a1.setBeforePayMana(CardFactoryUtil.input_targetPlayer(a1)); + //a1.setBeforePayMana(new Input_PayManaCost(a1)); + a1.setBeforePayMana(CardFactoryUtil.input_targetPlayer(a1, tap)); + card.addSpellAbility(a1); }//*************** END ************ END ************************** diff --git a/src/forge/CardFactory_Lands.java b/src/forge/CardFactory_Lands.java index 4d20814479f..353a86935c7 100644 --- a/src/forge/CardFactory_Lands.java +++ b/src/forge/CardFactory_Lands.java @@ -704,7 +704,7 @@ class CardFactory_Lands { PlayerZone library = AllZone.getZone(Constant.Zone.Library, card.getController()); CardList list = new CardList(library.getCards()); list = list.getType("Basic"); - if (list.size() > 0 && AllZone.GameAction.isCardInPlay(card)) + if (super.canPlay() && list.size() > 0 && AllZone.GameAction.isCardInPlay(card)) return true; else return false; @@ -796,6 +796,126 @@ class CardFactory_Lands { ability.setDescription("tap, Sacrifice Terramorphic Expanse: Search your library for a basic land card and put it into play tapped. Then shuffle your library."); ability.setBeforePayMana(runtime); }//*************** END ************ END ************************** + + + //*************** START *********** START ************************** + else if(cardName.equals("Arid Mesa") || cardName.equals("Marsh Flats") || cardName.equals("Misty Rainforest") || + cardName.equals("Scalding Tarn") || cardName.equals("Verdant Catacombs") || + cardName.equals("Bloodstained Mire") || cardName.equals("Flooded Strand") || cardName.equals("Polluted Delta") || + cardName.equals("Windswept Heath") || cardName.equals("Wooded Foothills")) + { + + final String[] land1 = new String[1]; + final String[] land2 = new String[1]; + + if (cardName.equals("Arid Mesa")) { land1[0] = "Mountain"; land2[0] = "Plains";} + else if (cardName.equals("Marsh Flats")) { land1[0] = "Plains"; land2[0] = "Swamp";} + else if (cardName.equals("Misty Rainforest")) { land1[0] = "Forest"; land2[0] = "Island";} + else if (cardName.equals("Scalding Tarn")) { land1[0] = "Island"; land2[0] = "Mountain";} + else if (cardName.equals("Verdant Catacombs")) { land1[0] = "Swamp"; land2[0] = "Forest";} + else if (cardName.equals("Bloodstained Mire")) { land1[0] = "Swamp"; land2[0] = "Mountain";} + else if (cardName.equals("Flooded Strand")) { land1[0] = "Plains"; land2[0] = "Island";} + else if (cardName.equals("Polluted Delta")) { land1[0] = "Island"; land2[0] = "Swamp";} + else if (cardName.equals("Windswept Heath")) { land1[0] = "Forest"; land2[0] = "Plains";} + else if (cardName.equals("Wooded Foothills")) { land1[0] = "Mountain"; land2[0] = "Forest";} + + //tap sacrifice + final Ability_Tap ability = new Ability_Tap(card, "0") + { + + private static final long serialVersionUID = 6865042319287843154L; + + public boolean canPlayAI() + { + return false; + } + public void chooseTargetAI() + { + AllZone.GameAction.sacrifice(card); + } + public boolean canPlay() + { + PlayerZone library = AllZone.getZone(Constant.Zone.Library, card.getController()); + CardList list = new CardList(library.getCards()); + list = list.filter(new CardListFilter() + { + public boolean addCard(Card c) + { + return c.getType().contains(land1[0]) || c.getType().contains(land2[0]); + } + }); + if (super.canPlay() && list.size() > 0 && AllZone.GameAction.isCardInPlay(card)) + return true; + else + return false; + + }//canPlay() + public void resolve() + { + if(card.getOwner().equals(Constant.Player.Human)) + humanResolve(); + //else + // computerResolve(); + } + + public void humanResolve() + { + PlayerZone library = AllZone.getZone(Constant.Zone.Library, card.getController()); + PlayerZone play = AllZone.getZone(Constant.Zone.Play , card.getController()); + + CardList full = new CardList(library.getCards()); + CardList land = new CardList(library.getCards()); + land = land.filter(new CardListFilter() + { + public boolean addCard(Card c) + { + return c.getType().contains(land1[0]) || c.getType().contains(land2[0]); + } + }); + + Object o = AllZone.Display.getChoiceOptional("Choose a " +land1[0] + " or " + land2[0], full.toArray()); + if(o != null) + { + + Card c = (Card)o; + if (land.contains(c)) + { + library.remove(c); + play.add(c); + } + } + AllZone.GameAction.shuffle(card.getController()); + }//resolve() + };//SpellAbility + + Input runtime = new Input() + { + + private static final long serialVersionUID = -7328086812286814833L; + boolean once = true; + public void showMessage() + { + //this is necessary in order not to have a StackOverflowException + //because this updates a card, it creates a circular loop of observers + if(once) + { + once = false; + String player = card.getController(); + AllZone.GameAction.getPlayerLife(player).subtractLife(1); + AllZone.GameAction.sacrifice(card); + + ability.setStackDescription(card.getController() +" - Search your library for a "+land1[0]+" or "+land2[0]+" card and put it onto the battlefield. Then shuffle your library."); + AllZone.Stack.add(ability); + + stop(); + } + }//showMessage() + }; + card.addSpellAbility(ability); + ability.setDescription("Tap, Pay 1 life, Sacrifice " + card.getName() +": Search your library for a "+land1[0]+" or "+land2[0]+" card and put it onto the battlefield. Then shuffle your library."); + ability.setBeforePayMana(runtime); + }//*************** END ************ END ************************** + //*************** START *********** START ************************** else if(cardName.equals("Tortuga")) @@ -2438,6 +2558,7 @@ class CardFactory_Lands { }//*************** END ************ END ************************** + /* //*************** START *********** START ************************** else if(cardName.equals("Skarrg, the Rage Pits")) { @@ -2537,6 +2658,8 @@ class CardFactory_Lands { }//*************** END ************ END ************************** + */ + //*************** START *********** START ************************** else if(cardName.equals("Daru Encampment")) { diff --git a/src/forge/CombatUtil.java b/src/forge/CombatUtil.java index d54bb842576..1cc1e685951 100644 --- a/src/forge/CombatUtil.java +++ b/src/forge/CombatUtil.java @@ -1804,7 +1804,7 @@ public class CombatUtil AllZone.EndOfCombat.addAt(atEOC); } - else if (b.getName().equals("AEther Membrane") ) + else if (b.getName().equals("AEther Membrane") || b.getName().equals("Aether Membrane") ) { final Card attacker = a; final Ability ability = new Ability(b, "0") diff --git a/src/forge/ReadCard.java b/src/forge/ReadCard.java index 89cc2dd79da..e86e258390d 100644 --- a/src/forge/ReadCard.java +++ b/src/forge/ReadCard.java @@ -87,6 +87,7 @@ public class ReadCard implements Runnable, NewConstants { s = readLine(); if(c.isCreature()) { + //System.out.println("Creature name:" + c.getName()); int n = s.indexOf("/"); int att = Integer.parseInt(s.substring(0, n)); int def = Integer.parseInt(s.substring(n + 1));