diff --git a/res/card-pictures.txt b/res/card-pictures.txt index 3ecd9486eaf..20074891ea0 100644 --- a/res/card-pictures.txt +++ b/res/card-pictures.txt @@ -3832,3 +3832,26 @@ skull_catapult.jpg http://www.wizards.com/global/images/magic/general/skull_cat fodder_cannon.jpg http://www.wizards.com/global/images/magic/general/fodder_cannon.jpg krovikan_horror.jpg http://www.wizards.com/global/images/magic/general/krovikan_horror.jpg goblin_bombardment.jpg http://www.wizards.com/global/images/magic/general/goblin_bombardment.jpg +vitaspore_thallid.jpg http://www.wizards.com/global/images/magic/general/vitaspore_thallid.jpg +thallid_germinator.jpg http://www.wizards.com/global/images/magic/general/thallid_germinator.jpg +thallid_devourer.jpg http://www.wizards.com/global/images/magic/general/thallid_devourer.jpg +deathspore_thallid.jpg http://www.wizards.com/global/images/magic/general/deathspore_thallid.jpg +plagued_rusalka.jpg http://www.wizards.com/global/images/magic/general/plagued_rusalka.jpg +marsh_lurker.jpg http://www.wizards.com/global/images/magic/general/marsh_lurker.jpg +gruul_guildmage.jpg http://www.wizards.com/global/images/magic/general/gruul_guildmage.jpg +need_for_speed.jpg http://www.wizards.com/global/images/magic/general/need_for_speed.jpg +goblin_sledder.jpg http://www.wizards.com/global/images/magic/general/goblin_sledder.jpg +megatog.jpg http://www.wizards.com/global/images/magic/general/megatog.jpg +thaumatog.jpg http://www.wizards.com/global/images/magic/general/thaumatog.jpg +lithatog.jpg http://www.wizards.com/global/images/magic/general/lithatog.jpg +foratog.jpg http://www.wizards.com/global/images/magic/general/foratog.jpg +auratog.jpg http://www.wizards.com/global/images/magic/general/auratog.jpg +atog.jpg http://www.wizards.com/global/images/magic/general/atog.jpg +reconstruction.jpg http://www.wizards.com/global/images/magic/general/reconstruction.jpg +ritual_of_restoration.jpg http://www.wizards.com/global/images/magic/general/ritual_of_restoration.jpg +sages_knowledge.jpg http://www.wizards.com/global/images/magic/general/sages_knowledge.jpg +altars_light.jpg http://www.wizards.com/global/images/magic/general/altars_light.jpg +caustic_rain.jpg http://www.wizards.com/global/images/magic/general/caustic_rain.jpg +erase.jpg http://www.wizards.com/global/images/magic/general/erase.jpg +ionas_judgment.jpg http://www.wizards.com/global/images/magic/general/ionas_judgment.jpg +wipe_clean.jpg http://www.wizards.com/global/images/magic/general/wipe_clean.jpg \ No newline at end of file diff --git a/res/cards.txt b/res/cards.txt index 446dfffc262..3be523c773e 100644 --- a/res/cards.txt +++ b/res/cards.txt @@ -1,3 +1,122 @@ +Vitaspore Thallid +1 G +Creature Fungus +At the beginning of your upkeep, put a spore counter on Vitaspore Thallid. +1/1 +Remove three spore counters from CARDNAME: Put a 1/1 green Saproling creature token onto the battlefield. +abPumpTgt 0 Sac-Saproling:Haste + +Thallid Germinator +2 G +Creature Fungus +At the beginning of your upkeep, put a spore counter on Thallid Germinator. +2/2 +Remove three spore counters from CARDNAME: Put a 1/1 green Saproling creature token onto the battlefield. +abPumpTgt 0 Sac-Saproling:+1/+1 + +Thallid Devourer +1 G G +Creature Fungus +At the beginning of your upkeep, put a spore counter on Thallid Devourer. +2/2 +Remove three spore counters from CARDNAME: Put a 1/1 green Saproling creature token onto the battlefield. +abPump 0 Sac-Saproling:+1/+2 + +Deathspore Thallid +1 B +Creature Zombie Fungus +At the beginning of your upkeep, put a spore counter on Deathspore Thallid. +1/1 +Remove three spore counters from CARDNAME: Put a 1/1 green Saproling creature token onto the battlefield. +abPumpTgt 0 Sac-Saproling:-1/-1 + +Plagued Rusalka +B +Creature Spirit +no text +1/1 +abPumpTgt B Sac-Creature:-1/-1 + +Marsh Lurker +3 B +Creature Beast +no text +3/2 +abPump 0 Sac-Swamp:Fear + +Gruul Guildmage +RG RG +Creature Human Shaman +no text +2/2 +abPumpTgt 3 G:+2/+2 +abDamageTgtP 3 R Sac-Land:2 + +Phyrexian Plaguelord +3 B B +Creature Carrier +no text +4/4 +abPumpTgt T Sac-CARDNAME:-4/-4 +abPumpTgt 0 Sac-Creature:-1/-1 + +Need for Speed +R +Enchantment +no text +abPumpTgt 0 Sac-Land:Haste + +Goblin Sledder +R +Creature Goblin +no text +1/1 +abPumpTgt 0 Sac-Goblin:+1/+1 + +Megatog +4 R R +Creature Atog +no text +3/4 +abPump 0 Sac-Artifact:+3/+3 + +Thaumatog +1 W G +Creature Atog +no text +1/2 +abPump 0 Sac-Enchantment:+1/+1 +abPump 0 Sac-Land:+1/+1 + +Lithatog +1 R G +Creature Atog +no text +1/2 +abPump 0 Sac-Artifact:+1/+1 +abPump 0 Sac-Land:+1/+1 + +Foratog +2 G +Creature Atog +no text +1/2 +abPump G Sac-Forest:+2/+2 + +Auratog +1 W +Creature Atog +no text +1/2 +abPump 0 Sac-Enchantment:+2/+2 + +Atog +1 R +Creature Atog +no text +1/2 +abPump 0 Sac-Artifact:+2/+2 + Déjà Vu 2 U Sorcery @@ -2676,12 +2795,6 @@ Creature Human Druid At the beginning of your upkeep, you may put a 1/1 green Squirrel creature token onto the battlefield. 1/1 -Phyrexian Plaguelord -3 B B -Creature Carrier -no text -4/4 - Reclaim G Instant diff --git a/res/common.txt b/res/common.txt index 0eb9f2688b6..3f3f038d1c7 100644 --- a/res/common.txt +++ b/res/common.txt @@ -1720,3 +1720,17 @@ Glassdust Hulk Deepwood Wolverine Rabid Wolverines Dwarven Berserker +Vitaspore Thallid +Thallid Germinator +Thallid Devourer +Deathspore Thallid +Marsh Lurker +Goblin Sledder +Atog +Déjà Vu +Reconstruction +Ritual of Restoration +Sage's Knowledge +Erase +Iona's Judgment +Wipe Clean diff --git a/res/quest/common.txt b/res/quest/common.txt index 0eb9f2688b6..3f3f038d1c7 100644 --- a/res/quest/common.txt +++ b/res/quest/common.txt @@ -1720,3 +1720,17 @@ Glassdust Hulk Deepwood Wolverine Rabid Wolverines Dwarven Berserker +Vitaspore Thallid +Thallid Germinator +Thallid Devourer +Deathspore Thallid +Marsh Lurker +Goblin Sledder +Atog +Déjà Vu +Reconstruction +Ritual of Restoration +Sage's Knowledge +Erase +Iona's Judgment +Wipe Clean diff --git a/res/quest/rare.txt b/res/quest/rare.txt index 21a92446756..556539aa408 100644 --- a/res/quest/rare.txt +++ b/res/quest/rare.txt @@ -1001,3 +1001,6 @@ Archon of Redemption Terra Eternal Scion of the Wild Battle Squadron +Need for Speed +Megatog +Auratog diff --git a/res/quest/uncommon.txt b/res/quest/uncommon.txt index 78aca2272ee..327ded62a22 100644 --- a/res/quest/uncommon.txt +++ b/res/quest/uncommon.txt @@ -1061,3 +1061,10 @@ Harbor Guardian Baloth Woodcrasher Citanul Druid Goblin Bombardment +Plagued Rusalka +Gruul Guildmage +Thaumatog +Lithatog +Foratog +Altar's Light +Caustic Rain diff --git a/res/rare.txt b/res/rare.txt index 21a92446756..556539aa408 100644 --- a/res/rare.txt +++ b/res/rare.txt @@ -1001,3 +1001,6 @@ Archon of Redemption Terra Eternal Scion of the Wild Battle Squadron +Need for Speed +Megatog +Auratog diff --git a/res/uncommon.txt b/res/uncommon.txt index 78aca2272ee..327ded62a22 100644 --- a/res/uncommon.txt +++ b/res/uncommon.txt @@ -1061,3 +1061,10 @@ Harbor Guardian Baloth Woodcrasher Citanul Druid Goblin Bombardment +Plagued Rusalka +Gruul Guildmage +Thaumatog +Lithatog +Foratog +Altar's Light +Caustic Rain diff --git a/src/forge/CardFactory.java b/src/forge/CardFactory.java index f542af338eb..87d97da2497 100644 --- a/src/forge/CardFactory.java +++ b/src/forge/CardFactory.java @@ -951,14 +951,28 @@ public class CardFactory implements NewConstants { if(Tgt[0]) tmpCost = k[0].substring(9); else tmpCost = k[0].substring(6); + final boolean sacCost[] = {false}; + boolean sacFirstCost = false; + final String sacType[] = {""}; + boolean sacThis = false; + + if(tmpCost.contains("Sac-")) { + sacCost[0] = true; + int sacPos = tmpCost.indexOf("Sac-"); + sacType[0] = tmpCost.substring(sacPos).replace("Sac-", "").trim(); + sacThis = (sacType[0].equals("CARDNAME")); + tmpCost = tmpCost.substring(0,sacPos-1).trim(); + sacFirstCost = (tmpCost.length() == 0); + } + boolean tapCost = false; - boolean tapOnlyCost = false; + boolean tapFirstCost = false; if(tmpCost.contains("T")) { tapCost = true; tmpCost = tmpCost.replace("T", ""); tmpCost = tmpCost.trim(); - if(tmpCost.length() == 0) tapOnlyCost = true; + tapFirstCost = tmpCost.length() == 0; } final String manaCost = tmpCost.trim(); @@ -1097,10 +1111,28 @@ public class CardFactory implements NewConstants { } 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; + StringBuilder abCost = new StringBuilder(); + abCost.append(manaCost); + if (tapCost){ + if (tapFirstCost) + abCost.append("T"); + else + abCost.append(", t"); + abCost.append("ap"); + } + if (sacCost[0]){ + if (sacFirstCost) + abCost.append("S"); + else + abCost.append(", s"); + abCost.append("acrifice "); + if (!sacThis) + abCost.append("a "); + abCost.append(sacType[0]); + } + abCost.append(": "); + spDesc[0] = abCost + d; stDesc[0] = d; } @@ -1129,6 +1161,8 @@ public class CardFactory implements NewConstants { @Override public boolean canPlayAI() { + if (sacCost[0]) return false; + defense = getNumDefense(); keyword = Keyword[0]; @@ -1192,7 +1226,8 @@ public class CardFactory implements NewConstants { return false; } }); - // list.remove(card); // if mana-only cost, allow self-target + if (sacCost[0] && sacType[0].equals("CARDNAME")) // if sacrifice , don't self-target + list.remove(card); return list; }//getCreatures() @@ -1251,6 +1286,12 @@ public class CardFactory implements NewConstants { if(Tgt[0] == true) ability.setBeforePayMana(CardFactoryUtil.input_targetCreature(ability)); else ability.setTargetCard(card); + if(sacCost[0]){ + if (sacType[0].equals("CARDNAME")) + ability.setAfterPayMana(CardFactoryUtil.input_sacrificeThis(ability)); + else + ability.setAfterPayMana(CardFactoryUtil.input_sacrificeType(ability, sacType[0], "Sacrifice a "+sacType[0])); + } card.addSpellAbility(ability); } if(tapCost) { @@ -1278,6 +1319,7 @@ public class CardFactory implements NewConstants { @Override public boolean canPlayAI() { + if (sacCost[0]) return false; defense = getNumDefense(); keyword = Keyword[0]; @@ -1386,8 +1428,14 @@ public class CardFactory implements NewConstants { if(Tgt[0] == true) ability.setBeforePayMana(CardFactoryUtil.input_targetCreature(ability)); else ability.setTargetCard(card); + if(sacCost[0]){ + if (sacType[0].equals("CARDNAME") || sacType[0].equals(card.getName())) + ability.setAfterPayMana(CardFactoryUtil.input_sacrificeThis(ability)); + else + ability.setAfterPayMana(CardFactoryUtil.input_sacrificeType(ability, sacType[0], "Sacrifice a "+sacType[0])); + } - if(!tapOnlyCost) ability.setManaCost(manaCost); + if(!tapFirstCost && !sacFirstCost) ability.setManaCost(manaCost); card.addSpellAbility(ability); } diff --git a/src/forge/CardFactoryUtil.java b/src/forge/CardFactoryUtil.java index 8c8eb6169f6..f649b83ec61 100644 --- a/src/forge/CardFactoryUtil.java +++ b/src/forge/CardFactoryUtil.java @@ -518,6 +518,36 @@ public class CardFactoryUtil { return target; }//input_sacrifice() + public static Input input_sacrificeThis(final SpellAbility spell) { + Input target = new Input() { + private static final long serialVersionUID = 2685832214519141903L; + + @Override + public void showMessage() { + Card card = spell.getSourceCard(); + String[] choices = {"Yes", "No"}; + if(card.getController().equals(Constant.Player.Human)) { + Object o = AllZone.Display.getChoice("Sacrifice " + card.getName() + " ?", choices); + if(o.equals("Yes")) { + sacrifice(card); + } + else{ + //undo? + stop(); + } + } + } + + public void sacrifice(Card card) + { + AllZone.GameAction.sacrifice(card); + stop(); + AllZone.Stack.add(spell); + } + }; + return target; + }//input_sacrifice() + public static Input input_sacrificeType(final SpellAbility spell, final String type, final String message) { // This input should be setAfterManaPaid so it can add the spell to the stack Input target = new Input() { @@ -2582,13 +2612,18 @@ public class CardFactoryUtil { } void done() { - if(spell instanceof Ability_Tap && spell.getManaCost().equals("0")) stopSetNext(new Input_NoCost_TapAbility( - (Ability_Tap) spell)); + if(spell instanceof Ability_Tap && spell.getManaCost().equals("0")) + stopSetNext(new Input_NoCost_TapAbility((Ability_Tap) spell)); else if(spell.getManaCost().equals("0") || this.isFree())//for "sacrifice this card" abilities { - this.setFree(false); - AllZone.Stack.add(spell, spell.getSourceCard().getManaCost().contains("X")); - stop(); + if (spell.getAfterPayMana() == null){ + this.setFree(false); + AllZone.Stack.add(spell, spell.getSourceCard().getManaCost().contains("X")); + stop(); + } + else{ + stopSetNext(spell.getAfterPayMana()); + } } else stopSetNext(new Input_PayManaCost(spell)); paid.execute(); diff --git a/src/forge/CardFactory_Creatures.java b/src/forge/CardFactory_Creatures.java index 6e3f34242d6..b1c1844bd2f 100644 --- a/src/forge/CardFactory_Creatures.java +++ b/src/forge/CardFactory_Creatures.java @@ -2213,150 +2213,6 @@ public class CardFactory_Creatures { card.addComesIntoPlayCommand(intoPlay); }//*************** END ************ END ************************** - //*************** START *********** START ************************** - else if(cardName.equals("Phyrexian Plaguelord")) { - final SpellAbility negMajorBoost = new Ability_Tap(card, "0") { - private static final long serialVersionUID = -7797077528670642603L; - - @Override - public void resolve() { - final Card[] target = new Card[1]; - final Command untilEOT = new Command() { - private static final long serialVersionUID = 723868397363666853L; - - public void execute() { - if(AllZone.GameAction.isCardInPlay(target[0])) { - target[0].addTempAttackBoost(4); - target[0].addTempDefenseBoost(4); - } - } - }; - - target[0] = getTargetCard(); - if(AllZone.GameAction.isCardInPlay(target[0]) && CardFactoryUtil.canTarget(card, target[0])) { - target[0].addTempAttackBoost(-4); - target[0].addTempDefenseBoost(-4); - - AllZone.EndOfTurn.addUntil(untilEOT); - } - } - - @Override - public boolean canPlayAI() { - return false; // todo: don't want the AI just sacrificing creatures without a strong plan in place - }//canPlayAI() - }; - Input majorTarget = new Input() { - private static final long serialVersionUID = 607823017778070255L; - - @Override - public void showMessage() { - AllZone.Display.showMessage("Select target creature for " + card.getName()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - stop(); - } - - @Override - public void selectCard(Card target, PlayerZone zone) { - // Choose a legal target, then sacrifice a creature - if (card.isUntapped()){ - if(!CardFactoryUtil.canTarget(negMajorBoost, target)) { - AllZone.Display.showMessage("Cannot target this card (Shroud? Protection?)."); - } - else if(target.isCreature() && zone.is(Constant.Zone.Play)) { - card.tap(); - AllZone.GameAction.sacrifice(card); - negMajorBoost.setTargetCard(target); - AllZone.Stack.add(negMajorBoost); - } - } - stop(); - } - };//Input - negMajorBoost.setDescription("T, Sacrifice CARDNAME: Target creature gets -4/-4 until end of turn"); - negMajorBoost.setBeforePayMana(majorTarget); - - final SpellAbility negMinorBoost = new Ability(card, "0") { - @Override - public void resolve() { - final Card[] target = new Card[1]; - final Command untilEOT = new Command() { - private static final long serialVersionUID = 723868397363666853L; - - public void execute() { - if(AllZone.GameAction.isCardInPlay(target[0])) { - target[0].addTempAttackBoost(1); - target[0].addTempDefenseBoost(1); - } - } - }; - - target[0] = getTargetCard(); - if(AllZone.GameAction.isCardInPlay(target[0]) && CardFactoryUtil.canTarget(card, target[0])) { - target[0].addTempAttackBoost(-1); - target[0].addTempDefenseBoost(-1); - - AllZone.EndOfTurn.addUntil(untilEOT); - } - } - - @Override - public boolean canPlayAI() { - return false; // todo: don't want the AI just sacrificing creatures without a strong plan in place - }//canPlayAI() - }; - Input target = new Input() { - private static final long serialVersionUID = -5404464532726469761L; - - @Override - public void showMessage() { - AllZone.Display.showMessage("Select target creature for " + card.getName()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - stop(); - } - - @Override - public void selectCard(Card target, PlayerZone zone) { - // Choose a legal target, then sacrifice a creature - if(!CardFactoryUtil.canTarget(negMinorBoost, target)) { - AllZone.Display.showMessage("Cannot target this card (Shroud? Protection?)."); - } else if(target.isCreature() && zone.is(Constant.Zone.Play)) { - - PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController()); - CardList creatures = new CardList(play.getCards()); - creatures = creatures.getType("Creature"); - - if (creatures.size() == 0) stop(); - - Object choice = AllZone.Display.getChoiceOptional("Select Creature to Sacrifice", creatures.toArray()); - - if (choice == null) stop(); - Card sac = (Card) choice; - if(AllZone.GameAction.isCardInPlay(sac)) { - AllZone.GameAction.sacrifice(sac); - negMinorBoost.setTargetCard(target); - AllZone.Stack.add(negMinorBoost); - } - - stop(); - } - } - };//Input - negMinorBoost.setDescription("Sacrifice a creature: Target creature gets -1/-1 until end of turn"); - negMinorBoost.setBeforePayMana(target); - card.addSpellAbility(negMajorBoost); - card.addSpellAbility(negMinorBoost); - - } //*************** END ************ END ************************** - //*************** START *********** START ************************** else if(cardName.equals("Cao Ren, Wei Commander")) { final SpellAbility ability = new Ability(card, "0") { diff --git a/src/forge/GameAction.java b/src/forge/GameAction.java index 5aa2999129a..fcdb0f38b57 100644 --- a/src/forge/GameAction.java +++ b/src/forge/GameAction.java @@ -3325,14 +3325,18 @@ public class GameAction { manaCost = GetSpellCostChange(sa); } if(manaCost.isPaid() && sa.getBeforePayMana() == null) { - CardList HHandList = new CardList(AllZone.getZone(Constant.Zone.Hand, Constant.Player.Human).getCards()); - if(HHandList.contains(sa.getSourceCard())) AllZone.Human_Hand.remove(sa.getSourceCard()); - AllZone.Stack.add(sa); - if(sa.isTapAbility() && !sa.wasCancelled()) sa.getSourceCard().tap(); - if(sa.isUntapAbility()) sa.getSourceCard().untap(); - return; + if (sa.getAfterPayMana() == null){ + CardList HHandList = new CardList(AllZone.getZone(Constant.Zone.Hand, Constant.Player.Human).getCards()); + if(HHandList.contains(sa.getSourceCard())) AllZone.Human_Hand.remove(sa.getSourceCard()); + AllZone.Stack.add(sa); + if(sa.isTapAbility() && !sa.wasCancelled()) sa.getSourceCard().tap(); + if(sa.isUntapAbility()) sa.getSourceCard().untap(); + return; + } + else + AllZone.InputControl.setInput(sa.getAfterPayMana()); } - if(sa.getBeforePayMana() == null) AllZone.InputControl.setInput(new Input_PayManaCost(sa)); + else if(sa.getBeforePayMana() == null) AllZone.InputControl.setInput(new Input_PayManaCost(sa)); else AllZone.InputControl.setInput(sa.getBeforePayMana()); } diff --git a/src/forge/Input_NoCost_TapAbility.java b/src/forge/Input_NoCost_TapAbility.java index f4f4e7cf0b9..f663691e3d7 100644 --- a/src/forge/Input_NoCost_TapAbility.java +++ b/src/forge/Input_NoCost_TapAbility.java @@ -16,8 +16,13 @@ public class Input_NoCost_TapAbility extends Input { //prevents this from running multiple times, which it is for some reason if(ability.getSourceCard().isUntapped()) { ability.getSourceCard().tap(); - AllZone.Stack.add(ability); - stopSetNext(new ComputerAI_StackNotEmpty()); + if (ability.getAfterPayMana() == null) + { + AllZone.Stack.add(ability); + stopSetNext(new ComputerAI_StackNotEmpty()); + } + else + stopSetNext(ability.getAfterPayMana()); } } }