diff --git a/res/card-pictures.txt b/res/card-pictures.txt index 5431dcd4787..1b8ad015934 100644 --- a/res/card-pictures.txt +++ b/res/card-pictures.txt @@ -38,6 +38,13 @@ snow_covered_mountain.jpg http://www.wizards.com/global/images/magic/gene snow_covered_mountain1.jpg http://www.wizards.com/global/images/magic/general/snow_covered_mountain.jpg snow_covered_mountain2.jpg http://www.magickartenmarkt.de/img/cards/Ice_Age/snow_covered_mountain.jpg snow_covered_mountain3.jpg http://www.magickartenmarkt.de/img/cards/Ice_Age/snow_covered_mountain.jpg +ancestral_vision.jpg http://www.wizards.com/global/images/magic/general/ancestral_vision.jpg +duskrider_peregrine.jpg http://www.wizards.com/global/images/magic/general/duskrider_peregrine.jpg +errant_ephemeron.jpg http://www.wizards.com/global/images/magic/general/errant_ephemeron.jpg +keldon_halberdier.jpg http://www.wizards.com/global/images/magic/general/keldon_halberdier.jpg +durkwood_baloth.jpg http://www.wizards.com/global/images/magic/general/durkwood_baloth.jpg +lotus_bloom.jpg http://www.wizards.com/global/images/magic/general/lotus_bloom.jpg +corpulent_corpse.jpg http://www.wizards.com/global/images/magic/general/corpulent_corpse.jpg bogardan_hellkite.jpg http://www.wizards.com/global/images/magic/general/bogardan_hellkite.jpg sparkmage_apprentice.jpg http://www.wizards.com/global/images/magic/general/sparkmage_apprentice.jpg akoum_boulderfoot.jpg http://www.wizards.com/global/images/magic/general/akoum_boulderfoot.jpg diff --git a/res/cards.txt b/res/cards.txt index 1d814adcdfb..f45e5696521 100644 --- a/res/cards.txt +++ b/res/cards.txt @@ -1,3 +1,94 @@ +Duskrider Peregrine +5 W +Creature - Bird +no text +3/3 +Flying +Protection from black +Suspend:3:1 W + +Ancestral Vision +no cost +Sorcery +(Note: Ancestral Vision currently draws 0 cards due to the combined upkeep/draw phases. Once they are seperated, this should work as expected.) +Suspend:4:U +spDrawCardsTgt:3:Target player draws three cards.:CARDNAME - draw 3 cards. + +Errant Ephemeron +6 U +Creature - Illusion +no text +4/4 +Flying +Suspend:4:1 U + +Keldon Halberdier +4 R +Creature - Human Warrior +no text +4/1 +First strike +Suspend:4:R + +Durkwood Baloth +4 G G +Creature - Beast +no text +5/5 +Suspend:5:G + +Lotus Bloom +no cost +Artifact +no text +Suspend:3:0 +tap, Sacrifice CARDNAME: Add W W W to your mana pool. +tap, Sacrifice CARDNAME: Add U U U to your mana pool. +tap, Sacrifice CARDNAME: Add B B B to your mana pool. +tap, Sacrifice CARDNAME: Add R R R to your mana pool. +tap, Sacrifice CARDNAME: Add G G G to your mana pool. + +Corpulent Corpse +5 B +Creature Zombie +no text +3/3 +Fear +Suspend:5:B + +Shade of Trokair +3 W +Creature Shade +no text +1/2 +Suspend:3:W +abPump W:+1/+1 + +Giant Dustwasp +3 G G +Creature Insect +No Text +3/3 +Flying +Suspend:4:1 G + +Pardic Dragon +4 R R +Creature Dragon +(Note: Opponent adding time counters when playing spell not implemented. Might be able to do with Whenever keyword) +4/4 +Flying +Suspend:2:R R +abPump R:+1/+0 + +Viscerid Deepwalker +4 U +Creature Homarid Warrior +no text +2/3 +Suspend:4:U +abPump U:+1/+0 + Bogardan Hellkite 6 R R Creature Dragon @@ -19303,13 +19394,6 @@ no text Fear abPump B:+1/+1 -Shade of Trokair -3 W -Creature Shade -(NOTE: "Suspend" not implemented.) -1/2 -abPump W:+1/+1 - Looming Shade 2 B Creature Shade @@ -19405,13 +19489,6 @@ no text 3/3 abPump B:+1/+1 -Viscerid Deepwalker -4 U -Creature Homarid Warrior -(NOTE: "Suspend" not implemented.) -2/3 -abPump U:+1/+0 - Colos Yearling 2 R Creature Goat Beast @@ -19480,14 +19557,6 @@ no text Flying abPump W:+0/+1 -Pardic Dragon -4 R R -Creature Dragon -(NOTE: "Suspend" not implemented.) -4/4 -Flying -abPump R:+1/+0 - Aven Flock 4 W Creature Bird Soldier @@ -21745,13 +21814,6 @@ Haste Trample At the beginning of the end step, sacrifice CARDNAME. -Giant Dustwasp -3 G G -Creature Insect -(NOTE: "Suspend" not implemented.) -3/3 -Flying - Brute Force R Instant diff --git a/src/forge/Card.java b/src/forge/Card.java index f4b065c47d5..03d8c1f2fca 100644 --- a/src/forge/Card.java +++ b/src/forge/Card.java @@ -74,6 +74,7 @@ public class Card extends MyObservable { private boolean unearthed; private boolean madness = false; + private boolean suspendCast = false; private int exaltedMagnitude = 0; @@ -1800,6 +1801,14 @@ public class Card extends MyObservable { madnessCost = cost; } + public boolean hasSuspendCast() { + return suspendCast; + } + + public void setSuspendCast(boolean b) { + suspendCast = b; + } + public void setKicked(boolean b) { kicked = b; } diff --git a/src/forge/CardFactory.java b/src/forge/CardFactory.java index 053dc83cb54..8f6d01ab557 100644 --- a/src/forge/CardFactory.java +++ b/src/forge/CardFactory.java @@ -3433,12 +3433,26 @@ public class CardFactory implements NewConstants { //card.removeIntrinsicKeyword(parse); String k[] = parse.split(":"); - final String manacost = k[1]; card.setMadness(true); card.setMadnessCost(k[1]); } }//madness + + if(hasKeyword(card, "Suspend") != -1) { + // Suspend:: + int n = hasKeyword(card, "Suspend"); + if(n != -1) { + String parse = card.getKeyword().get(n).toString(); + card.removeIntrinsicKeyword(parse); + + String k[] = parse.split(":"); + + final int timeCounters = Integer.parseInt(k[1]); + final String cost = k[2]; + card.addSpellAbility(CardFactoryUtil.ability_suspend(card, cost, timeCounters)); + } + }//madness if(hasKeyword(card, "Devour") != -1) { int n = hasKeyword(card, "Devour"); @@ -5000,7 +5014,6 @@ public class CardFactory implements NewConstants { card.clearSpellAbility(); card.addSpellAbility(spell); }//*************** END ************ END ************************** - //*************** START *********** START ************************** else if(cardName.equals("Dragon Roost")) { diff --git a/src/forge/CardFactoryUtil.java b/src/forge/CardFactoryUtil.java index ddbb134b7c0..af05ebe04ef 100644 --- a/src/forge/CardFactoryUtil.java +++ b/src/forge/CardFactoryUtil.java @@ -12,6 +12,7 @@ import java.util.Map.Entry; import com.esotericsoftware.minlog.Log; +import forge.Constant.Zone; public class CardFactoryUtil { @@ -1553,6 +1554,37 @@ public class CardFactoryUtil { return transmute; }//ability_transmute() + public static SpellAbility ability_suspend(final Card sourceCard, final String suspendCost, final int suspendCounters) { + final SpellAbility suspend = new Ability_Hand(sourceCard, suspendCost) { + private static final long serialVersionUID = 21625903128384507L; + + @Override + public boolean canPlay(){ + // if not in hand can't suspend + if (!AllZone.GameAction.isCardInZone(sourceCard, AllZone.getZone(Zone.Hand, sourceCard.getOwner()))) + return false; + + if (sourceCard.isInstant()) + return true; + return Phase.canCastSorcery(sourceCard.getOwner()); + } + + @Override + public boolean canPlayAI() { + return false; + } + + @Override + public void resolve() { + AllZone.GameAction.removeFromGame(sourceCard); + sourceCard.addCounter(Counters.TIME, suspendCounters); + } + }; + suspend.setDescription("Suspend " +suspendCounters + ": "+ suspendCost); + suspend.setStackDescription(sourceCard + " suspending for " + suspendCounters + " turns.)"); + return suspend; + }//ability_cycle() + public static SpellAbility eqPump_Equip(final Card sourceCard, final int Power, final int Tough, final String[] extrinsicKeywords, final String Manacost) { final Ability equip = new Ability(sourceCard, Manacost) { private static final long serialVersionUID = -4960704261761785512L; diff --git a/src/forge/GameAction.java b/src/forge/GameAction.java index c9537f4cf5c..92f9edfaa71 100644 --- a/src/forge/GameAction.java +++ b/src/forge/GameAction.java @@ -2147,7 +2147,8 @@ public class GameAction { PlayerZone zone = AllZone.getZone(c); //could be hand, grave, play, ... PlayerZone removed = AllZone.getZone(Constant.Zone.Removed_From_Play, c.getOwner()); - zone.remove(c); + if (zone != null) // for suspend + zone.remove(c); if(!c.isToken()) removed.add(c); } @@ -2829,7 +2830,8 @@ public class GameAction { choices.add("Play land"); for(SpellAbility sa:abilities) { - if(sa.canPlay()) { + // for uncastables like lotus bloom, check if manaCost is blank + if(sa.canPlay() && !sa.getManaCost().equals("")) { choices.add(sa.toString()); map.put(sa.toString(), sa); } @@ -2843,6 +2845,9 @@ public class GameAction { else choice = (String) AllZone.Display.getChoiceOptional("Choose", choices.toArray()); + if (choice == null) + return; + if(choice.equals("Play land")){ playLand(c, AllZone.Human_Hand); return; diff --git a/src/forge/GameActionUtil.java b/src/forge/GameActionUtil.java index 595178cbd3c..cadd58c4606 100644 --- a/src/forge/GameActionUtil.java +++ b/src/forge/GameActionUtil.java @@ -16,6 +16,7 @@ public class GameActionUtil { upkeep_DamageUpkeepCost(); //deal damage unless upkeep cost is paid upkeep_CumulativeUpkeepCost(); //sacrifice unless cumulative upkeep cost is paid upkeep_Echo(); + upkeep_Suspend(); upkeep_TabernacleUpkeepCost(); upkeep_MagusTabernacleUpkeepCost(); // upkeep_CheckEmptyDeck_Lose(); //still a little buggy @@ -3248,6 +3249,50 @@ public class GameActionUtil { } }//echo + public static void upkeep_Suspend() { + String player = AllZone.Phase.getActivePlayer(); + + PlayerZone exile = AllZone.getZone(Constant.Zone.Removed_From_Play, player); + CardList list = new CardList(); + list.addAll(exile.getCards()); + //list = list.getType("Creature"); + list = list.filter(new CardListFilter() { + public boolean addCard(Card c) { + for(String s : c.getKeyword()){ + if (s.contains("Suspend")) + return true; + } + return false; + } + }); + + if (list.size() == 0) return; + + for(final Card c : list){ + int counters = c.getCounters(Counters.TIME); + if (counters > 0) + { + c.setCounter(Counters.TIME, counters-1); + if (counters == 1){ + c.setSuspendCast(true); + + // todo(sol): haste should wear off when player loses control. need to figure out where to add that. + Command intoPlay = new Command() { + private static final long serialVersionUID = -4514610171270596654L; + + public void execute() { + if(AllZone.GameAction.isCardInPlay(c) && c.isCreature()) + c.addExtrinsicKeyword("Haste"); + }//execute() + }; + + c.addComesIntoPlayCommand(intoPlay); + AllZone.GameAction.playCardNoCost(c); + exile.remove(c); + } + } + } + }//suspend public static void upkeep_UpkeepCost() { String player = AllZone.Phase.getActivePlayer();