diff --git a/res/cardsfolder/convolute.txt b/res/cardsfolder/convolute.txt index 1348aafd9a7..d2967499a8a 100644 --- a/res/cardsfolder/convolute.txt +++ b/res/cardsfolder/convolute.txt @@ -1,7 +1,8 @@ Name:Convolute ManaCost:2 U Types:Instant -Text:Counter target spell unless its controller pays 4. +Text:no text +A:SP$Counter | Cost$ 2 U | Type$ Spell | UnlessCost$ 4 | SpellDescription$ Counter target spell unless its controller pays 4. SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/convolute.jpg SetInfo:RAV|Common|http://magiccards.info/scans/en/rav/41.jpg diff --git a/res/cardsfolder/daze.txt b/res/cardsfolder/daze.txt index 1814019574c..725d6ffc536 100644 --- a/res/cardsfolder/daze.txt +++ b/res/cardsfolder/daze.txt @@ -2,6 +2,8 @@ Name:Daze ManaCost:1 U Types:Instant Text:no text +A:SP$Counter | Cost$ 1 U | Type$ Spell | UnlessCost$ 1 | SpellDescription$ Counter target spell unless its controller pays 1. +A:SP$Counter | Cost$ Return<1/Island> | Type$ Spell | UnlessCost$ 1 | CostDesc$ You may return an Island you control to its owner's hand | SpellDescription$ rather than pay CARDNAME's mana cost. SVar:RemAIDeck:True SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/daze.jpg diff --git a/res/cardsfolder/force_spike.txt b/res/cardsfolder/force_spike.txt index 79d78dc44d7..6e6c9d387da 100644 --- a/res/cardsfolder/force_spike.txt +++ b/res/cardsfolder/force_spike.txt @@ -1,7 +1,8 @@ Name:Force Spike ManaCost:U Types:Instant -Text:Counter target spell unless its controller pays 1. +Text:no text +A:SP$Counter | Cost$ U | Type$ Spell | UnlessCost$ 1 | SpellDescription$ Counter target spell unless its controller pays 1. SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/force_spike.jpg SetInfo:5ED|Common|http://magiccards.info/scans/en/5e/88.jpg diff --git a/res/cardsfolder/mana_leak.txt b/res/cardsfolder/mana_leak.txt index 69aaebf6675..ad7190b7865 100644 --- a/res/cardsfolder/mana_leak.txt +++ b/res/cardsfolder/mana_leak.txt @@ -1,7 +1,8 @@ Name:Mana Leak ManaCost:1 U Types:Instant -Text:Counter target spell unless its controller pays 3. +Text:no text +A:SP$Counter | Cost$ 1 U | Type$ Spell | UnlessCost$ 3 | SpellDescription$ Counter target spell unless its controller pays 3. SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/mana_leak.jpg SetInfo:M11|Common|http://magiccards.info/scans/en/m11/62.jpg diff --git a/res/cardsfolder/mana_tithe.txt b/res/cardsfolder/mana_tithe.txt index aee98b77843..ab6c5d09ed6 100644 --- a/res/cardsfolder/mana_tithe.txt +++ b/res/cardsfolder/mana_tithe.txt @@ -1,7 +1,8 @@ Name:Mana Tithe ManaCost:W Types:Instant -Text:Counter target spell unless its controller pays 1. +Text:no text +A:SP$Counter | Cost$ W | Type$ Spell | UnlessCost$ 1 | SpellDescription$ Counter target spell unless its controller pays 1. SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/mana_tithe.jpg SetInfo:PLC|Common|http://magiccards.info/scans/en/pc/25.jpg diff --git a/res/cardsfolder/miscalculation.txt b/res/cardsfolder/miscalculation.txt index d6e2e23f87c..fe4b8bfc6ce 100644 --- a/res/cardsfolder/miscalculation.txt +++ b/res/cardsfolder/miscalculation.txt @@ -1,7 +1,8 @@ Name:Miscalculation ManaCost:1 U Types:Instant -Text:Counter target spell unless its controller pays 2. +Text:no text +A:SP$Counter | Cost$ 1 U | Type$ Spell | UnlessCost$ 2 | SpellDescription$ Counter target spell unless its controller pays 2. K:Cycling:2 SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/miscalculation.jpg diff --git a/res/cardsfolder/runeboggle.txt b/res/cardsfolder/runeboggle.txt index 4fc5b83c2cf..2360759fbaa 100644 --- a/res/cardsfolder/runeboggle.txt +++ b/res/cardsfolder/runeboggle.txt @@ -1,8 +1,9 @@ Name:Runeboggle ManaCost:2 U Types:Instant -Text:Counter target spell unless its controller pays 1. -K:Draw a card. +Text:no text +A:SP$Counter | Cost$ 2 U | Type$ Spell | UnlessCost$ 1 | SubAbility$SVar=DBDraw | SpellDescription$ Counter target spell unless its controller pays 1. Draw a card +SVar:DBDraw:DB$Draw | NumCards$ 1 SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/runeboggle.jpg SetInfo:GPT|Common|http://magiccards.info/scans/en/gp/33.jpg diff --git a/res/cardsfolder/spell_pierce.txt b/res/cardsfolder/spell_pierce.txt index cbe3443970c..d28d23b0eb1 100644 --- a/res/cardsfolder/spell_pierce.txt +++ b/res/cardsfolder/spell_pierce.txt @@ -1,7 +1,8 @@ Name:Spell Pierce ManaCost:U Types:Instant -Text:Counter target noncreature spell unless its controller pays 2. +Text:no text +A:SP$Counter | Cost$ U | Type$ Spell | CounterValid$ Card.nonCreature | UnlessCost$ 2 | SpellDescription$ Counter target noncreature spell unless its controller pays 2. SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/spell_pierce.jpg SetInfo:ZEN|Common|http://magiccards.info/scans/en/zen/67.jpg diff --git a/res/cardsfolder/spell_snip.txt b/res/cardsfolder/spell_snip.txt index daa4adb303d..6eabc233869 100644 --- a/res/cardsfolder/spell_snip.txt +++ b/res/cardsfolder/spell_snip.txt @@ -1,7 +1,8 @@ Name:Spell Snip ManaCost:2 U Types:Instant -Text:Counter target spell unless its controller pays 1. +Text:no text +A:SP$Counter | Cost$ 2 U | Type$ Spell | UnlessCost$ 1 | SpellDescription$ Counter target spell unless its controller pays 1. K:Cycling:2 SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/spell_snip.jpg diff --git a/src/forge/AbilityFactory_CounterMagic.java b/src/forge/AbilityFactory_CounterMagic.java index d31b60f6c3b..ae16af31af3 100644 --- a/src/forge/AbilityFactory_CounterMagic.java +++ b/src/forge/AbilityFactory_CounterMagic.java @@ -27,6 +27,7 @@ public class AbilityFactory_CounterMagic { private String destination = null; private String[] splitTargetingRestrictions = null; private String[] splitExtraActions; + private String unlessCost = null; private final SpellAbility[] tgt = new SpellAbility[1]; @@ -45,6 +46,9 @@ public class AbilityFactory_CounterMagic { splitExtraActions = params.get("ExtraActions").split(" "); } else splitExtraActions = new String[] {"None"}; + + if(params.containsKey("UnlessCost")) + unlessCost = params.get("UnlessCost"); tgt[0] = null; } @@ -116,192 +120,45 @@ public class AbilityFactory_CounterMagic { if(matchSpellAbility(sa.getSourceCard(), tgt[0], splitTargetingRestrictions, targetType) && AllZone.Stack.contains(tgt[0]) && !tgt[0].getSourceCard().keywordsContain("CARDNAME can't be countered.")) { - SpellAbility tgtSA = tgt[0]; - AllZone.Stack.remove(tgt[0]); + final SpellAbility tgtSA = tgt[0]; System.out.println("Send countered spell to " + destination); - if(tgtSA.isAbility()) { - //For Ability-targeted counterspells - do not move it anywhere, even if Destination$ is specified. + if(unlessCost != null) { + Ability ability = new Ability(af.getHostCard(), unlessCost) { + @Override + public void resolve() { + ; + } + }; + + final Command unpaidCommand = new Command() { + private static final long serialVersionUID = 8094833091127334678L; + + public void execute() { + removeFromStack(tgtSA,sa); + } + }; + + if(AllZone.Stack.peek().getActivatingPlayer().isHuman()) { + GameActionUtil.payManaDuringAbilityResolve(af.getHostCard() + "\r\n", ability.getManaCost(), + Command.Blank, unpaidCommand); + } else { + if(ComputerUtil.canPayCost(ability)) ComputerUtil.playNoStack(ability); + else { + removeFromStack(tgtSA,sa); + } + } + doExtraActions(tgtSA,sa); } - else if(destination.equals("Graveyard")) { - AllZone.GameAction.moveToGraveyard(tgtSA.getSourceCard()); - } - else if(destination.equals("Exile")) { - AllZone.GameAction.exile(tgtSA.getSourceCard()); - } - else if(destination.equals("Topdeck")) { - AllZone.GameAction.moveToTopOfLibrary(tgtSA.getSourceCard()); - } - else if(destination.equals("Hand")) { - AllZone.GameAction.moveToHand(tgtSA.getSourceCard()); - } - else if(destination.equals("BottomDeck")) { - AllZone.GameAction.moveToBottomOfLibrary(tgtSA.getSourceCard()); - } - else if(destination.equals("Shuffle")) { - AllZone.GameAction.moveToBottomOfLibrary(tgtSA.getSourceCard()); - tgtSA.getSourceCard().getController().shuffle(); - } - else { - throw new IllegalArgumentException("AbilityFactory_CounterMagic: Invalid Destination argument for card " + source.getName()); - } - - for(int ea = 0; ea < splitExtraActions.length; ea++) { - boolean isOptional = false; - - if(splitExtraActions[0].equals("None")) { - break; - } - String ActionID = splitExtraActions[ea].substring(0,splitExtraActions[ea].indexOf('(')); - - Player Target = null; - - String ActionParams = splitExtraActions[ea].substring(splitExtraActions[ea].indexOf('(')+1); - ActionParams = ActionParams.substring(0,ActionParams.length()-1); - - String[] SplitActionParams = ActionParams.split(","); - - System.out.println("Extra Action: " + ActionID); - System.out.println("Parameters: " + ActionParams); - - if(ActionID.startsWith("My-")) { - ActionID = ActionID.substring(3); - Target = source.getController(); - } - else if(ActionID.startsWith("Opp-")) { - ActionID = ActionID.substring(4); - Target = source.getController().getOpponent(); - } - else if(ActionID.startsWith("CC-")) { - ActionID = ActionID.substring(3); - Target = tgtSA.getSourceCard().getController(); - } - - if(ActionID.startsWith("May-")) { - ActionID = ActionID.substring(4); - isOptional = true; - } - - if(ActionID.equals("Draw")) { - if(isOptional) { - if(Target == AllZone.HumanPlayer) { - if(GameActionUtil.showYesNoDialog(source, "Do you want to draw " + SplitActionParams[0] + " card(s)?")) { - Target.drawCards(Integer.parseInt(SplitActionParams[0])); - } - } - else { - //AI decision-making, only draws a card if it doesn't risk discarding it. - if(AllZone.getZone(Constant.Zone.Hand,AllZone.ComputerPlayer).getCards().length + Integer.parseInt(SplitActionParams[0]) < 6) { - Target.drawCards(Integer.parseInt(SplitActionParams[0])); - } - } - } - else { - Target.drawCards(Integer.parseInt(SplitActionParams[0])); - } - - } - else if(ActionID.equals("Discard")) { - if(isOptional) { - if(Target == AllZone.HumanPlayer) { - if(GameActionUtil.showYesNoDialog(source, "Do you want to discard " + SplitActionParams[0] + " card(s)?")) { - Target.discard(Integer.parseInt(SplitActionParams[0]), sa, true); - } - } - else { - //AI decisionmaking. Should take Madness cards and the like into account in the future. Right now always refuses to discard. - } - } - else { - Target.discard(Integer.parseInt(SplitActionParams[0]), sa, true); - } - } - else if(ActionID.equals("LoseLife")) { - if(isOptional) { - if(Target == AllZone.HumanPlayer) { - if(GameActionUtil.showYesNoDialog(source, "Do you want to lose " + SplitActionParams[0] + " life?")) { - Target.loseLife(Integer.parseInt(SplitActionParams[0]), source); - } - } - else { - //AI decisionmaking. Not sure why one would ever want to agree to this, except for the rare case of Near-Death Experience+Ali Baba. - } - } - else { - Target.loseLife(Integer.parseInt(SplitActionParams[0]), source); - } - - } - else if(ActionID.equals("GainLife")) { - if(isOptional) { - if(Target == AllZone.HumanPlayer) { - if(AllZone.Display.getChoice("Do you want to gain" + SplitActionParams[0] + "life?","Yes","No").equals("Yes")) { - Target.gainLife(Integer.parseInt(SplitActionParams[0]), source); - } - } - else { - //AI decisionmaking. Not sure why one would ever want to decline this, except for the rare case of Near-Death Experience. - Target.gainLife(Integer.parseInt(SplitActionParams[0]), source); - } - } - else { - Target.gainLife(Integer.parseInt(SplitActionParams[0]), source); - } - } - else if(ActionID.equals("RevealHand")) { - if(isOptional) { - System.out.println(Target); - if(Target == AllZone.HumanPlayer) { - if(AllZone.Display.getChoice("Do you want to reveal your hand?","Yes","No").equals("Yes")) { - //Does nothing now, of course, but sometime in the future the AI may be able to remember cards revealed and prioritize discard spells accordingly. - } - } - else { - //AI decisionmaking. Not sure why one would ever want to agree to this - } - } - else { - System.out.println(Target); - if(Target == AllZone.HumanPlayer) { - //Does nothing now, of course, but sometime in the future the AI may be able to remember cards revealed and prioritize discard spells accordingly. - } - else { - CardList list = new CardList(AllZone.getZone(Constant.Zone.Hand,AllZone.ComputerPlayer).getCards()); - AllZone.Display.getChoiceOptional("Revealed cards",list.toArray()); - } - } - } - else if(ActionID.equals("RearrangeTopOfLibrary")) { - if(isOptional) { - if(Target == AllZone.HumanPlayer) { - if(AllZone.Display.getChoice("Do you want to rearrange the top " + SplitActionParams[0] + " cards of your library?","Yes","No").equals("Yes")) { - AllZoneUtil.rearrangeTopOfLibrary(Target, Integer.parseInt(SplitActionParams[0]), false); - } - } - else { - //AI decisionmaking. AI simply can't atm, and wouldn't know how best to do it anyway. - } - } - else { - if(Target == AllZone.HumanPlayer) { - AllZoneUtil.rearrangeTopOfLibrary(Target, Integer.parseInt(SplitActionParams[0]), false); - } - else { - CardList list = new CardList(AllZone.getZone(Constant.Zone.Hand,AllZone.ComputerPlayer).getCards()); - AllZone.Display.getChoiceOptional("Revealed cards",list.toArray()); - } - } - } - else { - throw new IllegalArgumentException("AbilityFactory_CounterMagic: Invalid Extra Action for card " + sa.getSourceCard().getName()); - } + else + { + removeFromStack(tgtSA,sa); + doExtraActions(tgtSA,sa); } + } - //end copied from spCounter - //} - if (af.hasSubAbility()){ Ability_Sub abSub = sa.getSubAbility(); if (abSub != null){ @@ -316,6 +173,7 @@ public class AbilityFactory_CounterMagic { //reset tgts tgt[0] = null; + } private String counterStackDescription(AbilityFactory af, SpellAbility sa){ @@ -372,8 +230,194 @@ public class AbilityFactory_CounterMagic { Ability_Sub subAb = sa.getSubAbility(); if (subAb != null) toReturn &= subAb.chkAI_Drawback(); + return toReturn; } + + private void removeFromStack(SpellAbility tgtSA,SpellAbility srcSA) + { + AllZone.Stack.remove(tgtSA); + + if(tgtSA.isAbility()) { + //For Ability-targeted counterspells - do not move it anywhere, even if Destination$ is specified. + } + else if(destination.equals("Graveyard")) { + AllZone.GameAction.moveToGraveyard(tgtSA.getSourceCard()); + } + else if(destination.equals("Exile")) { + AllZone.GameAction.exile(tgtSA.getSourceCard()); + } + else if(destination.equals("Topdeck")) { + AllZone.GameAction.moveToTopOfLibrary(tgtSA.getSourceCard()); + } + else if(destination.equals("Hand")) { + AllZone.GameAction.moveToHand(tgtSA.getSourceCard()); + } + else if(destination.equals("BottomDeck")) { + AllZone.GameAction.moveToBottomOfLibrary(tgtSA.getSourceCard()); + } + else if(destination.equals("Shuffle")) { + AllZone.GameAction.moveToBottomOfLibrary(tgtSA.getSourceCard()); + tgtSA.getSourceCard().getController().shuffle(); + } + else { + throw new IllegalArgumentException("AbilityFactory_CounterMagic: Invalid Destination argument for card " + srcSA.getSourceCard().getName()); + } + } + + private void doExtraActions(SpellAbility tgtSA,SpellAbility srcSA) + { + for(int ea = 0; ea < splitExtraActions.length; ea++) { + boolean isOptional = false; + + if(splitExtraActions[0].equals("None")) { + break; + } + String ActionID = splitExtraActions[ea].substring(0,splitExtraActions[ea].indexOf('(')); + + Player Target = null; + + String ActionParams = splitExtraActions[ea].substring(splitExtraActions[ea].indexOf('(')+1); + ActionParams = ActionParams.substring(0,ActionParams.length()-1); + + String[] SplitActionParams = ActionParams.split(","); + + System.out.println("Extra Action: " + ActionID); + System.out.println("Parameters: " + ActionParams); + + if(ActionID.startsWith("My-")) { + ActionID = ActionID.substring(3); + Target = srcSA.getSourceCard().getController(); + } + else if(ActionID.startsWith("Opp-")) { + ActionID = ActionID.substring(4); + Target = srcSA.getSourceCard().getController().getOpponent(); + } + else if(ActionID.startsWith("CC-")) { + ActionID = ActionID.substring(3); + Target = tgtSA.getSourceCard().getController(); + } + + if(ActionID.startsWith("May-")) { + ActionID = ActionID.substring(4); + isOptional = true; + } + + if(ActionID.equals("Draw")) { + if(isOptional) { + if(Target == AllZone.HumanPlayer) { + if(GameActionUtil.showYesNoDialog(srcSA.getSourceCard(), "Do you want to draw " + SplitActionParams[0] + " card(s)?")) { + Target.drawCards(Integer.parseInt(SplitActionParams[0])); + } + } + else { + //AI decision-making, only draws a card if it doesn't risk discarding it. + if(AllZone.getZone(Constant.Zone.Hand,AllZone.ComputerPlayer).getCards().length + Integer.parseInt(SplitActionParams[0]) < 6) { + Target.drawCards(Integer.parseInt(SplitActionParams[0])); + } + } + } + else { + Target.drawCards(Integer.parseInt(SplitActionParams[0])); + } + + } + else if(ActionID.equals("Discard")) { + if(isOptional) { + if(Target == AllZone.HumanPlayer) { + if(GameActionUtil.showYesNoDialog(srcSA.getSourceCard(), "Do you want to discard " + SplitActionParams[0] + " card(s)?")) { + Target.discard(Integer.parseInt(SplitActionParams[0]), srcSA, true); + } + } + else { + //AI decisionmaking. Should take Madness cards and the like into account in the future. Right now always refuses to discard. + } + } + else { + Target.discard(Integer.parseInt(SplitActionParams[0]), srcSA, true); + } + } + else if(ActionID.equals("LoseLife")) { + if(isOptional) { + if(Target == AllZone.HumanPlayer) { + if(GameActionUtil.showYesNoDialog(srcSA.getSourceCard(), "Do you want to lose " + SplitActionParams[0] + " life?")) { + Target.loseLife(Integer.parseInt(SplitActionParams[0]), srcSA.getSourceCard()); + } + } + else { + //AI decisionmaking. Not sure why one would ever want to agree to this, except for the rare case of Near-Death Experience+Ali Baba. + } + } + else { + Target.loseLife(Integer.parseInt(SplitActionParams[0]), srcSA.getSourceCard()); + } + + } + else if(ActionID.equals("GainLife")) { + if(isOptional) { + if(Target == AllZone.HumanPlayer) { + if(AllZone.Display.getChoice("Do you want to gain" + SplitActionParams[0] + "life?","Yes","No").equals("Yes")) { + Target.gainLife(Integer.parseInt(SplitActionParams[0]), srcSA.getSourceCard()); + } + } + else { + //AI decisionmaking. Not sure why one would ever want to decline this, except for the rare case of Near-Death Experience. + Target.gainLife(Integer.parseInt(SplitActionParams[0]), srcSA.getSourceCard()); + } + } + else { + Target.gainLife(Integer.parseInt(SplitActionParams[0]), srcSA.getSourceCard()); + } + } + else if(ActionID.equals("RevealHand")) { + if(isOptional) { + System.out.println(Target); + if(Target == AllZone.HumanPlayer) { + if(AllZone.Display.getChoice("Do you want to reveal your hand?","Yes","No").equals("Yes")) { + //Does nothing now, of course, but sometime in the future the AI may be able to remember cards revealed and prioritize discard spells accordingly. + } + } + else { + //AI decisionmaking. Not sure why one would ever want to agree to this + } + } + else { + System.out.println(Target); + if(Target == AllZone.HumanPlayer) { + //Does nothing now, of course, but sometime in the future the AI may be able to remember cards revealed and prioritize discard spells accordingly. + } + else { + CardList list = new CardList(AllZone.getZone(Constant.Zone.Hand,AllZone.ComputerPlayer).getCards()); + AllZone.Display.getChoiceOptional("Revealed cards",list.toArray()); + } + } + } + else if(ActionID.equals("RearrangeTopOfLibrary")) { + if(isOptional) { + if(Target == AllZone.HumanPlayer) { + if(AllZone.Display.getChoice("Do you want to rearrange the top " + SplitActionParams[0] + " cards of your library?","Yes","No").equals("Yes")) { + AllZoneUtil.rearrangeTopOfLibrary(Target, Integer.parseInt(SplitActionParams[0]), false); + } + } + else { + //AI decisionmaking. AI simply can't atm, and wouldn't know how best to do it anyway. + } + } + else { + if(Target == AllZone.HumanPlayer) { + AllZoneUtil.rearrangeTopOfLibrary(Target, Integer.parseInt(SplitActionParams[0]), false); + } + else { + CardList list = new CardList(AllZone.getZone(Constant.Zone.Hand,AllZone.ComputerPlayer).getCards()); + AllZone.Display.getChoiceOptional("Revealed cards",list.toArray()); + } + } + } + else { + throw new IllegalArgumentException("AbilityFactory_CounterMagic: Invalid Extra Action for card " + srcSA.getSourceCard().getName()); + } + } + } private static boolean matchSpellAbility(Card srcCard, SpellAbility sa, String[] splitRestrictions, String targetType) { if(targetType.equals("Spell")) { diff --git a/src/forge/CardFactory_Instants.java b/src/forge/CardFactory_Instants.java index f708b89362b..f2a3e476b4f 100644 --- a/src/forge/CardFactory_Instants.java +++ b/src/forge/CardFactory_Instants.java @@ -1210,7 +1210,7 @@ public class CardFactory_Instants { spell.setBeforePayMana(CardFactoryUtil.input_targetPlayer(spell)); }//*************** END ************ END ************************** - + /* //*************** START *********** START ************************** else if(cardName.equals("Spell Pierce")) { SpellAbility spell = new Spell(card) { @@ -1405,7 +1405,7 @@ public class CardFactory_Instants { card.setText(""); } }//*************** END ************ END ************************** - + */ //*************** START *********** START ************************** else if(cardName.equals("Impulse")) {