Costs no longer keep AI's choice in the CostPart class,

CardList has filter method to apply 2 filters sequentially
Untap: old inputs replaced with synchronized ones.
InputProxy - using an AtomicReference to hold current input, just in case.
This commit is contained in:
Maxmtg
2013-03-28 08:14:13 +00:00
parent 564c97571b
commit 98a539f23a
29 changed files with 652 additions and 990 deletions

1
.gitattributes vendored
View File

@@ -13713,6 +13713,7 @@ src/main/java/forge/card/cost/CostUntap.java -text
src/main/java/forge/card/cost/CostUntapType.java -text src/main/java/forge/card/cost/CostUntapType.java -text
src/main/java/forge/card/cost/CostUtil.java -text src/main/java/forge/card/cost/CostUtil.java -text
src/main/java/forge/card/cost/InputPayCostBase.java -text src/main/java/forge/card/cost/InputPayCostBase.java -text
src/main/java/forge/card/cost/PaymentDecision.java -text
src/main/java/forge/card/cost/package-info.java svneol=native#text/plain src/main/java/forge/card/cost/package-info.java svneol=native#text/plain
src/main/java/forge/card/mana/IParserManaCost.java -text src/main/java/forge/card/mana/IParserManaCost.java -text
src/main/java/forge/card/mana/Mana.java svneol=native#text/plain src/main/java/forge/card/mana/Mana.java svneol=native#text/plain

View File

@@ -8993,7 +8993,7 @@ public class Card extends GameEntity implements Comparable<Card> {
if (getController() == null) { if (getController() == null) {
return false; return false;
} }
return getController().getCardsIn(ZoneType.Battlefield).contains(this); return getController().getZone(ZoneType.Battlefield).contains(this);
} }
public void onCleanupPhase(final Player turn) { public void onCleanupPhase(final Player turn) {

View File

@@ -254,6 +254,10 @@ public class CardLists {
return Lists.newArrayList(Iterables.filter(cardList, filt)); return Lists.newArrayList(Iterables.filter(cardList, filt));
} }
public static List<Card> filter(Iterable<Card> cardList, Predicate<Card> f1, Predicate<Card> f2) {
return Lists.newArrayList(Iterables.filter(Iterables.filter(cardList, f1), f2));
}
public static List<Card> createCardList(Card c) { public static List<Card> createCardList(Card c) {
List<Card> res = new ArrayList<Card>(); List<Card> res = new ArrayList<Card>();
res.add(c); res.add(c);

View File

@@ -21,11 +21,11 @@ public class FThreads {
private FThreads() { } // no instances supposed private FThreads() { } // no instances supposed
private final static ExecutorService threadPool = Executors.newCachedThreadPool(); private final static ExecutorService cachedPool = Executors.newCachedThreadPool();
private static ExecutorService getCachedPool() { return threadPool; } private static ExecutorService getCachedPool() { return cachedPool; }
private final static ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(1); private final static ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);
private static ScheduledExecutorService getScheduledPool() { return scheduledPool; } private static ScheduledExecutorService getScheduledPool() { return scheduledPool; }
// This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5 // This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5
public final static ExecutorService getComputingPool(float loadFactor) { public final static ExecutorService getComputingPool(float loadFactor) {
return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor))); return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor)));

View File

@@ -28,6 +28,7 @@ import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost; import forge.card.cost.Cost;
import forge.card.cost.CostDiscard; import forge.card.cost.CostDiscard;
import forge.card.cost.CostPart; import forge.card.cost.CostPart;
import forge.card.cost.PaymentDecision;
import forge.card.spellability.AbilitySub; import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
@@ -70,8 +71,10 @@ public class DrawAi extends SpellAbilityAi {
for (final CostPart part : abCost.getCostParts()) { for (final CostPart part : abCost.getCostParts()) {
if (part instanceof CostDiscard) { if (part instanceof CostDiscard) {
CostDiscard cd = (CostDiscard) part; CostDiscard cd = (CostDiscard) part;
cd.decideAIPayment((AIPlayer) ai, sa, sa.getSourceCard(), null); PaymentDecision decision = cd.decideAIPayment((AIPlayer) ai, sa, sa.getSourceCard());
for (Card discard : cd.getList()) { if ( null == decision )
return false;
for (Card discard : decision.cards) {
if (!ComputerUtil.isWorseThanDraw(ai, discard)) { if (!ComputerUtil.isWorseThanDraw(ai, discard)) {
return false; return false;
} }

View File

@@ -145,13 +145,22 @@ public class Cost {
StringBuilder manaParts = new StringBuilder(); StringBuilder manaParts = new StringBuilder();
String[] parts = TextUtil.splitWithParenthesis(parse, ' ', '<', '>'); String[] parts = TextUtil.splitWithParenthesis(parse, ' ', '<', '>');
// make this before parse so that classes that need it get data in their constructor
for(String part : parts) {
if ( part.equals("T") || part.equals("Tap") )
this.tapCost = true;
if ( part.equals("Q") || part.equals("Untap") )
this.untapCost = true;
}
for(String part : parts) { for(String part : parts) {
if( "XCantBe0".equals(part) ) if( "XCantBe0".equals(part) )
xCantBe0 = true; xCantBe0 = true;
else if ( "X".equals(part) ) else if ( "X".equals(part) )
amountX++; amountX++;
else { else {
CostPart cp = parseCostPart(part); CostPart cp = parseCostPart(part, tapCost, untapCost);
if ( null != cp ) if ( null != cp )
this.costParts.add(cp); this.costParts.add(cp);
else else
@@ -175,22 +184,20 @@ public class Cost {
if( cp instanceof CostUntap ) { if( cp instanceof CostUntap ) {
costParts.remove(iCp); costParts.remove(iCp);
costParts.add(0, cp); costParts.add(0, cp);
untapCost = true;
} }
if( cp instanceof CostTap ) { if( cp instanceof CostTap ) {
costParts.remove(iCp); costParts.remove(iCp);
costParts.add(0, cp); costParts.add(0, cp);
tapCost = true;
} }
} }
} }
private static CostPart parseCostPart(String parse) { private static CostPart parseCostPart(String parse, boolean tapCost, boolean untapCost) {
if(parse.startsWith("tapXType<")) { if(parse.startsWith("tapXType<")) {
final String[] splitStr = abCostParse(parse, 3); final String[] splitStr = abCostParse(parse, 3);
final String description = splitStr.length > 2 ? splitStr[2] : null; final String description = splitStr.length > 2 ? splitStr[2] : null;
return new CostTapType(splitStr[0], splitStr[1], description); return new CostTapType(splitStr[0], splitStr[1], description, tapCost);
} }
if(parse.startsWith("untapYType<")) { if(parse.startsWith("untapYType<")) {
@@ -337,9 +344,9 @@ public class Cost {
* @return an array of {@link java.lang.String} objects. * @return an array of {@link java.lang.String} objects.
*/ */
private static String[] abCostParse(final String parse, final int numParse) { private static String[] abCostParse(final String parse, final int numParse) {
final int startPos = parse.indexOf("<"); final int startPos = 1 + parse.indexOf("<");
final int endPos = parse.indexOf(">", startPos); final int endPos = parse.indexOf(">", startPos);
String str = parse.substring(startPos + 1, endPos); String str = parse.substring(startPos, endPos);
final String[] splitStr = str.split("/", numParse); final String[] splitStr = str.split("/", numParse);
return splitStr; return splitStr;

View File

@@ -29,33 +29,7 @@ import forge.gui.GuiDialog;
* The Class CostPayLife. * The Class CostPayLife.
*/ */
public class CostDamage extends CostPart { public class CostDamage extends CostPart {
private int lastPaidAmount = 0;
/**
* Gets the last paid amount.
*
* @return the last paid amount
*/
public final int getLastPaidAmount() {
return this.lastPaidAmount;
}
/**
* Sets the last paid amount.
*
* @param paidAmount
* the new last paid amount
*/
public final void setLastPaidAmount(final int paidAmount) {
this.lastPaidAmount = paidAmount;
}
/**
* Instantiates a new cost pay life.
*
* @param amount
* the amount
*/
public CostDamage(final String amount) { public CostDamage(final String amount) {
this.setAmount(amount); this.setAmount(amount);
} }
@@ -91,8 +65,8 @@ public class CostDamage extends CostPart {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { public final void payAI(final PaymentDecision decision, final AIPlayer ai, SpellAbility ability, Card source) {
ability.getActivatingPlayer().addDamage(this.getLastPaidAmount(), source); ability.getActivatingPlayer().addDamage(decision.c, source);
} }
/* /*
@@ -139,21 +113,19 @@ public class CostDamage extends CostPart {
* , forge.Card, forge.card.cost.Cost_Payment) * , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { public final PaymentDecision decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source) {
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (c == null) { if (c == null) {
final String sVar = ability.getSVar(this.getAmount()); final String sVar = ability.getSVar(this.getAmount());
// Generalize this // Generalize this
if (sVar.equals("XChoice")) { if (sVar.equals("XChoice")) {
return false; return null; // cannot pay
} else { } else {
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
} }
} }
// activator.payLife(c, null); return new PaymentDecision(c);
this.setLastPaidAmount(c);
return true;
} }
} }

View File

@@ -33,6 +33,7 @@ import forge.game.GameState;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.Aggregates;
/** /**
* The Class CostDiscard. * The Class CostDiscard.
@@ -145,19 +146,6 @@ public class CostDiscard extends CostPartWithList {
return true; return true;
} }
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
for (final Card c : this.getList()) {
executePayment(ability, c);
}
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -172,149 +160,131 @@ public class CostDiscard extends CostPartWithList {
List<Card> handList = new ArrayList<Card>(activator.getCardsIn(ZoneType.Hand)); List<Card> handList = new ArrayList<Card>(activator.getCardsIn(ZoneType.Hand));
String discardType = this.getType(); String discardType = this.getType();
final String amount = this.getAmount(); final String amount = this.getAmount();
this.resetList();
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
if (!handList.contains(source)) { return handList.contains(source) && executePayment(ability, source);
return false; }
}
executePayment(ability, source); if (discardType.equals("Hand")) {
return true; return executePayment(ability, handList);
//this.addToList(source); }
} else if (discardType.equals("Hand")) {
this.setList(handList); if (discardType.equals("LastDrawn")) {
activator.discardHand(ability);
return true;
} else if (discardType.equals("LastDrawn")) {
final Card lastDrawn = activator.getLastDrawnCard(); final Card lastDrawn = activator.getLastDrawnCard();
if (!handList.contains(lastDrawn)) { return handList.contains(lastDrawn) && executePayment(ability, lastDrawn);
return false; }
Integer c = this.convertAmount();
if (discardType.equals("Random")) {
if (c == null) {
final String sVar = ability.getSVar(amount);
// Generalize this
if (sVar.equals("XChoice")) {
c = CostUtil.chooseXValue(source, ability, handList.size());
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
} }
executePayment(ability, lastDrawn);
return true; return executePayment(ability, Aggregates.random(handList, c));
} else { } else {
Integer c = this.convertAmount(); String type = new String(discardType);
boolean sameName = false;
if (discardType.equals("Random")) { if (type.contains("+WithSameName")) {
if (c == null) { sameName = true;
final String sVar = ability.getSVar(amount); type = type.replace("+WithSameName", "");
// Generalize this
if (sVar.equals("XChoice")) {
c = CostUtil.chooseXValue(source, ability, handList.size());
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
this.setList(activator.discardRandom(c, ability));
return true;
} else {
String type = new String(discardType);
boolean sameName = false;
if (type.contains("+WithSameName")) {
sameName = true;
type = type.replace("+WithSameName", "");
}
final String[] validType = type.split(";");
handList = CardLists.getValidCards(handList, validType, activator, ability.getSourceCard());
final List<Card> landList2 = handList;
if (sameName) {
handList = CardLists.filter(handList, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
for (Card card : landList2) {
if (!card.equals(c) && card.getName().equals(c.getName())) {
return true;
}
}
return false;
}
});
}
if (c == null) {
final String sVar = ability.getSVar(amount);
// Generalize this
if (sVar.equals("XChoice")) {
c = CostUtil.chooseXValue(source, ability, handList.size());
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
InputSelectCards inp = new InputSelectCardsFromList(c, c, handList);
inp.setMessage("Select %d more " + getDescriptiveType() + " to discard.");
//InputPayment inp = new InputPayCostDiscard(ability, handList, this, c, discardType);
FThreads.setInputAndWait(inp);
if( inp.hasCancelled() || inp.getSelected().size() != c)
return false;
for(Card crd : inp.getSelected())
executePayment(ability, crd);
return true;
} }
final String[] validType = type.split(";");
handList = CardLists.getValidCards(handList, validType, activator, ability.getSourceCard());
final List<Card> landList2 = handList;
if (sameName) {
handList = CardLists.filter(handList, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
for (Card card : landList2) {
if (!card.equals(c) && card.getName().equals(c.getName())) {
return true;
}
}
return false;
}
});
}
if (c == null) {
final String sVar = ability.getSVar(amount);
// Generalize this
if (sVar.equals("XChoice")) {
c = CostUtil.chooseXValue(source, ability, handList.size());
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
InputSelectCards inp = new InputSelectCardsFromList(c, c, handList);
inp.setMessage("Select %d more " + getDescriptiveType() + " to discard.");
//InputPayment inp = new InputPayCostDiscard(ability, handList, this, c, discardType);
FThreads.setInputAndWait(inp);
if( inp.hasCancelled() || inp.getSelected().size() != c)
return false;
return executePayment(ability, inp.getSelected());
} }
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
final String type = this.getType(); final String type = this.getType();
final List<Card> hand = ai.getCardsIn(ZoneType.Hand); final List<Card> hand = ai.getCardsIn(ZoneType.Hand);
this.resetList();
if (type.equals("LastDrawn")) { if (type.equals("LastDrawn")) {
if (!hand.contains(ai.getLastDrawnCard())) { if (!hand.contains(ai.getLastDrawnCard())) {
return false; return null;
} }
this.addToList(ai.getLastDrawnCard()); return new PaymentDecision(ai.getLastDrawnCard());
} }
else if (this.payCostFromSource()) { else if (this.payCostFromSource()) {
if (!hand.contains(source)) { if (!hand.contains(source)) {
return false; return null;
} }
this.addToList(source); return new PaymentDecision(source);
} }
else if (type.equals("Hand")) { else if (type.equals("Hand")) {
this.getList().addAll(hand); return new PaymentDecision(hand);
} }
else { if (type.contains("WithSameName")) {
if (type.contains("WithSameName")) { return null;
return false; }
} Integer c = this.convertAmount();
Integer c = this.convertAmount(); if (c == null) {
if (c == null) { final String sVar = ability.getSVar(this.getAmount());
final String sVar = ability.getSVar(this.getAmount()); if (sVar.equals("XChoice")) {
if (sVar.equals("XChoice")) { return null;
return false; }
} c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); }
}
if (type.equals("Random")) {
if (type.equals("Random")) { return new PaymentDecision(CardLists.getRandomSubList(hand, c));
this.setList(CardLists.getRandomSubList(hand, c)); } else {
} else { return new PaymentDecision(ai.getAi().getCardsToDiscard(c, type.split(";"), ability));
this.setList(ai.getAi().getCardsToDiscard(c, type.split(";"), ability));
}
} }
return this.getList() != null;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/ */
@Override @Override
public void executePayment(SpellAbility ability, Card targetCard) { protected void doPayment(SpellAbility ability, Card targetCard) {
this.addToList(targetCard);
targetCard.getController().discard(targetCard, ability); targetCard.getController().discard(targetCard, ability);
} }
@@ -326,6 +296,14 @@ public class CostDiscard extends CostPartWithList {
return "Discarded"; return "Discarded";
} }
/* (non-Javadoc)
* @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) {
executePayment(ability, decision.cards);
}
// Inputs // Inputs
} }

View File

@@ -505,28 +505,6 @@ public class CostExile extends CostPartWithList {
return true; return true;
} }
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
for (final Card c : this.getList()) {
Singletons.getModel().getGame().getAction().exile(c);
if (this.from.equals(ZoneType.Stack)) {
ArrayList<SpellAbility> spells = c.getSpellAbilities();
for (SpellAbility spell : spells) {
if (c.isInZone(ZoneType.Exile)) {
final SpellAbilityStackInstance si = Singletons.getModel().getGame().getStack().getInstanceFromSpellAbility(spell);
Singletons.getModel().getGame().getStack().remove(si);
}
}
}
}
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -597,51 +575,7 @@ public class CostExile extends CostPartWithList {
} }
/*
* (non-Javadoc)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) {
this.resetList();
if (this.payCostFromSource()) {
this.getList().add(source);
} else if (this.getType().equals("All")) {
this.setList(new ArrayList<Card>(ability.getActivatingPlayer().getCardsIn(this.getFrom())));
} else {
Integer c = this.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(this.getAmount());
// Generalize this
if (sVar.equals("XChoice")) {
return false;
}
if (sVar.equals("YChoice")) {
return false;
}
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
if (this.from.equals(ZoneType.Library)) {
this.setList(ai.getCardsIn(ZoneType.Library, c));
} else if (this.sameZone) {
// TODO Determine exile from same zone for AI
return false;
} else {
this.setList(ComputerUtil.chooseExileFrom(ai, this.getFrom(), this.getType(), source,
ability.getTargetCard(), c));
}
if ((this.getList() == null) || (this.getList().size() < c)) {
return false;
}
}
return true;
}
// Inputs // Inputs
@@ -683,8 +617,7 @@ public class CostExile extends CostPartWithList {
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/ */
@Override @Override
public void executePayment(SpellAbility ability, Card targetCard) { protected void doPayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
ability.getActivatingPlayer().getGame().getAction().exile(targetCard); ability.getActivatingPlayer().getGame().getAction().exile(targetCard);
} }
@@ -696,4 +629,57 @@ public class CostExile extends CostPartWithList {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return "Exiled"; return "Exiled";
} }
/* (non-Javadoc)
* @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
if (this.payCostFromSource()) {
return new PaymentDecision(source);
}
if (this.getType().equals("All")) {
return new PaymentDecision(new ArrayList<Card>(ai.getCardsIn(this.getFrom())));
}
Integer c = this.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(this.getAmount());
// Generalize this
if (sVar.equals("XChoice") || sVar.equals("YChoice")) {
return null;
}
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
if (this.from.equals(ZoneType.Library)) {
return new PaymentDecision(ai.getCardsIn(ZoneType.Library, c));
} else if (this.sameZone) {
// TODO Determine exile from same zone for AI
return null;
} else {
return new PaymentDecision(ComputerUtil.chooseExileFrom(ai, this.getFrom(), this.getType(), source, ability.getTargetCard(), c));
}
}
/* (non-Javadoc)
* @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) {
for (final Card c : decision.cards) {
executePayment(ability, c);
if (this.from.equals(ZoneType.Stack)) {
ArrayList<SpellAbility> spells = c.getSpellAbilities();
for (SpellAbility spell : spells) {
if (c.isInZone(ZoneType.Exile)) {
final SpellAbilityStackInstance si = ai.getGame().getStack().getInstanceFromSpellAbility(spell);
ai.getGame().getStack().remove(si);
}
}
}
}
}
} }

View File

@@ -33,26 +33,6 @@ import forge.gui.GuiChoose;
*/ */
public class CostGainLife extends CostPart { public class CostGainLife extends CostPart {
private final int cntPlayers; // MAX_VALUE means ALL/EACH PLAYERS private final int cntPlayers; // MAX_VALUE means ALL/EACH PLAYERS
private int lastPaidAmount = 0;
/**
* Gets the last paid amount.
*
* @return the last paid amount
*/
public final int getLastPaidAmount() {
return this.lastPaidAmount;
}
/**
* Sets the last paid amount.
*
* @param paidAmount
* the new last paid amount
*/
public final void setLastPaidAmount(final int paidAmount) {
this.lastPaidAmount = paidAmount;
}
/** /**
* Instantiates a new cost gain life. * Instantiates a new cost gain life.
@@ -109,7 +89,7 @@ public class CostGainLife extends CostPart {
} }
} }
return cntPlayers < Integer.MAX_VALUE ? cntAbleToGainLife >= cntPlayers : cntAbleToGainLife == possibleTargets.size(); return cntAbleToGainLife >= cntPlayers || cntPlayers == Integer.MAX_VALUE && cntAbleToGainLife == possibleTargets.size();
} }
/* /*
@@ -119,12 +99,12 @@ public class CostGainLife extends CostPart {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { public final void payAI(final PaymentDecision decision, final AIPlayer ai, SpellAbility ability, Card source) {
int playersLeft = cntPlayers; int playersLeft = cntPlayers;
for (final Player opp : getPotentialTargets(game, ai, source)) { for (final Player opp : getPotentialTargets(ai.getGame(), ai, source)) {
if (opp.canGainLife() && playersLeft > 0) { if (opp.canGainLife() && playersLeft > 0) {
playersLeft--; playersLeft--;
opp.gainLife(this.getLastPaidAmount(), null); opp.gainLife(decision.c, null);
} }
} }
} }
@@ -190,15 +170,13 @@ public class CostGainLife extends CostPart {
* , forge.Card, forge.card.cost.Cost_Payment) * , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { public final PaymentDecision decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source) {
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (c == null) { if (c == null) {
final String sVar = ability.getSVar(this.getAmount()); final String sVar = ability.getSVar(this.getAmount());
// Generalize this // Generalize this
if (sVar.equals("XChoice")) { if (sVar.equals("XChoice")) {
return false; return null;
} else { } else {
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
} }
@@ -212,9 +190,9 @@ public class CostGainLife extends CostPart {
} }
if (oppsThatCanGainLife.size() == 0) { if (oppsThatCanGainLife.size() == 0) {
return false; return null;
} }
this.setLastPaidAmount(c);
return true; return new PaymentDecision(c);
} }
} }

View File

@@ -17,12 +17,9 @@
*/ */
package forge.card.cost; package forge.card.cost;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import forge.Card; import forge.Card;
import forge.Singletons;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
@@ -49,6 +46,14 @@ public class CostMill extends CostPartWithList {
this.setAmount(amount); this.setAmount(amount);
} }
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
return "Milled";
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -74,50 +79,6 @@ public class CostMill extends CostPartWithList {
return i < zone.size(); return i < zone.size();
} }
/*
* (non-Javadoc)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) {
this.resetList();
Integer c = this.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(this.getAmount());
// Generalize this
if (sVar.equals("XChoice")) {
return false;
}
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
this.setList(new ArrayList<Card>(ai.getCardsIn(ZoneType.Library, c)));
if ((this.getList() == null) || (this.getList().size() < c)) {
return false;
}
return true;
}
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
for (final Card c : this.getList()) {
Singletons.getModel().getGame().getAction().moveToGraveyard(c);
}
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -143,21 +104,13 @@ public class CostMill extends CostPartWithList {
} }
final List<Card> list = activator.getCardsIn(ZoneType.Library, c); final List<Card> list = activator.getCardsIn(ZoneType.Library, c);
if ((list == null) || (list.size() > c)) {
// I don't believe this is possible
return false;
}
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append("Mill ").append(c).append(" cards from your library?"); sb.append("Mill ").append(c).append(" cards from your library?");
if ( false == GuiDialog.confirm(source, sb.toString()) ) if ( false == GuiDialog.confirm(source, sb.toString()) )
return false; return false;
this.resetList(); for(final Card card : list) { // this list is a copy, no exception expected
final Iterator<Card> itr = list.iterator();
while (itr.hasNext()) {
final Card card = itr.next();
executePayment(ability, card); executePayment(ability, card);
} }
return true; return true;
@@ -193,17 +146,27 @@ public class CostMill extends CostPartWithList {
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/ */
@Override @Override
public void executePayment(SpellAbility ability, Card targetCard) { protected void doPayment(SpellAbility ability, Card targetCard) {
this.addToList(targetCard);
ability.getActivatingPlayer().getGame().getAction().moveToGraveyard(targetCard); ability.getActivatingPlayer().getGame().getAction().moveToGraveyard(targetCard);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList() * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/ */
@Override @Override
public String getHashForList() { public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
return "Milled"; 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);
}
List<Card> topLib = ai.getCardsIn(ZoneType.Library, c);
return topLib.size() < c ? null : new PaymentDecision(topLib);
} }
} }

View File

@@ -28,17 +28,8 @@ import forge.game.player.Player;
* The Class CostPart. * The Class CostPart.
*/ */
public abstract class CostPart { public abstract class CostPart {
/** The optional. */
// private boolean optional = false;
/** The amount. */
private String amount = "1"; private String amount = "1";
/** The type. */
private final String type; private final String type;
/** The type description. */
private final String typeDescription; private final String typeDescription;
/** /**
@@ -171,7 +162,7 @@ public abstract class CostPart {
* {@link forge.card.cost.CostPayment} * {@link forge.card.cost.CostPayment}
* @return true, if successful * @return true, if successful
*/ */
public abstract boolean decideAIPayment(final AIPlayer ai, SpellAbility ability, Card source, CostPayment payment); public abstract PaymentDecision decideAIPayment(final AIPlayer ai, SpellAbility ability, Card source);
/** /**
* Pay ai. * Pay ai.
@@ -186,7 +177,7 @@ public abstract class CostPart {
* {@link forge.card.cost.CostPayment} * {@link forge.card.cost.CostPayment}
* @param game * @param game
*/ */
public abstract void payAI(final AIPlayer ai, SpellAbility ability, Card source, CostPayment payment, GameState game); public abstract void payAI(final PaymentDecision decision, final AIPlayer ai, SpellAbility ability, Card source);
/** /**
* Pay human. * Pay human.

View File

@@ -187,14 +187,11 @@ public class CostPartMana extends CostPart {
return true; return true;
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) {
ComputerUtilMana.payManaCost(ai, ability); ComputerUtilMana.payManaCost(ai, ability);
} }
@@ -235,16 +232,12 @@ public class CostPartMana extends CostPart {
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
return true; return new PaymentDecision(0);
} }
// Inputs // Inputs

View File

@@ -17,13 +17,13 @@
*/ */
package forge.card.cost; package forge.card.cost;
import java.util.HashSet; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set;
import forge.Card; import forge.Card;
import forge.CardUtil; import forge.CardUtil;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.player.AIPlayer;
/** /**
* The Class CostPartWithList. * The Class CostPartWithList.
@@ -31,7 +31,7 @@ import forge.card.spellability.SpellAbility;
public abstract class CostPartWithList extends CostPart { public abstract class CostPartWithList extends CostPart {
/** The list. */ /** The list. */
private Set<Card> list = new HashSet<Card>(); private final List<Card> list = new ArrayList<Card>();
// set is here because executePayment() adds card to list, while ai's decide payment does the same thing. // set is here because executePayment() adds card to list, while ai's decide payment does the same thing.
// set allows to avoid duplication // set allows to avoid duplication
@@ -40,21 +40,10 @@ public abstract class CostPartWithList extends CostPart {
* *
* @return the list * @return the list
*/ */
public final Set<Card> getList() { public final List<Card> getList() {
return this.list; return this.list;
} }
/**
* Sets the list.
*
* @param setList
* the new list
*/
public final void setList(final List<Card> setList) {
this.list.clear();
list.addAll(setList);
}
/** /**
* Reset list. * Reset list.
*/ */
@@ -62,13 +51,7 @@ public abstract class CostPartWithList extends CostPart {
this.list.clear(); this.list.clear();
} }
/** protected final void addToList(final Card c) {
* Adds the to list.
*
* @param c
* the c
*/
public final void addToList(final Card c) {
this.list.add(c); this.list.add(c);
} }
@@ -105,14 +88,33 @@ public abstract class CostPartWithList extends CostPart {
*/ */
public CostPartWithList(final String amount, final String type, final String description) { public CostPartWithList(final String amount, final String type, final String description) {
super(amount, type, description); super(amount, type, description);
this.resetList();
} }
public abstract void executePayment(SpellAbility ability, Card targetCard); public final boolean executePayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
doPayment(ability, targetCard);
return true;
}
// always returns true, made this to inline with return
public final boolean executePayment(SpellAbility ability, Collection<Card> targetCards) {
for(Card c: targetCards)
executePayment(ability, c);
return true;
}
protected abstract void doPayment(SpellAbility ability, Card targetCard);
/** /**
* TODO: Write javadoc for this method. * TODO: Write javadoc for this method.
* @return * @return
*/ */
public abstract String getHashForList(); public abstract String getHashForList();
@Override
public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) {
executePayment(ability, decision.cards);
reportPaidCardsTo(ability);
}
} }

View File

@@ -29,27 +29,8 @@ import forge.gui.GuiDialog;
* The Class CostPayLife. * The Class CostPayLife.
*/ */
public class CostPayLife extends CostPart { public class CostPayLife extends CostPart {
private int lastPaidAmount = 0; int paidAmount = 0;
/**
* Gets the last paid amount.
*
* @return the last paid amount
*/
public final int getLastPaidAmount() {
return this.lastPaidAmount;
}
/**
* Sets the last paid amount.
*
* @param paidAmount
* the new last paid amount
*/
public final void setLastPaidAmount(final int paidAmount) {
this.lastPaidAmount = paidAmount;
}
/** /**
* Instantiates a new cost pay life. * Instantiates a new cost pay life.
* *
@@ -80,7 +61,7 @@ public class CostPayLife extends CostPart {
@Override @Override
public final void refund(final Card source) { public final void refund(final Card source) {
// Really should be activating player // Really should be activating player
source.getController().payLife(this.lastPaidAmount * -1, null); source.getController().payLife(this.paidAmount * -1, null);
} }
/* /*
@@ -104,15 +85,14 @@ public class CostPayLife extends CostPart {
return true; return true;
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) {
ai.payLife(this.getLastPaidAmount(), null); // TODO Auto-generated method stub
paidAmount = decision.c;
ai.payLife(paidAmount, null);
} }
/* /*
@@ -153,34 +133,29 @@ public class CostPayLife extends CostPart {
} else { } else {
return false; return false;
} }
paidAmount = c;
return true; return true;
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (c == null) { if (c == null) {
final String sVar = ability.getSVar(this.getAmount()); final String sVar = ability.getSVar(this.getAmount());
// Generalize this // Generalize this
if (sVar.equals("XChoice")) { if (sVar.equals("XChoice")) {
return false; return null;
} else { } else {
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
} }
} }
if (!ai.canPayLife(c)) { if (!ai.canPayLife(c)) {
return false; return null;
} }
// activator.payLife(c, null); // activator.payLife(c, null);
this.setLastPaidAmount(c); return new PaymentDecision(c);
return true;
} }
} }

View File

@@ -18,8 +18,9 @@
package forge.card.cost; package forge.card.cost;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import forge.Card; import forge.Card;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
@@ -181,15 +182,17 @@ public class CostPayment {
parts.add(new CostPartMana("0", 0, false)); parts.add(new CostPartMana("0", 0, false));
} }
Map<Class<? extends CostPart>, PaymentDecision> decisions = new HashMap<Class<? extends CostPart>, PaymentDecision>();
// Set all of the decisions before attempting to pay anything // Set all of the decisions before attempting to pay anything
for (final CostPart part : parts) { for (final CostPart part : parts) {
if (!part.decideAIPayment(ai, this.ability, source, this)) { PaymentDecision decision = part.decideAIPayment(ai, this.ability, source);
return false; if ( null == decision ) return false;
} decisions.put(part.getClass(), decision);
} }
for (final CostPart part : parts) { for (final CostPart part : parts) {
part.payAI(ai, this.ability, this.ability.getSourceCard(), this, game); part.payAI(decisions.get(part.getClass()), ai, this.ability, this.ability.getSourceCard());
} }
return true; return true;
} }

View File

@@ -217,16 +217,14 @@ public class CostPutCounter extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) {
Integer c = getNumberOfCounters(ability); Integer c = getNumberOfCounters(ability);
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
executePayment(ability, source, c); executePayment(ability, source, c);
} else { } else {
// Put counter on chosen card // Put counter on chosen card
for (final Card card : this.getList()) { executePayment(ability, decision.cards);
executePayment(ability, card);
}
} }
} }
@@ -260,44 +258,23 @@ public class CostPutCounter extends CostPartWithList {
return c; return c;
} }
/*
* (non-Javadoc)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) {
this.resetList();
if (this.payCostFromSource()) {
this.addToList(source);
return true;
} else {
final List<Card> typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ai, source);
Card card = null;
if (this.getType().equals("Creature.YouCtrl")) {
card = ComputerUtilCard.getWorstCreatureAI(typeList);
} else {
card = ComputerUtilCard.getWorstPermanentAI(typeList, false, false, false, false);
}
this.addToList(card);
}
return true;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/ */
@Override @Override
public void executePayment(SpellAbility ability, Card targetCard){ protected void doPayment(SpellAbility ability, Card targetCard){
executePayment(ability, targetCard, 1); targetCard.addCounter(this.getCounter(), 1, false);
} }
public void executePayment(SpellAbility ability, Card targetCard, int c) { protected void executePayment(SpellAbility ability, Card targetCard, int c) {
targetCard.addCounter(this.getCounter(), c, false); CounterType counterType = this.getCounter();
this.addToList(targetCard); if( c > 1 ) {
Integer oldValue = targetCard.getCounters().get(counterType);
int newValue = c + (oldValue == null ? 0 : oldValue.intValue()) - 1;
targetCard.getCounters().put(counterType, Integer.valueOf(newValue));
}
// added c - 1 without firing triggers, the last counter added should fire trigger.
executePayment(ability, targetCard);
} }
@@ -305,4 +282,26 @@ public class CostPutCounter extends CostPartWithList {
public String getHashForList() { public String getHashForList() {
return "CounterPut"; return "CounterPut";
} }
/* (non-Javadoc)
* @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
if (this.payCostFromSource()) {
return new PaymentDecision(source);
}
final List<Card> typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ai, source);
Card card = null;
if (this.getType().equals("Creature.YouCtrl")) {
card = ComputerUtilCard.getWorstCreatureAI(typeList);
} else {
card = ComputerUtilCard.getWorstPermanentAI(typeList, false, false, false, false);
}
return new PaymentDecision(card);
}
} }

View File

@@ -183,6 +183,7 @@ public class CostRemoveCounter extends CostPartWithList {
private final CounterType counter; private final CounterType counter;
private int lastPaidAmount = 0; private int lastPaidAmount = 0;
private int cntCounters = 1;
private ZoneType zone; private ZoneType zone;
/** /**
@@ -320,14 +321,11 @@ public class CostRemoveCounter extends CostPartWithList {
return true; return true;
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPartWithList#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) {
final String amount = this.getAmount(); final String amount = this.getAmount();
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (c == null) { if (c == null) {
@@ -339,10 +337,10 @@ public class CostRemoveCounter extends CostPartWithList {
} }
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
source.subtractCounter(this.counter, c); executePayment(ability, source);
} else { } else {
for (final Card card : this.getList()) { for (final Card card : decision.cards) {
card.subtractCounter(this.counter, c); executePayment(ability, card);
} }
} }
source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c)); source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c));
@@ -361,108 +359,45 @@ public class CostRemoveCounter extends CostPartWithList {
final Card source = ability.getSourceCard(); final Card source = ability.getSourceCard();
Integer c = this.convertAmount(); Integer c = this.convertAmount();
int maxCounters = 0; int maxCounters = 0;
if (!this.payCostFromSource()) { if (amount.equals("All"))
if (c == null) { cntCounters = maxCounters;
final String sVar = ability.getSVar(amount); else if (c == null) {
// Generalize this final String sVar = ability.getSVar(amount);
if (sVar.equals("XChoice")) { if (sVar.equals("XChoice")) {
c = CostUtil.chooseXValue(source, ability, maxCounters); cntCounters = CostUtil.chooseXValue(source, ability, maxCounters);
} else { } else {
c = AbilityUtils.calculateAmount(source, amount, ability); cntCounters = AbilityUtils.calculateAmount(source, amount, ability);
}
} }
} else cntCounters = c;
if (!this.payCostFromSource()) {
final InputPayment inp; final InputPayment inp;
if (this.getZone().equals(ZoneType.Battlefield)) { if (this.getZone().equals(ZoneType.Battlefield)) {
inp = new InputPayCostRemoveCounterType(c, ability, this.getType(), this); inp = new InputPayCostRemoveCounterType(cntCounters, ability, this.getType(), this);
} else { } else {
inp = new InputPayCostRemoveCounterFrom(this, this.getType(), ability, c); inp = new InputPayCostRemoveCounterFrom(this, this.getType(), ability, cntCounters);
} }
FThreads.setInputAndWait(inp); FThreads.setInputAndWait(inp);
if( inp.isPaid() ) source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c));
return inp.isPaid(); return inp.isPaid();
} }
maxCounters = source.getCounters(this.counter); maxCounters = source.getCounters(this.counter);
if (amount.equals("All")) {
c = maxCounters;
} else {
if (c == null) {
final String sVar = ability.getSVar(amount);
// Generalize this
if (sVar.equals("XChoice")) {
c = CostUtil.chooseXValue(source, ability, maxCounters);
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
}
if (maxCounters < c) return false; if (maxCounters < c) return false;
this.addToList(source);
source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c)); source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c));
executePayment(ability, source, c); executePayment(ability, source);
return true; return true;
} }
/*
* (non-Javadoc)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { protected void doPayment(SpellAbility ability, Card targetCard){
final String amount = this.getAmount(); targetCard.subtractCounter(this.getCounter(), cntCounters);
Integer c = this.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(amount);
if (sVar.equals("XChoice")) {
return false;
}
if (amount.equals("All")) {
c = source.getCounters(this.counter);
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
if (!this.payCostFromSource()) {
this.getList().clear();
final List<Card> typeList =
CardLists.getValidCards(ai.getCardsIn(this.getZone()), this.getType().split(";"), ai, source);
for (Card card : typeList) {
if (card.getCounters(this.getCounter()) >= c) {
this.addToList(card);
return true;
}
}
return false;
}
if (c > source.getCounters(this.getCounter())) {
System.out.println("Not enough " + this.counter + " on " + source.getName());
return false;
}
return true;
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
executePayment(ability, targetCard, 1);
}
public void executePayment(SpellAbility ability, Card targetCard, int n) {
addToList(targetCard);
targetCard.subtractCounter(getCounter(), n);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList() * @see forge.card.cost.CostPartWithList#getHashForList()
*/ */
@@ -470,4 +405,43 @@ public class CostRemoveCounter extends CostPartWithList {
public String getHashForList() { public String getHashForList() {
return "CounterRemove"; return "CounterRemove";
} }
/* (non-Javadoc)
* @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
final String amount = this.getAmount();
Integer c = this.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(amount);
if (sVar.equals("XChoice")) {
return null;
}
if (amount.equals("All")) {
c = source.getCounters(this.counter);
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
if (!this.payCostFromSource()) {
final List<Card> typeList =
CardLists.getValidCards(ai.getCardsIn(this.getZone()), this.getType().split(";"), ai, source);
for (Card card : typeList) {
if (card.getCounters(this.getCounter()) >= c) {
return new PaymentDecision(card);
}
}
return null;
}
if (c > source.getCounters(this.getCounter())) {
System.out.println("Not enough " + this.counter + " on " + source.getName());
return null;
}
return new PaymentDecision(source);
}
} }

View File

@@ -111,19 +111,6 @@ public class CostReturn extends CostPartWithList {
return true; return true;
} }
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
for (final Card c : this.getList()) {
executePayment(ability, c);
}
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -180,30 +167,24 @@ public class CostReturn extends CostPartWithList {
* , forge.Card, forge.card.cost.Cost_Payment) * , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { public final PaymentDecision decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source) {
this.resetList(); if (this.payCostFromSource())
if (this.payCostFromSource()) { return new PaymentDecision(source);
this.getList().add(source);
} else { Integer c = this.convertAmount();
Integer c = this.convertAmount(); if (c == null) {
if (c == null) { c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
this.setList(ComputerUtil.chooseReturnType(ai, this.getType(), source, ability.getTargetCard(), c));
if (this.getList().isEmpty()) {
return false;
}
} }
return true;
List<Card> res = ComputerUtil.chooseReturnType(ai, this.getType(), source, ability.getTargetCard(), c);
return res.isEmpty() ? null : new PaymentDecision(res);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/ */
@Override @Override
public void executePayment(SpellAbility ability, Card targetCard) { protected void doPayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
ability.getActivatingPlayer().getGame().getAction().moveToHand(targetCard); ability.getActivatingPlayer().getGame().getAction().moveToHand(targetCard);
} }
@@ -215,6 +196,16 @@ public class CostReturn extends CostPartWithList {
return "Returned"; return "Returned";
} }
/* (non-Javadoc)
* @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) {
for (final Card c : decision.cards) {
executePayment(ability, c);
}
}
// Inputs // Inputs

View File

@@ -31,7 +31,6 @@ import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
/** /**
@@ -172,56 +171,37 @@ public class CostReveal extends CostPartWithList {
return true; return true;
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
final String type = this.getType(); final String type = this.getType();
List<Card> hand = new ArrayList<Card>(ai.getCardsIn(ZoneType.Hand)); List<Card> hand = new ArrayList<Card>(ai.getCardsIn(ZoneType.Hand));
this.resetList();
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
if (!hand.contains(source)) { if (!hand.contains(source)) {
return false; return null;
} }
return new PaymentDecision(source);
this.getList().add(source);
} else if (this.getType().equals("Hand")) {
this.setList(new ArrayList<Card>(ai.getCardsIn(ZoneType.Hand)));
return true;
} else {
hand = CardLists.getValidCards(hand, type.split(";"), ai, source);
Integer c = this.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(this.getAmount());
if (sVar.equals("XChoice")) {
c = hand.size();
} else {
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
}
this.setList(ai.getAi().getCardsToDiscard(c, type.split(";"), ability));
} }
return this.getList() != null;
}
/* if (this.getType().equals("Hand"))
* (non-Javadoc) return new PaymentDecision(new ArrayList<Card>(ai.getCardsIn(ZoneType.Hand)));
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility, hand = CardLists.getValidCards(hand, type.split(";"), ai, source);
* forge.Card, forge.card.cost.Cost_Payment) Integer c = this.convertAmount();
*/ if (c == null) {
@Override final String sVar = ability.getSVar(this.getAmount());
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { if (sVar.equals("XChoice")) {
GuiChoose.oneOrNone("Revealed cards:", this.getList()); c = hand.size();
for(Card c: getList()) // should not throw concurrent modification here - no items should be added. } else {
executePayment(ability, c); c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
}
return new PaymentDecision(ai.getAi().getCardsToDiscard(c, type.split(";"), ability));
} }
/* /*
@@ -236,7 +216,6 @@ public class CostReveal extends CostPartWithList {
final Player activator = ability.getActivatingPlayer(); final Player activator = ability.getActivatingPlayer();
final Card source = ability.getSourceCard(); final Card source = ability.getSourceCard();
final String amount = this.getAmount(); final String amount = this.getAmount();
this.resetList();
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
executePayment(ability, source); executePayment(ability, source);
@@ -304,8 +283,7 @@ public class CostReveal extends CostPartWithList {
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/ */
@Override @Override
public void executePayment(SpellAbility ability, Card targetCard) { protected void doPayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
// write code to actually reveal card // write code to actually reveal card
} }

View File

@@ -184,20 +184,6 @@ public class CostSacrifice extends CostPartWithList {
return true; return true;
} }
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
this.reportPaidCardsTo(ability);
for (final Card c : this.getList()) {
executePayment(ability, c);
}
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -219,18 +205,11 @@ public class CostSacrifice extends CostPartWithList {
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
if (source.getController() == ability.getActivatingPlayer() && source.isInPlay()) { if (source.getController() == ability.getActivatingPlayer() && source.isInPlay()) {
if (!GuiDialog.confirm(source, source.getName() + " - Sacrifice?")) return GuiDialog.confirm(source, source.getName() + " - Sacrifice?") && executePayment(ability, source);
return false;
executePayment(ability, source);
return true;
} }
} else if (amount.equals("All")) { } else if (amount.equals("All")) {
this.setList(list); return executePayment(ability, list);
// TODO Ask First
for (final Card card : list) {
executePayment(ability, card);
}
return true;
} else { } else {
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (c == null) { if (c == null) {
@@ -251,50 +230,8 @@ public class CostSacrifice extends CostPartWithList {
return false; return false;
} }
/*
* (non-Javadoc)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { protected void doPayment(SpellAbility ability, Card targetCard) {
this.resetList();
final Player activator = ability.getActivatingPlayer();
if (this.payCostFromSource()) {
this.getList().add(source);
} else if (this.getAmount().equals("All")) {
/*List<Card> typeList = new ArrayList<Card>(activator.getCardsIn(ZoneType.Battlefield));
typeList = CardLists.getValidCards(typeList, this.getType().split(";"), activator, source);
if (activator.hasKeyword("You can't sacrifice creatures to cast spells or activate abilities.")) {
typeList = CardLists.getNotType(typeList, "Creature");
}*/
// Does the AI want to use Sacrifice All?
return false;
} else {
Integer c = this.convertAmount();
if (c == null) {
if (ability.getSVar(this.getAmount()).equals("XChoice")) {
return false;
}
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
this.setList(ComputerUtil.chooseSacrificeType(activator, this.getType(), source, ability.getTargetCard(), c));
if (this.getList() == null) {
return false;
}
}
return true;
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
this.addToList(targetCard);
ability.getActivatingPlayer().getGame().getAction().sacrifice(targetCard, ability); ability.getActivatingPlayer().getGame().getAction().sacrifice(targetCard, ability);
} }
@@ -306,6 +243,37 @@ public class CostSacrifice extends CostPartWithList {
return "Sacrificed"; return "Sacrificed";
} }
/* (non-Javadoc)
* @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
if (this.payCostFromSource()) {
return new PaymentDecision(source);
}
if (this.getAmount().equals("All")) {
/*List<Card> typeList = new ArrayList<Card>(activator.getCardsIn(ZoneType.Battlefield));
typeList = CardLists.getValidCards(typeList, this.getType().split(";"), activator, source);
if (activator.hasKeyword("You can't sacrifice creatures to cast spells or activate abilities.")) {
typeList = CardLists.getNotType(typeList, "Creature");
}*/
// Does the AI want to use Sacrifice All?
return null;
}
Integer c = this.convertAmount();
if (c == null) {
if (ability.getSVar(this.getAmount()).equals("XChoice")) {
return null;
}
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
List<Card> list = ComputerUtil.chooseSacrificeType(ai, this.getType(), source, ability.getTargetCard(), c);
return new PaymentDecision(list);
}
// Inputs // Inputs
} }

View File

@@ -74,14 +74,11 @@ public class CostTap extends CostPart {
return source.isUntapped() && (!source.isSick() || source.hasKeyword("CARDNAME may activate abilities as though it has haste.")); return source.isUntapped() && (!source.isSick() || source.hasKeyword("CARDNAME may activate abilities as though it has haste."));
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) {
source.tap(); source.tap();
} }
@@ -102,15 +99,11 @@ public class CostTap extends CostPart {
return true; return true;
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
return true; return new PaymentDecision(0);
} }
} }

View File

@@ -100,6 +100,8 @@ public class CostTapType extends CostPartWithList {
} }
} }
private final boolean canTapSource;
/** /**
* Instantiates a new cost tap type. * Instantiates a new cost tap type.
* *
@@ -110,8 +112,9 @@ public class CostTapType extends CostPartWithList {
* @param description * @param description
* the description * the description
*/ */
public CostTapType(final String amount, final String type, final String description) { public CostTapType(final String amount, final String type, final String description, boolean costHasTapSource) {
super(amount, type, description); super(amount, type, description);
canTapSource = !costHasTapSource;
} }
@Override @Override
@@ -187,19 +190,6 @@ public class CostTapType extends CostPartWithList {
return true; return true;
} }
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
for (final Card c : this.getList()) {
c.tap();
}
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -229,16 +219,11 @@ public class CostTapType extends CostPartWithList {
return inp.isPaid(); return inp.isPaid();
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
final boolean tap = payment.getCost().hasTapCost();
final String amount = this.getAmount(); final String amount = this.getAmount();
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (c == null) { if (c == null) {
@@ -254,22 +239,22 @@ public class CostTapType extends CostPartWithList {
} }
} }
this.setList(ComputerUtil.chooseTapType(ai, this.getType(), source, tap, c)); List<Card> totap = ComputerUtil.chooseTapType(ai, this.getType(), source, !canTapSource, c);
if (this.getList() == null) {
if (totap == null) {
System.out.println("Couldn't find a valid card to tap for: " + source.getName()); System.out.println("Couldn't find a valid card to tap for: " + source.getName());
return false; return null;
} }
return true; return new PaymentDecision(totap);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/ */
@Override @Override
public void executePayment(SpellAbility ability, Card targetCard) { protected void doPayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
targetCard.tap(); targetCard.tap();
} }

View File

@@ -86,26 +86,6 @@ public class CostUnattach extends CostPartWithList {
return false; return false;
} }
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
Card cardToUnattach = findCardToUnattach(source, (Player) ai, ability);
if (cardToUnattach == null) {
// We really shouldn't be able to get here if there's nothing to unattach
return;
}
Card equippingCard = cardToUnattach.getEquipping().get(0);
cardToUnattach.unEquipCard(equippingCard);
this.addToList(cardToUnattach);
this.reportPaidCardsTo(ability);
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -146,30 +126,29 @@ public class CostUnattach extends CostPartWithList {
return null; return null;
} }
/*
* (non-Javadoc)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) {
this.resetList();
return true;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/ */
@Override @Override
public void executePayment(SpellAbility ability, Card targetCard) { protected void doPayment(SpellAbility ability, Card targetCard) {
targetCard.unEquipCard(targetCard.getEquipping().get(0)); targetCard.unEquipCard(targetCard.getEquipping().get(0));
this.addToList(targetCard);
} }
@Override @Override
public String getHashForList() { public String getHashForList() {
return "Unattached"; return "Unattached";
} }
/* (non-Javadoc)
* @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
Card cardToUnattach = findCardToUnattach(source, (Player) ai, ability);
if (cardToUnattach == null) {
// We really shouldn't be able to get here if there's nothing to unattach
return null;
}
return new PaymentDecision(cardToUnattach.getEquippingCard());
}
} }

View File

@@ -73,17 +73,6 @@ public class CostUntap extends CostPart {
return source.isTapped() && (!source.isSick() || source.hasKeyword("CARDNAME may activate abilities as though it has haste.")); return source.isTapped() && (!source.isSick() || source.hasKeyword("CARDNAME may activate abilities as though it has haste."));
} }
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
source.untap();
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -101,15 +90,19 @@ public class CostUntap extends CostPart {
return true; return true;
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
return true; return new PaymentDecision(0);
}
/* (non-Javadoc)
* @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) {
source.untap();
} }
} }

View File

@@ -49,6 +49,7 @@ public class CostUntapType extends CostPartWithList {
private final CostUntapType untapType; private final CostUntapType untapType;
private static final long serialVersionUID = -7151144318287088542L; private static final long serialVersionUID = -7151144318287088542L;
private int nUntapped = 0; private int nUntapped = 0;
private final SpellAbility sa;
/** /**
@@ -56,13 +57,15 @@ public class CostUntapType extends CostPartWithList {
* @param nCards * @param nCards
* @param cardList * @param cardList
* @param untapType * @param untapType
* @param ability
* @param sa * @param sa
* @param payment * @param payment
*/ */
public InputPayCostUntapY(int nCards, List<Card> cardList, CostUntapType untapType) { public InputPayCostUntapY(int nCards, List<Card> cardList, CostUntapType untapType, SpellAbility ability) {
this.nCards = nCards; this.nCards = nCards;
this.cardList = cardList; this.cardList = cardList;
this.untapType = untapType; this.untapType = untapType;
this.sa = ability;
} }
@Override @Override
@@ -81,14 +84,12 @@ public class CostUntapType extends CostPartWithList {
} }
@Override @Override
public void selectCard(final Card card) { public void selectCard(final Card card) {
Zone zone = Singletons.getModel().getGame().getZoneOf(card); Zone zone = Singletons.getModel().getGame().getZoneOf(card);
if (zone.is(ZoneType.Battlefield) && cardList.contains(card) && card.isTapped()) { if (zone.is(ZoneType.Battlefield) && cardList.contains(card) && card.isTapped()) {
// send in List<Card> for Typing // send in List<Card> for Typing
card.untap(); untapType.executePayment(sa, card);
untapType.addToList(card);
cardList.remove(card); cardList.remove(card);
this.nUntapped++; this.nUntapped++;
@@ -154,17 +155,6 @@ public class CostUntapType extends CostPartWithList {
return sb.toString(); return sb.toString();
} }
/**
* Adds the card to untapped list.
*
* @param c
* the card
*/
public final void addToUntappedList(final Card c) {
this.getList().add(c);
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -205,19 +195,6 @@ public class CostUntapType extends CostPartWithList {
return true; return true;
} }
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility,
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
for (final Card c : this.getList()) {
c.untap();
}
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -246,21 +223,33 @@ public class CostUntapType extends CostPartWithList {
c = AbilityUtils.calculateAmount(source, amount, ability); c = AbilityUtils.calculateAmount(source, amount, ability);
} }
} }
InputPayment inp = new InputPayCostUntapY(c, typeList, this); InputPayment inp = new InputPayCostUntapY(c, typeList, this, ability);
FThreads.setInputAndWait(inp); FThreads.setInputAndWait(inp);
return inp.isPaid(); return inp.isPaid();
} }
/* /* (non-Javadoc)
* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*
* @see
* forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility
* , forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @Override
public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { protected void doPayment(SpellAbility ability, Card targetCard) {
final boolean untap = payment.getCost().hasUntapCost(); targetCard.untap();
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
return "Untapped";
}
/* (non-Javadoc)
* @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) {
boolean untap = false; // payment.getCost().hasUntapCost();
final String amount = this.getAmount(); final String amount = this.getAmount();
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (c == null) { if (c == null) {
@@ -278,32 +267,15 @@ public class CostUntapType extends CostPartWithList {
c = AbilityUtils.calculateAmount(source, amount, ability); c = AbilityUtils.calculateAmount(source, amount, ability);
} }
} }
this.setList(ComputerUtil.chooseUntapType(ai, this.getType(), source, untap, c)); List<Card> list = ComputerUtil.chooseUntapType(ai, this.getType(), source, untap, c);
if (this.getList() == null) { if (list == null) {
System.out.println("Couldn't find a valid card to untap for: " + source.getName()); System.out.println("Couldn't find a valid card to untap for: " + source.getName());
return false; return null;
} }
return true; return new PaymentDecision(list);
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
targetCard.untap();
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
return "Untapped";
} }
// Inputs // Inputs

View File

@@ -0,0 +1,35 @@
package forge.card.cost;
import java.util.ArrayList;
import java.util.List;
import forge.Card;
/**
* TODO: Write javadoc for this type.
*
*/
public class PaymentDecision {
public int c = 0;
public final List<Card> cards = new ArrayList<Card>();
public PaymentDecision(int cnt) {
c = cnt;
}
public PaymentDecision(List<Card> chosen) {
cards.addAll(chosen);
}
public PaymentDecision(Card chosen) {
cards.add(chosen);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("Payment Decision: %d, %s", c, cards);
}
}

View File

@@ -28,9 +28,13 @@ import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.CardPredicates.Presets; import forge.CardPredicates.Presets;
import forge.CounterType; import forge.CounterType;
import forge.FThreads;
import forge.GameEntity; import forge.GameEntity;
import forge.Singletons; import forge.Singletons;
import forge.control.input.InputBase; import forge.control.input.InputBase;
import forge.control.input.InputSelectCards;
import forge.control.input.InputSelectCardsFromList;
import forge.control.input.InputSyncronizedBase;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
import forge.game.player.Player; import forge.game.player.Player;
@@ -106,20 +110,28 @@ public class Untap extends Phase {
return true; return true;
} }
public static final Predicate<Card> CANUNTAP = new Predicate<Card>() {
@Override
public boolean apply(Card c) {
return Untap.canUntap(c);
}
};
/** /**
* <p> * <p>
* doUntap. * doUntap.
* </p> * </p>
*/ */
private void doUntap() { private void doUntap() {
final Player player = Singletons.getModel().getGame().getPhaseHandler().getPlayerTurn(); final Player player = game.getPhaseHandler().getPlayerTurn();
final Predicate<Card> tappedCanUntap = Predicates.and(Presets.TAPPED, Presets.CANUNTAP); final Predicate<Card> tappedCanUntap = Predicates.and(Presets.TAPPED, CANUNTAP);
List<Card> list = new ArrayList<Card>(player.getCardsIn(ZoneType.Battlefield)); List<Card> list = new ArrayList<Card>(player.getCardsIn(ZoneType.Battlefield));
List<Card> bounceList = CardLists.getKeyword(list, "During your next untap step, as you untap your permanents, return CARDNAME to its owner's hand."); List<Card> bounceList = CardLists.getKeyword(list, "During your next untap step, as you untap your permanents, return CARDNAME to its owner's hand.");
for (final Card c : bounceList) { for (final Card c : bounceList) {
Singletons.getModel().getGame().getAction().moveToHand(c); game.getAction().moveToHand(c);
} }
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, new Predicate<Card>() {
@@ -128,16 +140,13 @@ public class Untap extends Phase {
if (!Untap.canUntap(c)) { if (!Untap.canUntap(c)) {
return false; return false;
} }
if (Untap.canOnlyUntapOneLand() && c.isLand()) { if (canOnlyUntapOneLand() && c.isLand()) {
return false; return false;
} }
if (c.isArtifact() if (c.isArtifact() && (game.isCardInPlay("Damping Field") || game.isCardInPlay("Imi Statue"))) {
&& (Singletons.getModel().getGame().isCardInPlay("Damping Field") || Singletons.getModel().getGame().isCardInPlay("Imi Statue"))) {
return false; return false;
} }
if (c.isCreature() if (c.isCreature() && (game.isCardInPlay("Smoke") || game.isCardInPlay("Stoic Angel") || game.isCardInPlay("Intruder Alarm"))) {
&& (Singletons.getModel().getGame().isCardInPlay("Smoke") || Singletons.getModel().getGame().isCardInPlay("Stoic Angel")
|| Singletons.getModel().getGame().isCardInPlay("Intruder Alarm"))) {
return false; return false;
} }
return true; return true;
@@ -181,7 +190,7 @@ public class Untap extends Phase {
} }
} }
} }
} else if ((c.getCounters(CounterType.WIND) > 0) && Singletons.getModel().getGame().isCardInPlay("Freyalise's Winds")) { } else if ((c.getCounters(CounterType.WIND) > 0) && game.isCardInPlay("Freyalise's Winds")) {
// remove a WIND counter instead of untapping // remove a WIND counter instead of untapping
c.subtractCounter(CounterType.WIND, 1); c.subtractCounter(CounterType.WIND, 1);
} else { } else {
@@ -190,7 +199,7 @@ public class Untap extends Phase {
} }
// other players untapping during your untap phase // other players untapping during your untap phase
final List<Card> cardsWithKW = CardLists.getKeyword(Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield), final List<Card> cardsWithKW = CardLists.getKeyword(game.getCardsIn(ZoneType.Battlefield),
"CARDNAME untaps during each other player's untap step."); "CARDNAME untaps during each other player's untap step.");
final List<Player> otherPlayers = player.getOpponents(); final List<Player> otherPlayers = player.getOpponents();
otherPlayers.addAll(player.getAllies()); otherPlayers.addAll(player.getAllies());
@@ -200,123 +209,49 @@ public class Untap extends Phase {
} }
// end other players untapping during your untap phase // end other players untapping during your untap phase
if (Untap.canOnlyUntapOneLand()) { if (canOnlyUntapOneLand()) {
if (player.isComputer()) { final List<Card> landList = CardLists.filter(player.getLandsInPlay(), tappedCanUntap);
// search for lands the computer has and only untap 1
List<Card> landList = player.getLandsInPlay(); if (!landList.isEmpty()) {
if (player.isComputer()) {
landList = CardLists.filter(landList, tappedCanUntap); // search for lands the computer has and only untap 1
if (landList.size() > 0) {
landList.get(0).untap(); landList.get(0).untap();
} } else {
} else { final InputSelectCards target = new InputSelectCardsFromList(1,1, landList);
final InputBase target = new InputBase() { target.setMessage("Select one tapped land to untap");
private static final long serialVersionUID = 6653677835629939465L; FThreads.setInputAndWait(target);
if( !target.hasCancelled() && !target.getSelected().isEmpty())
@Override target.getSelected().get(0).untap();
public void showMessage() {
CMatchUI.SINGLETON_INSTANCE.showMessage("Select one tapped land to untap");
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
this.stop();
}
@Override
public void selectCard(final Card c) {
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
if (c.isLand() && zone.is(ZoneType.Battlefield) && c.isTapped() && Untap.canUntap(c)) {
c.untap();
this.stop();
}
} // selectCard()
}; // Input
List<Card> landList = player.getLandsInPlay();
landList = CardLists.filter(landList, tappedCanUntap);
if (landList.size() > 0) {
Singletons.getModel().getMatch().getInput().setInput(target);
} }
} }
} }
if (Singletons.getModel().getGame().isCardInPlay("Damping Field") || Singletons.getModel().getGame().isCardInPlay("Imi Statue")) { if (game.isCardInPlay("Damping Field") || game.isCardInPlay("Imi Statue")) {
final Player turnOwner = Singletons.getModel().getGame().getPhaseHandler().getPlayerTurn(); final Player turnOwner = game.getPhaseHandler().getPlayerTurn();
if (turnOwner.isComputer()) { final List<Card> artList = CardLists.filter(turnOwner.getCardsIn(ZoneType.Battlefield), Presets.ARTIFACTS, tappedCanUntap);
List<Card> artList = new ArrayList<Card>(turnOwner.getCardsIn(ZoneType.Battlefield));
artList = CardLists.filter(artList, Presets.ARTIFACTS); if (!artList.isEmpty()) {
artList = CardLists.filter(artList, tappedCanUntap); if (turnOwner.isComputer()) {
if (artList.size() > 0) {
ComputerUtilCard.getBestArtifactAI(artList).untap(); ComputerUtilCard.getBestArtifactAI(artList).untap();
} } else {
} else { final InputSelectCards target = new InputSelectCardsFromList(1,1, artList);
final InputBase target = new InputBase() { target.setMessage("Select one tapped artifact to untap");
private static final long serialVersionUID = 5555427219659889707L; FThreads.setInputAndWait(target);
if( !target.hasCancelled() && !target.getSelected().isEmpty())
@Override target.getSelected().get(0).untap();
public void showMessage() {
CMatchUI.SINGLETON_INSTANCE.showMessage("Select one tapped artifact to untap");
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
this.stop();
}
@Override
public void selectCard(final Card c) {
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
if (c.isArtifact() && zone.is(ZoneType.Battlefield) && c.getController().isHuman()
&& Untap.canUntap(c)) {
c.untap();
this.stop();
}
} // selectCard()
}; // Input
List<Card> artList = new ArrayList<Card>(turnOwner.getCardsIn(ZoneType.Battlefield));
artList = CardLists.filter(artList, Presets.ARTIFACTS);
artList = CardLists.filter(artList, tappedCanUntap);
if (artList.size() > 0) {
Singletons.getModel().getMatch().getInput().setInput(target);
} }
} }
} }
if ((Singletons.getModel().getGame().isCardInPlay("Smoke") || Singletons.getModel().getGame().isCardInPlay("Stoic Angel"))) { if ((game.isCardInPlay("Smoke") || game.isCardInPlay("Stoic Angel"))) {
if (player.isComputer()) { final List<Card> creatures = CardLists.filter(player.getCreaturesInPlay(), tappedCanUntap);
List<Card> creatures = player.getCreaturesInPlay(); if (!creatures.isEmpty()) {
creatures = CardLists.filter(creatures, tappedCanUntap); if (player.isComputer()) {
if (creatures.size() > 0) {
creatures.get(0).untap(); creatures.get(0).untap();
} } else {
} else { final InputSelectCards target = new InputSelectCardsFromList(1, 1, creatures);
final InputBase target = new InputBase() { target.setMessage("Select one creature to untap");
private static final long serialVersionUID = 5555427219659889707L; FThreads.setInputAndWait(target);
if( !target.hasCancelled() && !target.getSelected().isEmpty())
@Override target.getSelected().get(0).untap();
public void showMessage() {
CMatchUI.SINGLETON_INSTANCE.showMessage("Select one creature to untap");
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
this.stop();
}
@Override
public void selectCard(final Card c) {
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
if (c.isCreature() && zone.is(ZoneType.Battlefield) && c.getController().isHuman()
&& Untap.canUntap(c)) {
c.untap();
this.stop();
}
} // selectCard()
}; // Input
final List<Card> creatures = CardLists.filter(player.getCreaturesInPlay(), tappedCanUntap);
if (creatures.size() > 0) {
Singletons.getModel().getMatch().getInput().setInput(target);
} }
} }
} }
@@ -334,11 +269,10 @@ public class Untap extends Phase {
game.getStack().chooseOrderOfSimultaneousStackEntryAll(); game.getStack().chooseOrderOfSimultaneousStackEntryAll();
} // end doUntap } // end doUntap
private static boolean canOnlyUntapOneLand() { private boolean canOnlyUntapOneLand() {
// Winter Orb was given errata so it no longer matters if it's tapped or // Winter Orb was given errata so it no longer matters if it's tapped or
// not // not
if (Singletons.getModel().getGame().isCardInPlay("Winter Orb") if (game.isCardInPlay("Winter Orb") || game.getPhaseHandler().getPlayerTurn().isCardInPlay("Mungha Wurm")) {
|| Singletons.getModel().getGame().getPhaseHandler().getPlayerTurn().isCardInPlay("Mungha Wurm")) {
return true; return true;
} }

View File

@@ -19,6 +19,7 @@ package forge.gui;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import java.util.concurrent.atomic.AtomicReference;
import forge.Card; import forge.Card;
import forge.FThreads; import forge.FThreads;
@@ -39,7 +40,7 @@ import forge.game.player.Player;
public class InputProxy implements Observer { public class InputProxy implements Observer {
/** The input. */ /** The input. */
private Input input; private AtomicReference<Input> input = new AtomicReference<Input>();
private MatchController match = null; private MatchController match = null;
public void setMatch(MatchController matchController) { public void setMatch(MatchController matchController) {
@@ -48,7 +49,7 @@ public class InputProxy implements Observer {
@Override @Override
public final synchronized void update(final Observable observable, final Object obj) { public final synchronized void update(final Observable observable, final Object obj) {
this.input = null; this.input.set(null);
final GameState game = match.getCurrentGame(); final GameState game = match.getCurrentGame();
final PhaseHandler ph = game.getPhaseHandler(); final PhaseHandler ph = game.getPhaseHandler();
@@ -68,7 +69,7 @@ public class InputProxy implements Observer {
//System.out.printf(" input is %s \t stack = %s%n", nextInput == null ? "null" : nextInput.getClass().getSimpleName(), match.getInput().printInputStack()); //System.out.printf(" input is %s \t stack = %s%n", nextInput == null ? "null" : nextInput.getClass().getSimpleName(), match.getInput().printInputStack());
if (nextInput != null) { if (nextInput != null) {
this.input = nextInput; this.input.set(nextInput);
FThreads.invokeInEDT(new Runnable() { @Override public void run() { nextInput.showMessage(); } }); FThreads.invokeInEDT(new Runnable() { @Override public void run() { nextInput.showMessage(); } });
} else if (!ph.isPlayerPriorityAllowed()) { } else if (!ph.isPlayerPriorityAllowed()) {
ph.getPriorityPlayer().getController().passPriority(); ph.getPriorityPlayer().getController().passPriority();
@@ -80,8 +81,9 @@ public class InputProxy implements Observer {
* </p> * </p>
*/ */
public final void selectButtonOK() { public final void selectButtonOK() {
if ( null == input ) return; Input inp = getInput();
input.selectButtonOK(); if ( null != inp )
inp.selectButtonOK();
} }
/** /**
@@ -90,8 +92,9 @@ public class InputProxy implements Observer {
* </p> * </p>
*/ */
public final void selectButtonCancel() { public final void selectButtonCancel() {
if ( null == input ) return; Input inp = getInput();
input.selectButtonCancel(); if ( null != inp )
inp.selectButtonCancel();
} }
/** /**
@@ -103,8 +106,9 @@ public class InputProxy implements Observer {
* a {@link forge.game.player.Player} object. * a {@link forge.game.player.Player} object.
*/ */
public final void selectPlayer(final Player player) { public final void selectPlayer(final Player player) {
if ( null == input ) return; Input inp = getInput();
input.selectPlayer(player); if ( null != inp )
inp.selectPlayer(player);
} }
/** /**
@@ -118,19 +122,20 @@ public class InputProxy implements Observer {
* a {@link forge.game.zone.PlayerZone} object. * a {@link forge.game.zone.PlayerZone} object.
*/ */
public final void selectCard(final Card card) { public final void selectCard(final Card card) {
if ( null == input ) return; Input inp = getInput();
input.selectCard(card); if ( null != inp )
inp.selectCard(card);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final String toString() { public final String toString() {
if ( null == input ) return "(null)"; Input inp = getInput();
return this.input.toString(); return null == inp ? "(null)" : inp.toString();
} }
/** @return {@link forge.gui.InputProxy.InputBase} */ /** @return {@link forge.gui.InputProxy.InputBase} */
public Input getInput() { public Input getInput() {
return this.input; return this.input.get();
} }
} }