From 3909cea01c98bb400995375ae529c403a3d6bafb Mon Sep 17 00:00:00 2001 From: jendave Date: Sat, 6 Aug 2011 21:25:58 +0000 Subject: [PATCH] add ExileFromTop<1/Card> (for example) as a Cost. add Royal Herbalist as an example. --- .gitattributes | 1 + res/cardsfolder/royal_herbalist.txt | 9 ++ src/forge/AllZoneUtil.java | 4 + src/forge/ComputerUtil.java | 19 +++ src/forge/card/spellability/Cost.java | 62 +++++++++- src/forge/card/spellability/Cost_Payment.java | 117 +++++++++++++++++- 6 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 res/cardsfolder/royal_herbalist.txt diff --git a/.gitattributes b/.gitattributes index a4ff9522d1b..3d73dd6d6eb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5407,6 +5407,7 @@ res/cardsfolder/rowan_treefolk.txt -text svneol=native#text/plain res/cardsfolder/royal_assassin.txt -text svneol=native#text/plain res/cardsfolder/royal_decree.txt -text svneol=native#text/plain res/cardsfolder/royal_falcon.txt -text svneol=native#text/plain +res/cardsfolder/royal_herbalist.txt -text svneol=native#text/plain res/cardsfolder/royal_trooper.txt -text svneol=native#text/plain res/cardsfolder/rubinia_soulsinger.txt -text svneol=native#text/plain res/cardsfolder/ruby_leech.txt -text svneol=native#text/plain diff --git a/res/cardsfolder/royal_herbalist.txt b/res/cardsfolder/royal_herbalist.txt new file mode 100644 index 00000000000..7fe61dc940c --- /dev/null +++ b/res/cardsfolder/royal_herbalist.txt @@ -0,0 +1,9 @@ +Name:Royal Herbalist +ManaCost:W +Types:Creature Human Cleric +Text:no text +PT:1/1 +A:AB$GainLife | Cost$ 2 ExileFromTop<1/Card> | LifeAmount$ 1 | SpellDescription$ You gain 1 life. +SVar:Rarity:Common +SVar:Picture:http://www.wizards.com/global/images/magic/general/royal_herbalist.jpg +End diff --git a/src/forge/AllZoneUtil.java b/src/forge/AllZoneUtil.java index 088604bf12b..9eff0aee42d 100644 --- a/src/forge/AllZoneUtil.java +++ b/src/forge/AllZoneUtil.java @@ -245,6 +245,10 @@ public class AllZoneUtil { return PlayerZoneUtil.isCardInZone(AllZone.getZone(Constant.Zone.Hand, player), card); } + public static boolean isCardInPlayerLibrary(Player player, Card card) { + return PlayerZoneUtil.isCardInZone(AllZone.getZone(Constant.Zone.Library, player), card); + } + ////////////// EXILE /** diff --git a/src/forge/ComputerUtil.java b/src/forge/ComputerUtil.java index 5bcf39195c6..8268d276e33 100644 --- a/src/forge/ComputerUtil.java +++ b/src/forge/ComputerUtil.java @@ -425,6 +425,21 @@ public class ComputerUtil return false; } + if(cost.getExileFromTopCost()){ + if(!cost.getExileFromTopThis()){ + CardList typeList = AllZoneUtil.getPlayerCardsInLibrary(AllZone.ComputerPlayer); + typeList = typeList.getValidCards(cost.getExileFromTopType().split(","), sa.getActivatingPlayer(), sa.getSourceCard()); + Card target = sa.getTargetCard(); + if (target != null && target.getController().equals(AllZone.ComputerPlayer)) // don't exile the card we're pumping + typeList.remove(target); + + if (cost.getExileFromTopAmount() > typeList.size()) + return false; + } + else if (cost.getExileFromTopThis() && !AllZoneUtil.isCardInPlayerLibrary(card.getController(), card)) + return false; + } + if (cost.getReturnCost()){ // if there's a return in the cost, just because we can Pay it doesn't mean we want to. if (!cost.getReturnThis()){ @@ -720,6 +735,10 @@ public class ComputerUtil return chooseExileFrom(Constant.Zone.Graveyard, type, activate, target, amount); } + static public CardList chooseExileFromTopType(String type, Card activate, Card target, int amount){ + return chooseExileFrom(Constant.Zone.Library, type, activate, target, amount); + } + static public CardList chooseExileFrom(String zone, String type, Card activate, Card target, int amount){ PlayerZone grave = AllZone.getZone(zone, AllZone.ComputerPlayer); CardList typeList = new CardList(grave.getCards()); diff --git a/src/forge/card/spellability/Cost.java b/src/forge/card/spellability/Cost.java index 1ae9e1b8d0d..d308a02fde6 100644 --- a/src/forge/card/spellability/Cost.java +++ b/src/forge/card/spellability/Cost.java @@ -45,6 +45,15 @@ public class Cost { public boolean getExileFromGraveThis() { return exileFromGraveThis; } private int exileFromGraveAmount = 0; public int getExileFromGraveAmount() { return exileFromGraveAmount; } + + private boolean exileFromTopCost = false; + public boolean getExileFromTopCost() { return exileFromTopCost; } + private String exileFromTopType = ""; // or CARDNAME + public String getExileFromTopType() { return exileFromTopType; } + private boolean exileFromTopThis = false; + public boolean getExileFromTopThis() { return exileFromTopThis; } + private int exileFromTopAmount = 0; + public int getExileFromTopAmount() { return exileFromTopAmount; } private boolean tapCost = false; public boolean getTap() { return tapCost; } @@ -105,8 +114,8 @@ public class Cost { public void setXMana(int xCost) { manaXCost = xCost; } public boolean isOnlyManaCost() { - return !sacCost && !exileCost && !exileFromHandCost && !exileFromGraveCost && !tapCost && !tapXTypeCost && - !untapCost && !subtractCounterCost && !addCounterCost && !lifeCost && !discardCost && !returnCost; + return !sacCost && !exileCost && !exileFromHandCost && !exileFromGraveCost && !exileFromTopCost && !tapCost && + !tapXTypeCost && !untapCost && !subtractCounterCost && !addCounterCost && !lifeCost && !discardCost && !returnCost; } public String getTotalMana() { @@ -232,6 +241,17 @@ public class Cost { exileFromGraveThis = (exileFromGraveType.equals("CARDNAME")); } + String exileFromTopStr = "ExileFromTop<"; + if(parse.contains(exileFromTopStr)) { + exileFromTopCost = true; + String[] splitStr = abCostParse(parse, exileFromTopStr, 2); + parse = abUpdateParse(parse, exileFromTopStr); + + exileFromTopAmount = Integer.parseInt(splitStr[0]); + exileFromTopType = splitStr[1]; + exileFromTopThis = false; + } + String returnStr = "Return<"; if(parse.contains(returnStr)) { returnCost = true; @@ -310,7 +330,7 @@ public class Cost { public boolean isUndoable() { return !(sacCost || exileCost || exileFromHandCost || exileFromGraveCost || tapXTypeCost || discardCost || - returnCost || lifeCost) && hasNoXManaCost() && hasNoManaCost(); + returnCost || lifeCost || exileFromTopCost) && hasNoXManaCost() && hasNoManaCost(); } @@ -399,6 +419,11 @@ public class Cost { first = false; } + if(exileFromTopCost) { + cost.append(exileFromTopString(first)); + first = false; + } + if (returnCost){ cost.append(returnString(first)); first = false; @@ -526,6 +551,11 @@ public class Cost { first = false; } + if( exileFromTopCost ) { + cost.append( exileFromTopString(first) ); + first = false; + } + if (returnCost){ cost.append(returnString(first)); first = false; @@ -656,6 +686,32 @@ public class Cost { } return cost.toString(); } + + public String exileFromTopString(boolean first) { + StringBuilder cost = new StringBuilder(); + if(first) { + if(isAbility) + cost.append("Exile "); + else + cost.append("exile "); + } + else { + cost.append(", Exile "); + } + + if(exileType.equals("CARDNAME")) + cost.append(name).append(" "); + else { + cost.append("the top"); + if(exileFromTopAmount != 1) { + cost.append(convertIntAndTypeToWords(exileFromTopAmount, exileFromTopType)); + } + cost.append(" card"); + if(exileFromTopAmount != 1) cost.append("s"); + cost.append(" of your library"); + } + return cost.toString(); + } public String returnString(boolean first) { diff --git a/src/forge/card/spellability/Cost_Payment.java b/src/forge/card/spellability/Cost_Payment.java index 0f4d47c74c2..11324b691bc 100644 --- a/src/forge/card/spellability/Cost_Payment.java +++ b/src/forge/card/spellability/Cost_Payment.java @@ -44,6 +44,7 @@ public class Cost_Payment { private boolean payExile; private boolean payExileFromHand; private boolean payExileFromGrave; + private boolean payExileFromTop; private boolean payLife; private boolean payDiscard; private boolean payTapXType; @@ -65,6 +66,7 @@ public class Cost_Payment { public void setPayExile(boolean bExile) { payExile = bExile; } public void setPayExileFromHand(boolean bExileFromHand) { payExileFromHand = bExileFromHand; } public void setPayExileFromGrave(boolean bExileFromGrave) { payExileFromGrave = bExileFromGrave; } + public void setPayExileFromTop(boolean bExileFromTop) { payExileFromTop = bExileFromTop; } public void setPayTapXType(boolean bTapX) { payTapXType = bTapX; } public void setPayReturn(boolean bReturn){ payReturn = bReturn; } @@ -82,6 +84,7 @@ public class Cost_Payment { payExile = !cost.getExileCost(); payExileFromHand = !cost.getExileFromHandCost(); payExileFromGrave = !cost.getExileFromGraveCost(); + payExileFromTop = !cost.getExileFromTopCost(); payLife = !cost.getLifeCost(); payDiscard = !cost.getDiscardCost(); payTapXType = !cost.getTapXTypeCost(); @@ -215,6 +218,18 @@ public class Cost_Payment { return false; } + if (cost.getExileFromTopCost()){ + if (!cost.getExileFromTopThis()){ + CardList typeList = AllZoneUtil.getPlayerCardsInLibrary(card.getController()); + + typeList = typeList.getValidCards(cost.getExileFromTopType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); + if (typeList.size() < cost.getExileFromTopAmount()) + return false; + } + else if (!AllZoneUtil.isCardInPlayerLibrary(card.getController(), card)) + return false; + } + if (cost.getReturnCost()){ if (!cost.getReturnThis()){ PlayerZone play = AllZone.getZone(Constant.Zone.Battlefield, card.getController()); @@ -399,6 +414,14 @@ public class Cost_Payment { return false; } + if(!payExileFromTop && cost.getExileFromTopCost()) { // exile stuff here + if (cost.getExileFromTopThis()) + setInput(exileFromTopThis(ability, this)); + else + setInput(exileFromTopType(ability, cost.getExileFromTopType(), this)); + return false; + } + if (!payReturn && cost.getReturnCost()){ // return stuff here if (cost.getReturnThis()) setInput(returnThis(ability, this)); @@ -416,7 +439,7 @@ public class Cost_Payment { // if you add a new Cost type add it here return (payTap && payUntap && payMana && payXMana && paySubCounter && payAddCounter && paySac && payExile && payLife && payDiscard && payTapXType && payReturn && - payExileFromHand && payExileFromGrave); + payExileFromHand && payExileFromGrave && payExileFromTop); } public void resetUndoList(){ @@ -478,6 +501,7 @@ public class Cost_Payment { CardList exileCard = new CardList(); CardList exileFromHandCard = new CardList(); CardList exileFromGraveCard = new CardList(); + CardList exileFromTopCard = new CardList(); CardList tapXCard = new CardList(); CardList returnCard = new CardList(); ability.setActivatingPlayer(AllZone.ComputerPlayer); @@ -536,6 +560,18 @@ public class Cost_Payment { } } + if (cost.getExileFromTopCost()){ + if (cost.getExileFromTopThis()) + exileFromTopCard.add(card); + else + exileFromTopCard = ComputerUtil.chooseExileFromTopType(cost.getExileFromTopType(), card, ability.getTargetCard(), cost.getExileFromTopAmount()); + + if (exileFromTopCard.size() != cost.getExileFromTopAmount()){ + System.out.println("Couldn't find a valid card to exile for: "+card.getName()); + return; + } + } + if (cost.getReturnCost()){ if (cost.getReturnThis()) returnCard.add(card); @@ -643,6 +679,11 @@ public class Cost_Payment { AllZone.GameAction.exile(c); } + if(cost.getExileFromTopCost()) { + for(Card c : exileFromTopCard) + AllZone.GameAction.exile(c); + } + if (cost.getReturnCost()){ for(Card c : returnCard) AllZone.GameAction.moveToHand(c); @@ -1022,6 +1063,33 @@ public class Cost_Payment { return target; }//input_exile() + public static Input exileFromTopThis(final SpellAbility spell, final Cost_Payment payment) { + Input target = new Input() { + private static final long serialVersionUID = 3416809678763443014L; + + @Override + public void showMessage() { + Card card = spell.getSourceCard(); + if(card.getController().equals(AllZone.HumanPlayer) && AllZoneUtil.isCardInPlayerHand(card.getController(), card)) { + //This can't really happen, but if for some reason it could.... + if(AllZoneUtil.getPlayerCardsInLibrary(card.getController()).size() > 0) { + payment.setPayExileFromTop(true); + payment.getAbility().addExiledCost(card); + AllZone.GameAction.exile(card); + stop(); + payment.payCost(); + } + else{ + payment.setCancel(true); + stop(); + payment.payCost(); + } + } + } + }; + return target; + }//input_exile() + public static Input exileFromGraveThis(final SpellAbility spell, final Cost_Payment payment) { Input target = new Input() { private static final long serialVersionUID = 6237561876518762902L; @@ -1229,6 +1297,53 @@ public class Cost_Payment { return target; }//exileFromGraveType() + public static Input exileFromTopType(final SpellAbility spell, final String type, final Cost_Payment payment){ + Input target = new Input() { + private static final long serialVersionUID = -4764871768555887091L; + + @Override + public void showMessage() { + //Card card = spell.getSourceCard(); + CardList typeList; + int nNeeded = payment.getCost().getExileFromTopAmount(); + PlayerZone lib = AllZone.getZone(Constant.Zone.Library, spell.getSourceCard().getController()); + typeList = new CardList(lib.getCards()); + typeList = typeList.getValidCards(type.split(";"), spell.getActivatingPlayer(), spell.getSourceCard()); + + for (int i=0; i < nNeeded; i++) { + if (typeList.size() == 0) + cancel(); + + if(lib.size() > 0) { + Card c = typeList.get(0); + typeList.remove(c); + payment.getAbility().addExiledCost(c); + AllZone.GameAction.exile(c); + if (i == nNeeded-1) done(); + } + } + } + + @Override + public void selectButtonCancel() { + cancel(); + } + + public void done(){ + payment.setPayExileFromTop(true); + stop(); + payment.payCost(); + } + + public void cancel(){ + payment.setCancel(true); + stop(); + payment.payCost(); + } + }; + return target; + }//exileFromTopType() + public static Input input_tapXCost(final int nCards, final String cardType, final CardList cardList, SpellAbility sa, final Cost_Payment payment) { //final SpellAbility sp = sa; Input target = new Input() {