diff --git a/.gitattributes b/.gitattributes index ef0828d7a3b..9c72fc95f20 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1968,6 +1968,7 @@ res/cardsfolder/kraken_hatchling.txt -text svneol=native#text/plain res/cardsfolder/krakilin.txt -text svneol=native#text/plain res/cardsfolder/kranioceros.txt -text svneol=native#text/plain res/cardsfolder/kresh_the_bloodbraided.txt -text svneol=native#text/plain +res/cardsfolder/kris_mage.txt -text svneol=native#text/plain res/cardsfolder/krosan_cloudscraper.txt -text svneol=native#text/plain res/cardsfolder/krosan_colossus.txt -text svneol=native#text/plain res/cardsfolder/krovikan_horror.txt -text svneol=native#text/plain @@ -3391,6 +3392,7 @@ res/cardsfolder/storm_herd.txt -text svneol=native#text/plain res/cardsfolder/storm_seeker.txt -text svneol=native#text/plain res/cardsfolder/storm_shaman.txt -text svneol=native#text/plain res/cardsfolder/storm_spirit.txt -text svneol=native#text/plain +res/cardsfolder/stormbind.txt -text svneol=native#text/plain res/cardsfolder/stormcallers_boon.txt -text svneol=native#text/plain res/cardsfolder/stormcloud_djinn.txt -text svneol=native#text/plain res/cardsfolder/stormfront_pegasus.txt -text svneol=native#text/plain diff --git a/res/cardsfolder/kris_mage.txt b/res/cardsfolder/kris_mage.txt new file mode 100644 index 00000000000..56a0c927a43 --- /dev/null +++ b/res/cardsfolder/kris_mage.txt @@ -0,0 +1,10 @@ +Name:Kris Mage +ManaCost:R +Types:Creature Human Spellshaper +Text:no text +PT:1/1 +K:abDamageTgtCP R T Discard<1,Any>:1 +K:SVar:Rarity:Common +K:SVar:Picture:http://www.wizards.com/global/images/magic/general/kris_mage.jpg +K:SVar:RemAIDeck:True +End diff --git a/res/cardsfolder/stormbind.txt b/res/cardsfolder/stormbind.txt new file mode 100644 index 00000000000..3022df7a2a1 --- /dev/null +++ b/res/cardsfolder/stormbind.txt @@ -0,0 +1,9 @@ +Name:Stormbind +ManaCost:1 R G +Types:Enchantment +Text:no text +K:abDamageTgtCP 2 Discard<1,Random>:2 +K:SVar:Rarity:Rare +K:SVar:Picture:http://www.wizards.com/global/images/magic/general/stormbind.jpg +K:SVar:RemAIDeck:True +End diff --git a/src/forge/Ability_Cost.java b/src/forge/Ability_Cost.java index 82382a26695..e686be2d120 100644 --- a/src/forge/Ability_Cost.java +++ b/src/forge/Ability_Cost.java @@ -32,6 +32,13 @@ public class Ability_Cost { private int lifeAmount = 0; public int getLifeAmount() { return lifeAmount; } + private boolean discardCost = false; + public boolean getDiscardCost() { return discardCost; } + private int discardAmount = 0; + public int getDiscardAmount() { return discardAmount; } + private String discardType = ""; + public String getDiscardType() { return discardType; } + public boolean hasNoManaCost() { return manaCost.equals("") || manaCost.equals("0"); }; private String manaCost = ""; public String getMana() { return manaCost; } @@ -75,6 +82,23 @@ public class Ability_Cost { lifeAmount = Integer.parseInt(str); } + if (parse.contains("Discard<")){ + // Discard + discardCost = true; + int startPos = parse.indexOf("Discard<"); + int endPos = parse.indexOf(">", startPos); + String str = parse.substring(startPos, endPos+1); + parse = parse.replace(str, "").trim(); + + str = str.replace("Discard<", ""); + str = str.replace(">", ""); + + String[] splitStr = str.split(","); + + discardAmount = Integer.parseInt(splitStr[0]); + discardType = splitStr[1]; + } + if(parse.contains("Sac-")) { // todo(sol): change from Sac- to Sac, also use IsValidCard with type sacCost = true; @@ -121,7 +145,7 @@ public class Ability_Cost { cost.append(manaCost); } - if (tapCost){ + if (tapCost || untapCost){ // tap cost for spells will not be in this form. } @@ -141,6 +165,29 @@ public class Ability_Cost { first = false; } + if (discardCost){ + if (first) + cost.append("discard "); + else + cost.append("and discard "); + if (discardType.equals("Hand")){ + cost.append(" your hand"); + } + else{ + cost.append(discardAmount); + int type = discardType.indexOf("/"); + if (type != -1) + cost.append(discardType.substring(type + 1)).append(" "); + cost.append(" card"); + if (discardAmount > 1) + cost.append("s"); + if (discardType.equals("Random")) + cost.append(" at random"); + } + + first = false; + } + cost.append(sacString(first)); cost.append("."); @@ -201,6 +248,28 @@ public class Ability_Cost { first = false; } + if (discardCost){ + if (first) + cost.append("Discard "); + else + cost.append(", discard "); + if (discardType.equals("Hand")){ + cost.append(" your hand"); + } + else{ + cost.append(discardAmount); + int type = discardType.indexOf("/"); + if (type != -1) + cost.append(discardType.substring(type + 1)).append(" "); + cost.append(" card"); + if (discardAmount > 1) + cost.append("s"); + if (discardType.equals("Random")) + cost.append(" at random"); + } + + first = false; + } cost.append(sacString(first)); diff --git a/src/forge/ComputerUtil.java b/src/forge/ComputerUtil.java index 5d81c9d524f..f0be44edb50 100644 --- a/src/forge/ComputerUtil.java +++ b/src/forge/ComputerUtil.java @@ -244,7 +244,27 @@ public class ComputerUtil return false; } - // check additional costs. + if (cost.getDiscardCost()){ + PlayerZone zone = AllZone.getZone(Constant.Zone.Hand, card.getController()); + CardList handList = new CardList(zone.getCards()); + String discType = cost.getDiscardType(); + int discAmount = cost.getDiscardAmount(); + if (discType.equals("Hand")){ + // this will always work + } + else{ + int type = discType.indexOf("/"); + if (type != -1){ + String validType[] = discType.substring(type+1).split(","); + handList = handList.getValidCards(validType); + } + if (discAmount > handList.size()){ + // not enough cards in hand to pay + return false; + } + } + } + if (cost.getSacCost()){ // if there's a sacrifice in the cost, just because we can Pay it doesn't mean we want to. if (!cost.getSacThis()){ diff --git a/src/forge/Cost_Payment.java b/src/forge/Cost_Payment.java index 0042e96af33..c2f5882d6e9 100644 --- a/src/forge/Cost_Payment.java +++ b/src/forge/Cost_Payment.java @@ -23,10 +23,12 @@ public class Cost_Payment { private boolean paySubCounter; private boolean paySac; private boolean payLife; + private boolean payDiscard; private boolean bCancel = false; public void setPayMana(boolean bPay){ payMana = bPay; } + public void setPayDiscard(boolean bSac){ payDiscard = bSac; } public void setPaySac(boolean bSac){ paySac = bSac; } final private Input changeInput = new Input() { @@ -42,6 +44,7 @@ public class Cost_Payment { paySubCounter = !cost.getSubCounter(); paySac = !cost.getSacCost(); payLife = !cost.getLifeCost(); + payDiscard = !cost.getDiscardCost(); } public boolean canPayAdditionalCosts(){ @@ -66,6 +69,27 @@ public class Cost_Payment { return false; } + if (cost.getDiscardCost()){ + PlayerZone zone = AllZone.getZone(Constant.Zone.Hand, card.getController()); + CardList handList = new CardList(zone.getCards()); + String discType = cost.getDiscardType(); + int discAmount = cost.getDiscardAmount(); + if (discType.equals("Hand")){ + // this will always work + } + else{ + int type = discType.indexOf("/"); + if (type != -1){ + String validType[] = discType.substring(type+1).split(","); + handList = handList.getValidCards(validType); + } + if (discAmount > handList.size()){ + // not enough cards in hand to pay + return false; + } + } + } + if (cost.getSacCost()){ if (!cost.getSacThis()){ PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController()); @@ -147,8 +171,33 @@ public class Cost_Payment { } } - if (!paySac && cost.getSacCost()){ - // sacrifice stuff here + if (!payDiscard && cost.getDiscardCost()){ // discard here + PlayerZone zone = AllZone.getZone(Constant.Zone.Hand, card.getController()); + CardList handList = new CardList(zone.getCards()); + String discType = cost.getDiscardType(); + int discAmount = cost.getDiscardAmount(); + if (discType.equals("Hand")){ + AllZone.GameAction.discardHand(card.getController(), ability); + payDiscard = true; + } + else{ + if (discType.equals("Random")){ + AllZone.GameAction.discardRandom(card.getController(), discAmount, ability); + payDiscard = true; + } + else{ + int type = discType.indexOf("/"); + if (type != -1){ + String validType[] = discType.substring(type+1).split(","); + handList = handList.getValidCards(validType); + } + changeInput.stopSetNext(input_discardCost(discAmount, handList, ability, this)); + return false; + } + } + } + + if (!paySac && cost.getSacCost()){ // sacrifice stuff here if (cost.getSacThis()) changeInput.stopSetNext(sacrificeThis(ability, this)); else @@ -161,7 +210,7 @@ public class Cost_Payment { } public boolean isAllPaid(){ - return (payTap && payUntap && payMana && paySubCounter && paySac && payLife); + return (payTap && payUntap && payMana && paySubCounter && paySac && payLife && payDiscard); } public void cancelPayment(){ @@ -190,10 +239,13 @@ public class Cost_Payment { life.payLife(cost.getLifeAmount()*-1); } + // can't really undiscard things + // can't really unsacrifice things } public void payComputerCosts(){ + // make sure ComputerUtil.canPayAdditionalCosts() is updated when updating new Costs Card sacCard = null; ability.setActivatingPlayer(Constant.Player.Computer); @@ -235,12 +287,87 @@ public class Cost_Payment { if (cost.getLifeCost()) AllZone.GameAction.getPlayerLife(card.getController()).payLife(cost.getLifeAmount()); + if (cost.getDiscardCost()){ + String discType = cost.getDiscardType(); + int discAmount = cost.getDiscardAmount(); + if (discType.equals("Hand")){ + AllZone.GameAction.discardHand(card.getController(), ability); + } + else{ + if (discType.equals("Random")){ + AllZone.GameAction.discardRandom(card.getController(), discAmount, ability); + } + else{ + int type = discType.indexOf("/"); + if (type != -1){ + String validType[] = discType.substring(type+1).split(","); + AllZone.GameAction.AI_discardNumType(discAmount, validType, ability); + } + else{ + AllZone.GameAction.AI_discardNumUnless(discAmount, ability); + } + } + } + } + if (cost.getSacCost()) AllZone.GameAction.sacrifice(sacCard); AllZone.Stack.add(ability); } + public static Input input_discardCost(final int nCards, final CardList handList, SpellAbility sa, final Cost_Payment payment) { + final SpellAbility sp = sa; + Input target = new Input() { + private static final long serialVersionUID = -329993322080934435L; + + int nDiscard = 0; + + @Override + public void showMessage() { + if (AllZone.Human_Hand.getCards().length == 0) stop(); + + AllZone.Display.showMessage("Select a card to discard"); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectButtonCancel() { + cancel(); + } + + @Override + public void selectCard(Card card, PlayerZone zone) { + if(zone.is(Constant.Zone.Hand) && handList.contains(card) ) { + // send in CardList for Typing + AllZone.GameAction.discard(card, sp); + handList.remove(card); + nDiscard++; + + //in case no more cards in hand + if(nDiscard == nCards) + done(); + else if (AllZone.Human_Hand.getCards().length == 0) // this really shouldn't happen + cancel(); + else + showMessage(); + } + } + + public void cancel(){ + payment.setCancel(true); + stop(); + payment.payCost(); + } + + public void done(){ + payment.setPayDiscard(true); + stop(); + payment.payCost(); + } + }; + return target; + }//input_discard() public static Input sacrificeThis(final SpellAbility spell, final Cost_Payment payment) { Input target = new Input() {