- Added Battlefield Scrounger, Leashling, and Penance

This commit is contained in:
swordshine
2013-06-08 06:07:00 +00:00
parent ac021be730
commit dc2cd5daaf
8 changed files with 475 additions and 1 deletions

4
.gitattributes vendored
View File

@@ -848,6 +848,7 @@ res/cardsfolder/b/battle_strain.txt svneol=native#text/plain
res/cardsfolder/b/battlefield_forge.txt svneol=native#text/plain
res/cardsfolder/b/battlefield_medic.txt svneol=native#text/plain
res/cardsfolder/b/battlefield_percher.txt svneol=native#text/plain
res/cardsfolder/b/battlefield_scrounger.txt -text
res/cardsfolder/b/battleflight_eagle.txt -text
res/cardsfolder/b/battlegate_mimic.txt svneol=native#text/plain
res/cardsfolder/b/battlegrace_angel.txt svneol=native#text/plain
@@ -6109,6 +6110,7 @@ res/cardsfolder/l/leap.txt svneol=native#text/plain
res/cardsfolder/l/leap_of_faith.txt -text
res/cardsfolder/l/leap_of_flame.txt svneol=native#text/plain
res/cardsfolder/l/leaping_lizard.txt svneol=native#text/plain
res/cardsfolder/l/leashling.txt -text
res/cardsfolder/l/leatherback_baloth.txt svneol=native#text/plain
res/cardsfolder/l/leave_no_trace.txt svneol=native#text/plain
res/cardsfolder/l/leech_bonder.txt -text
@@ -7881,6 +7883,7 @@ res/cardsfolder/p/pegasus_refuge.txt svneol=native#text/plain
res/cardsfolder/p/pegasus_stampede.txt svneol=native#text/plain
res/cardsfolder/p/pelakka_wurm.txt svneol=native#text/plain
res/cardsfolder/p/pemmins_aura.txt svneol=native#text/plain
res/cardsfolder/p/penance.txt -text
res/cardsfolder/p/pendelhaven.txt svneol=native#text/plain
res/cardsfolder/p/pendelhaven_elder.txt svneol=native#text/plain
res/cardsfolder/p/pendrell_drake.txt svneol=native#text/plain
@@ -14188,6 +14191,7 @@ src/main/java/forge/card/cost/CostPartMana.java -text
src/main/java/forge/card/cost/CostPartWithList.java -text
src/main/java/forge/card/cost/CostPayLife.java -text
src/main/java/forge/card/cost/CostPayment.java svneol=native#text/plain
src/main/java/forge/card/cost/CostPutCardToLib.java -text
src/main/java/forge/card/cost/CostPutCounter.java -text
src/main/java/forge/card/cost/CostRemoveCounter.java -text
src/main/java/forge/card/cost/CostReturn.java -text

View File

@@ -0,0 +1,8 @@
Name:Battlefield Scrounger
ManaCost:3 G G
Types:Creature Centaur
PT:3/3
A:AB$ Pump | Cost$ PutCardToLibFromGrave<3/-1/Card> | Activation$ Threshold | ActivationLimit$ 1 | NumAtt$ +3 | NumDef$ +3 | PrecostDesc$ Threshold - | SpellDescription$ CARDNAME gets +3/+3 until end of turn. Activate this ability only once each turn, and only if seven or more cards are in your graveyard.
SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/battlefield_scrounger.jpg
Oracle:Threshold - Put three cards from your graveyard on the bottom of your library: Battlefield Scrounger gets +3/+3 until end of turn. Activate this ability only once each turn, and only if seven or more cards are in your graveyard.

View File

@@ -0,0 +1,7 @@
Name:Leashling
ManaCost:6
Types:Artifact Creature Hound
PT:3/3
A:AB$ ChangeZone | Cost$ PutCardToLibFromHand<1/0/Card> | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return CARDNAME to its owner's hand.
SVar:Picture:http://www.wizards.com/global/images/magic/general/leashling.jpg
Oracle:Put a card from your hand on top of your library: Return Leashling to its owner's hand.

View File

@@ -0,0 +1,11 @@
Name:Penance
ManaCost:2 W
Types:Enchantment
A:AB$ ChooseSource | Cost$ PutCardToLibFromHand<1/0/Card> | Choices$ Card.Red,Card.Black | RememberChosen$ True | AILogic$ NeedsPrevention | SubAbility$ DBEffect | SpellDescription$ The next time a black or red source of your choice would deal damage this turn, prevent that damage.
SVar:DBEffect:DB$ Effect | ReplacementEffects$ RPreventNextFromSource | RememberObjects$ Remembered | SVars$ RPreventNextFromSource,ExileEffect | SubAbility$ DBCleanup | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1
SVar:RPreventNextFromSource:Event$ DamageDone | ValidSource$ Card.IsRemembered | ReplaceWith$ ExileEffect | PreventionEffect$ True | Description$ The next time the chosen source deals damage, prevent that damage.
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:RemRandomDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/penance.jpg
Oracle:Put a card from your hand on top of your library: The next time a black or red source of your choice would deal damage this turn, prevent that damage.

View File

@@ -316,12 +316,30 @@ public class Cost {
final String description = splitStr.length > 2 ? splitStr[2] : null;
return new CostReveal(splitStr[0], splitStr[1], description);
}
if(parse.startsWith("PutCardToLibFromHand<")) {
final String[] splitStr = abCostParse(parse, 4);
final String description = splitStr.length > 3 ? splitStr[3] : null;
return new CostPutCardToLib(splitStr[0], splitStr[1], splitStr[2], description, ZoneType.Hand);
}
if(parse.startsWith("PutCardToLibFromGrave<")) {
final String[] splitStr = abCostParse(parse, 4);
final String description = splitStr.length > 3 ? splitStr[3] : null;
return new CostPutCardToLib(splitStr[0], splitStr[1], splitStr[2], description, ZoneType.Graveyard);
}
if(parse.startsWith("PutCardToLibFromSameGrave<")) {
final String[] splitStr = abCostParse(parse, 4);
final String description = splitStr.length > 3 ? splitStr[3] : null;
return new CostPutCardToLib(splitStr[0], splitStr[1], splitStr[2], description, ZoneType.Graveyard, true);
}
// These won't show up with multiples
if (parse.equals("Untap") || parse.equals("Q")) {
return new CostUntap();
}
if (parse.equals("T")) {
return new CostTap();
}

View File

@@ -0,0 +1,339 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card.cost;
import java.util.ArrayList;
import java.util.List;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.Singletons;
import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility;
import forge.game.Game;
import forge.game.ai.ComputerUtil;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.gui.input.InputSelectCards;
import forge.gui.input.InputSelectCardsFromList;
/**
* This is for the "PutCardToLib" Cost.
*/
public class CostPutCardToLib extends CostPartWithList {
// PutCardToLibFromHand<Num/LibPos/Type{/TypeDescription}>
// PutCardToLibFromSameGrave<Num/LibPos/Type{/TypeDescription}>
// PutCardToLibFromGrave<Num/LibPos/Type{/TypeDescription}>
private ZoneType from = ZoneType.Hand;
private boolean sameZone = false;
private String libPosition = "0";
/**
* Gets the from.
*
* @return the from
*/
public final ZoneType getFrom() {
return this.from;
}
/**
* Gets the libposition.
*
* @return the libposition
*/
public final String getLibPos() {
return this.libPosition;
}
/**
* Instantiates a new cost exile.
*
* @param amount
* the amount
* @param type
* the type
* @param description
* the description
* @param from
* the from
*/
public CostPutCardToLib(final String amount, final String libpos,
final String type, final String description, final ZoneType from) {
super(amount, type, description);
if (from != null) {
this.from = from;
}
this.libPosition = libpos;
}
public CostPutCardToLib(final String amount, final String libpos, final String type,
final String description, final ZoneType from, final boolean sameZone) {
this(amount, libpos, type, description, from);
this.sameZone = sameZone;
}
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#toString()
*/
@Override
public final String toString() {
final StringBuilder sb = new StringBuilder();
final Integer i = this.convertAmount();
sb.append("Put ");
final String desc = this.getTypeDescription() == null ? this.getType() : this.getTypeDescription();
sb.append(Cost.convertAmountTypeToWords(i, this.getAmount(), desc));
if (this.sameZone) {
sb.append(" from the same ");
} else {
sb.append(" from your ");
}
sb.append(this.from).append(" on ");
if (this.libPosition.equals("0")) {
sb.append("top of");
} else {
sb.append("the bottom of");
}
if (this.sameZone) {
sb.append(" their owner's library");
} else {
sb.append(" your library");
}
return sb.toString();
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
return "CardPutToLib";
}
/*
* (non-Javadoc)
*
* @see
* forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility,
* forge.Card, forge.Player, forge.card.cost.Cost)
*/
@Override
public final boolean canPay(final SpellAbility ability) {
final Player activator = ability.getActivatingPlayer();
final Card source = ability.getSourceCard();
final Game game = activator.getGame();
Integer i = this.convertAmount();
if (i == null) {
final String sVar = ability.getSVar(this.getAmount());
if (sVar.equals("XChoice")) {
return true;
}
i = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
List<Card> typeList = new ArrayList<Card>();
if (this.sameZone) {
typeList = new ArrayList<Card>(game.getCardsIn(this.getFrom()));
} else {
typeList = new ArrayList<Card>(activator.getCardsIn(this.getFrom()));
}
typeList = CardLists.getValidCards(typeList, this.getType().split(";"), activator, source);
if (typeList.size() < i) {
return false;
}
if (this.sameZone) {
boolean foundPayable = false;
List<Player> players = game.getPlayers();
for (Player p : players) {
if (CardLists.filter(typeList, CardPredicates.isController(p)).size() >= i) {
foundPayable = true;
break;
}
}
if (!foundPayable) {
return false;
}
}
return true;
}
/*
* (non-Javadoc)
*
* @see
* forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Game game) {
final String amount = this.getAmount();
Integer c = this.convertAmount();
final Card source = ability.getSourceCard();
final Player activator = ability.getActivatingPlayer();
List<Card> list;
if (this.sameZone) {
list = new ArrayList<Card>(game.getCardsIn(this.getFrom()));
} else {
list = new ArrayList<Card>(activator.getCardsIn(this.getFrom()));
}
if (c == null) {
final String sVar = ability.getSVar(amount);
// Generalize this
if (sVar.equals("XChoice")) {
c = Cost.chooseXValue(source, ability, this.getList().size());
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
list = CardLists.getValidCards(list, this.getType().split(";"), activator, source);
if (this.from == ZoneType.Hand) {
InputSelectCards inp = new InputSelectCardsFromList(c, c, list);
inp.setMessage("Put %d card(s) from your " + from );
inp.setCancelAllowed(true);
Singletons.getControl().getInputQueue().setInputAndWait(inp);
return !inp.hasCancelled() && executePayment(ability, inp.getSelected());
}
if (this.sameZone){
List<Player> players = game.getPlayers();
List<Player> payableZone = new ArrayList<Player>();
for (Player p : players) {
List<Card> enoughType = CardLists.filter(list, CardPredicates.isOwner(p));
if (enoughType.size() < c) {
list.removeAll(enoughType);
} else {
payableZone.add(p);
}
}
return PutFromSame(list, c, payableZone);
} else {//Graveyard
return PutFromMiscZone(ability, c, list);
}
}
/**
* TODO: Write javadoc for this method.
* @param sa
* @param nNeeded
* @param typeList
* @return a boolean
*/
private boolean PutFromMiscZone(SpellAbility sa, int nNeeded, List<Card> typeList) {
for (int i = 0; i < nNeeded; i++) {
if (typeList.isEmpty()) {
return false;
}
final Card c = GuiChoose.oneOrNone("Put from " + getFrom() + " to library", typeList);
if (c != null) {
typeList.remove(c);
executePayment(sa, c);
} else {
return false;
}
}
return true;
}
private boolean PutFromSame(List<Card> list, int nNeeded, List<Player> payableZone) {
if (nNeeded == 0) {
return true;
}
final Player p = GuiChoose.oneOrNone(String.format("Put cards from whose %s?", getFrom()), payableZone);
if (p == null) {
return false;
}
List<Card> typeList = CardLists.filter(list, CardPredicates.isOwner(p));
for (int i = 0; i < nNeeded; i++) {
if (typeList.isEmpty()) {
return false;
}
final Card c = GuiChoose.oneOrNone("Put cards from " + getFrom() + " to Library", typeList);
if (c != null) {
typeList.remove(c);
executePayment(null, c);
} else {
return false;
}
}
return true;
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
protected void doPayment(SpellAbility ability, Card targetCard) {
ability.getActivatingPlayer().getGame().getAction().moveToLibrary(targetCard, Integer.parseInt(getLibPos()));
}
/* (non-Javadoc)
* @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public PaymentDecision decideAIPayment(Player ai, SpellAbility ability, Card source) {
Integer c = this.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(this.getAmount());
// Generalize this
if (sVar.equals("XChoice")) {
return null;
}
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
if (this.sameZone) {
// TODO Determine exile from same zone for AI
return null;
} else {
List<Card> chosen = ComputerUtil.choosePutToLibraryFrom(ai, this.getFrom(), this.getType(), source, ability.getTargetCard(), c);
return null == chosen ? null : new PaymentDecision(chosen);
}
}
}

View File

@@ -506,6 +506,51 @@ public class ComputerUtil {
return exileList;
}
/**
* <p>
* choosePutToLibraryFrom.
* </p>
*
* @param zone
* a {@link java.lang.String} object.
* @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 List<Card> choosePutToLibraryFrom(final Player ai, final ZoneType zone, final String type, final Card activate,
final Card target, final int amount) {
List<Card> typeList = ai.getCardsIn(zone);
typeList = CardLists.getValidCards(typeList, type.split(","), activate.getController(), activate);
if ((target != null) && target.getController() == ai && typeList.contains(target)) {
typeList.remove(target); // don't move the card we're pumping
}
if (typeList.size() < amount) {
return null;
}
CardLists.sortByPowerAsc(typeList);
final List<Card> list = new ArrayList<Card>();
if (zone != ZoneType.Hand) {
Collections.reverse(typeList);
}
for (int i = 0; i < amount; i++) {
list.add(typeList.get(i));
}
return list;
}
/**
* <p>
* chooseTapType.

View File

@@ -8,6 +8,7 @@ import org.apache.commons.lang3.StringUtils;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates.Presets;
import forge.CardPredicates;
import forge.CounterType;
import forge.FThreads;
import forge.GameLogEntryType;
@@ -26,6 +27,7 @@ import forge.card.cost.CostPartMana;
import forge.card.cost.CostPartWithList;
import forge.card.cost.CostPayLife;
import forge.card.cost.CostPayment;
import forge.card.cost.CostPutCardToLib;
import forge.card.cost.CostPutCounter;
import forge.card.cost.CostRemoveCounter;
import forge.card.cost.CostReturn;
@@ -396,6 +398,46 @@ public class HumanPlay {
}
}
}
else if (part instanceof CostPutCardToLib) {
//Jotun Grunt
int amount = Integer.parseInt(((CostPutCardToLib) part).getAmount());
final ZoneType from = ((CostPutCardToLib) part).getFrom();
List<Card> list = CardLists.getValidCards(p.getGame().getCardsIn(from), part.getType().split(","), p, source);
List<Player> players = p.getGame().getPlayers();
List<Player> payableZone = new ArrayList<Player>();
for (Player player : players) {
List<Card> enoughType = CardLists.filter(list, CardPredicates.isOwner(player));
if (enoughType.size() < amount) {
list.removeAll(enoughType);
} else {
payableZone.add(player);
}
}
Player chosen = GuiChoose.oneOrNone(String.format("Put cards from whose %s?",
((CostPutCardToLib) part).getFrom()), payableZone);
if (chosen == null) {
return false;
}
List<Card> typeList = CardLists.filter(list, CardPredicates.isOwner(chosen));
for (int i = 0; i < amount; i++) {
if (typeList.isEmpty()) {
return false;
}
final Card c = GuiChoose.oneOrNone("Put cards to Library", typeList);
if (c != null) {
typeList.remove(c);
p.getGame().getAction().moveToLibrary(c, Integer.parseInt(((CostPutCardToLib) part).getLibPos()));
} else {
return false;
}
}
return true;
}
else if (part instanceof CostSacrifice) {
int amount = Integer.parseInt(((CostSacrifice)part).getAmount());