From 34efd19d3aa24e2275fa378f3fcb9ac57038afaa Mon Sep 17 00:00:00 2001 From: moomarc Date: Mon, 1 Oct 2012 18:19:41 +0000 Subject: [PATCH] - Added Nicmagus Elemental (RtR) --- .gitattributes | 1 + res/cardsfolder/n/nivmagus_elemental.txt | 12 ++ src/main/java/forge/card/cost/Cost.java | 9 ++ src/main/java/forge/card/cost/CostExile.java | 116 +++++++++++++++++- src/main/java/forge/card/cost/CostUtil.java | 9 +- .../java/forge/game/player/ComputerUtil.java | 43 +++++-- 6 files changed, 176 insertions(+), 14 deletions(-) create mode 100644 res/cardsfolder/n/nivmagus_elemental.txt diff --git a/.gitattributes b/.gitattributes index 000225c5f3a..095fede3828 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6657,6 +6657,7 @@ res/cardsfolder/n/niv_mizzet_the_firemind.txt svneol=native#text/plain res/cardsfolder/n/niveous_wisps.txt svneol=native#text/plain res/cardsfolder/n/nivix_aerie_of_the_firemind.txt -text res/cardsfolder/n/nivix_guildmage.txt -text +res/cardsfolder/n/nivmagus_elemental.txt -text res/cardsfolder/n/no_dachi.txt svneol=native#text/plain res/cardsfolder/n/no_mercy.txt svneol=native#text/plain res/cardsfolder/n/no_rest_for_the_wicked.txt -text diff --git a/res/cardsfolder/n/nivmagus_elemental.txt b/res/cardsfolder/n/nivmagus_elemental.txt new file mode 100644 index 00000000000..9d7f0451e61 --- /dev/null +++ b/res/cardsfolder/n/nivmagus_elemental.txt @@ -0,0 +1,12 @@ +Name:Nivmagus Elemental +ManaCost:UR +Types:Creature Elemental +Text:no text +PT:1/2 +A:AB$ PutCounter | Cost$ ExileFromStack<1/Spell.Instant+YouCtrl;Spell.Sorcery+YouCtrl/instant or sorcery spell> | CostDesc$ Exile an instant or sorcery spell you control: | CounterType$ P1P1 | CounterNum$ 2 | SpellDescription$ Put two +1/+1 counters on CARDNAME. (That spell won't resolve.) +SVar:RemAIDeck:True +SVar:Rarity:Rare +SVar:Picture:http://www.wizards.com/global/images/magic/general/nivmagus_elemental.jpg +SetInfo:RTR|Rare|http://magiccards.info/scans/en/rtr/219.jpg +Oracle:Exile an instant or sorcery spell you control: Put two +1/+1 counters on Nivmagus Elemental. (That spell won't resolve.) +End \ No newline at end of file diff --git a/src/main/java/forge/card/cost/Cost.java b/src/main/java/forge/card/cost/Cost.java index 55e53314f77..e926625fd6b 100644 --- a/src/main/java/forge/card/cost/Cost.java +++ b/src/main/java/forge/card/cost/Cost.java @@ -161,6 +161,7 @@ public class Cost { private static final String EXILE_STR = "Exile<"; private static final String EXILE_FROM_HAND_STR = "ExileFromHand<"; private static final String EXILE_FROM_GRAVE_STR = "ExileFromGrave<"; + private static final String EXILE_FROM_STACK_STR = "ExileFromStack<"; private static final String EXILE_FROM_TOP_STR = "ExileFromTop<"; private static final String RETURN_STR = "Return<"; private static final String REVEAL_STR = "Reveal<"; @@ -305,6 +306,14 @@ public class Cost { this.costParts.add(new CostExile(splitStr[0], splitStr[1], description, ZoneType.Graveyard)); } + while (parse.contains(Cost.EXILE_FROM_STACK_STR)) { + final String[] splitStr = this.abCostParse(parse, Cost.EXILE_FROM_STACK_STR, 3); + parse = this.abUpdateParse(parse, Cost.EXILE_FROM_STACK_STR); + + final String description = splitStr.length > 2 ? splitStr[2] : null; + this.costParts.add(new CostExile(splitStr[0], splitStr[1], description, ZoneType.Stack)); + } + while (parse.contains(Cost.EXILE_FROM_TOP_STR)) { final String[] splitStr = this.abCostParse(parse, Cost.EXILE_FROM_TOP_STR, 3); parse = this.abUpdateParse(parse, Cost.EXILE_FROM_TOP_STR); diff --git a/src/main/java/forge/card/cost/CostExile.java b/src/main/java/forge/card/cost/CostExile.java index d33b5bfadd8..873949334d3 100644 --- a/src/main/java/forge/card/cost/CostExile.java +++ b/src/main/java/forge/card/cost/CostExile.java @@ -17,6 +17,7 @@ */ package forge.card.cost; +import java.util.ArrayList; import java.util.Iterator; import javax.swing.JOptionPane; @@ -28,6 +29,7 @@ import forge.GameActionUtil; import forge.Singletons; import forge.card.abilityfactory.AbilityFactory; import forge.card.spellability.SpellAbility; +import forge.card.spellability.SpellAbilityStackInstance; import forge.control.input.Input; import forge.game.player.ComputerUtil; import forge.game.player.Player; @@ -142,7 +144,14 @@ public class CostExile extends CostPartWithList { */ @Override public final boolean canPay(final SpellAbility ability, final Card source, final Player activator, final Cost cost) { - CardList typeList = activator.getCardsIn(this.getFrom()); + CardList typeList = new CardList(); + if (this.getFrom().equals(ZoneType.Stack)) { + for (int i = 0; i < AllZone.getStack().size(); i++) { + typeList.add(AllZone.getStack().peekAbility(i).getSourceCard()); + } + } else { + typeList = activator.getCardsIn(this.getFrom()); + } if (!this.getThis()) { typeList = typeList.getValidCards(this.getType().split(";"), activator, source); @@ -167,6 +176,15 @@ public class CostExile extends CostPartWithList { public final void payAI(final SpellAbility ability, final Card source, final CostPayment payment) { for (final Card c : this.getList()) { Singletons.getModel().getGameAction().exile(c); + if (this.from.equals(ZoneType.Stack)) { + ArrayList spells = c.getSpellAbilities(); + for (SpellAbility spell : spells) { + if (c.isInZone(ZoneType.Exile)) { + final SpellAbilityStackInstance si = AllZone.getStack().getInstanceFromSpellAbility(spell); + AllZone.getStack().remove(si); + } + } + } } } @@ -199,6 +217,8 @@ public class CostExile extends CostPartWithList { CostUtil.setInput(CostExile.exileThis(ability, payment, this)); } else if (this.from.equals(ZoneType.Battlefield) || this.from.equals(ZoneType.Hand)) { CostUtil.setInput(CostExile.exileType(ability, this, this.getType(), payment, c)); + } else if (this.from.equals(ZoneType.Stack)) { + CostUtil.setInput(CostExile.exileFromStack(ability, this, this.getType(), payment, c)); } else if (this.from.equals(ZoneType.Library)) { CostExile.exileFromTop(ability, this, payment, c); } else { @@ -362,6 +382,98 @@ public class CostExile extends CostPartWithList { return target; } // exileFrom() + /** + * Exile from Stack. + * + * @param sa + * the sa + * @param part + * the part + * @param type + * the type + * @param payment + * the payment + * @param nNeeded + * the n needed + * @return the input + */ + public static Input exileFromStack(final SpellAbility sa, final CostExile part, final String type, + final CostPayment payment, final int nNeeded) { + final Input target = new Input() { + private static final long serialVersionUID = 734256837615635021L; + private ArrayList saList; + private ArrayList descList; + + @Override + public void showMessage() { + if (nNeeded == 0) { + this.done(); + } + + saList = new ArrayList(); + descList = new ArrayList(); + + for (int i = 0; i < AllZone.getStack().size(); i++) { + final Card stC = AllZone.getStack().peekAbility(i).getSourceCard(); + final SpellAbility stSA = AllZone.getStack().peekAbility(i).getRootSpellAbility(); + if (stC.isValid(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()) && stSA.isSpell()) { + this.saList.add(stSA); + if (stC.isCopiedSpell()) { + this.descList.add(stSA.getStackDescription() + " (Copied Spell)"); + } else { + this.descList.add(stSA.getStackDescription()); + } + } + } + + for (int i = 0; i < nNeeded; i++) { + if (this.saList.size() == 0) { + this.cancel(); + } + + //Have to use the stack descriptions here because some copied spells have no description otherwise + final String o = GuiUtils + .chooseOneOrNone("Exile from " + part.getFrom(), this.descList); + + if (o != null) { + final SpellAbility toExile = this.saList.get(descList.indexOf(o)); + final Card c = toExile.getSourceCard(); + this.saList.remove(toExile); + part.addToList(c); + if (!c.isCopiedSpell()) { + Singletons.getModel().getGameAction().exile(c); + } + if (i == (nNeeded - 1)) { + this.done(); + } + final SpellAbilityStackInstance si = AllZone.getStack().getInstanceFromSpellAbility(toExile); + AllZone.getStack().remove(si); + } else { + this.cancel(); + break; + } + } + } + + @Override + public void selectButtonCancel() { + this.cancel(); + } + + public void done() { + this.stop(); + part.addListToHash(sa, "Exiled"); + payment.paidCost(part); + } + + public void cancel() { + this.stop(); + payment.cancelCost(); + } + }; + return target; + } // exileFrom() + /** *

* exileType. @@ -403,6 +515,8 @@ public class CostExile extends CostPartWithList { if (part.getFrom().equals(ZoneType.Hand)) { msg.append(" from your Hand"); + } else if (part.getFrom().equals(ZoneType.Stack)) { + msg.append(" from the Stack"); } this.typeList = sa.getActivatingPlayer().getCardsIn(part.getFrom()); this.typeList = this.typeList.getValidCards(type.split(";"), sa.getActivatingPlayer(), diff --git a/src/main/java/forge/card/cost/CostUtil.java b/src/main/java/forge/card/cost/CostUtil.java index 28f45e5210a..eac6bab8d65 100644 --- a/src/main/java/forge/card/cost/CostUtil.java +++ b/src/main/java/forge/card/cost/CostUtil.java @@ -37,7 +37,7 @@ import forge.gui.GuiUtils; */ public class CostUtil { private static Random r = new Random(); - + /** * Check sacrifice cost. * @@ -457,8 +457,7 @@ public class CostUtil { // Just a shortcut.. AllZone.getInputControl().setInput(in, true); } - - + public static Cost combineCosts(Cost cost1, Cost cost2) { if (cost1 == null) { if (cost2 == null) { @@ -467,11 +466,11 @@ public class CostUtil { return cost2; } } - + if (cost2 == null) { return cost1; } - + for (final CostPart part : cost1.getCostParts()) { if (!(part instanceof CostMana)) { cost2.getCostParts().add(part); diff --git a/src/main/java/forge/game/player/ComputerUtil.java b/src/main/java/forge/game/player/ComputerUtil.java index 3b21cf941f2..d46a447025e 100644 --- a/src/main/java/forge/game/player/ComputerUtil.java +++ b/src/main/java/forge/game/player/ComputerUtil.java @@ -1330,7 +1330,7 @@ public class ComputerUtil { if (sa.getPayCosts() != null) { for (CostPart part : sa.getPayCosts().getCostParts()) { if (part instanceof CostDiscard) { - return false; + return false; } } } @@ -1338,7 +1338,6 @@ public class ComputerUtil { } } } - landList = landList.filter(new Predicate() { @Override @@ -1481,7 +1480,7 @@ public class ComputerUtil { // priority 5 the highest final int priority = 6 - ip; for (Card c : typeList) { - if (priority == 3 && c.isLand() + if (priority == 3 && c.isLand() && !AllZone.getComputerPlayer().getCardsIn(ZoneType.Battlefield, "Crucible of Worlds").isEmpty()) { return c; } @@ -1491,7 +1490,7 @@ public class ComputerUtil { } } - // Discard lands + // Discard lands final CardList landsInHand = typeList.getType("Land"); if (!landsInHand.isEmpty()) { final CardList landsInPlay = AllZone.getComputerPlayer().getCardsIn(ZoneType.Battlefield).getType("Land"); @@ -1626,7 +1625,7 @@ public class ComputerUtil { continue; } List aiCards = AllZone.getComputerPlayer().getCardsIn(ZoneType.Battlefield); - final int numLandsInPlay = Iterables.size(Iterables.filter(aiCards, CardPredicates.Presets.LANDS)); + final int numLandsInPlay = Iterables.size(Iterables.filter(aiCards, CardPredicates.Presets.LANDS)); final CardList landsInHand = hand.filter(CardPredicates.Presets.LANDS); final int numLandsInHand = landsInHand.size(); @@ -1636,7 +1635,7 @@ public class ComputerUtil { || (numLandsInHand > 0 && numLandsInPlay > 5)) { discardList.add(landsInHand.get(0)); hand.remove(landsInHand.get(0)); - } else { // Discard other stuff + } else { // Discard other stuff CardListUtil.sortCMC(hand); int numLandsAvailable = numLandsInPlay; if (numLandsInHand > 0) { @@ -1716,6 +1715,26 @@ public class ComputerUtil { return ComputerUtil.chooseExileFrom(ZoneType.Graveyard, type, activate, target, amount); } + /** + *

+ * chooseExileFromStackType. + *

+ * + * @param type + * a {@link java.lang.String} object. + * @param activate + * a {@link forge.Card} object. + * @param target + * a {@link forge.Card} object. + * @param amount + * a int. + * @return a {@link forge.CardList} object. + */ + public static CardList chooseExileFromStackType(final String type, final Card activate, final Card target, + final int amount) { + return ComputerUtil.chooseExileFrom(ZoneType.Stack, type, activate, target, amount); + } + /** *

* chooseExileFrom. @@ -1735,8 +1754,16 @@ public class ComputerUtil { */ public static CardList chooseExileFrom(final ZoneType zone, final String type, final Card activate, final Card target, final int amount) { - CardList typeList = AllZone.getComputerPlayer().getCardsIn(zone); - typeList = typeList.getValidCards(type.split(","), activate.getController(), activate); + CardList typeList = new CardList(); + if (zone.equals(ZoneType.Stack)) { + for (int i = 0; i < AllZone.getStack().size(); i++) { + typeList.add(AllZone.getStack().peekAbility(i).getSourceCard()); + typeList = typeList.getValidCards(type.split(","), activate.getController(), activate); + } + } else { + typeList = AllZone.getComputerPlayer().getCardsIn(zone); + typeList = typeList.getValidCards(type.split(","), activate.getController(), activate); + } if ((target != null) && target.getController().isComputer() && typeList.contains(target)) { typeList.remove(target); // don't exile the card we're pumping }