From 98a539f23af68b05f196f1f4fa2a28fd63975e10 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Thu, 28 Mar 2013 08:14:13 +0000 Subject: [PATCH] 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. --- .gitattributes | 1 + src/main/java/forge/Card.java | 2 +- src/main/java/forge/CardLists.java | 4 + src/main/java/forge/FThreads.java | 8 +- .../java/forge/card/ability/ai/DrawAi.java | 7 +- src/main/java/forge/card/cost/Cost.java | 21 +- src/main/java/forge/card/cost/CostDamage.java | 38 +-- .../java/forge/card/cost/CostDiscard.java | 220 ++++++++---------- src/main/java/forge/card/cost/CostExile.java | 124 +++++----- .../java/forge/card/cost/CostGainLife.java | 40 +--- src/main/java/forge/card/cost/CostMill.java | 89 +++---- src/main/java/forge/card/cost/CostPart.java | 13 +- .../java/forge/card/cost/CostPartMana.java | 21 +- .../forge/card/cost/CostPartWithList.java | 54 ++--- .../java/forge/card/cost/CostPayLife.java | 57 ++--- .../java/forge/card/cost/CostPayment.java | 13 +- .../java/forge/card/cost/CostPutCounter.java | 71 +++--- .../forge/card/cost/CostRemoveCounter.java | 158 ++++++------- src/main/java/forge/card/cost/CostReturn.java | 51 ++-- src/main/java/forge/card/cost/CostReveal.java | 66 ++---- .../java/forge/card/cost/CostSacrifice.java | 102 +++----- src/main/java/forge/card/cost/CostTap.java | 21 +- .../java/forge/card/cost/CostTapType.java | 41 ++-- .../java/forge/card/cost/CostUnattach.java | 49 ++-- src/main/java/forge/card/cost/CostUntap.java | 31 +-- .../java/forge/card/cost/CostUntapType.java | 94 +++----- .../java/forge/card/cost/PaymentDecision.java | 35 +++ src/main/java/forge/game/phase/Untap.java | 178 +++++--------- src/main/java/forge/gui/InputProxy.java | 33 +-- 29 files changed, 652 insertions(+), 990 deletions(-) create mode 100644 src/main/java/forge/card/cost/PaymentDecision.java diff --git a/.gitattributes b/.gitattributes index a73561f8cb3..0e2c0ae73b0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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/CostUtil.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/mana/IParserManaCost.java -text src/main/java/forge/card/mana/Mana.java svneol=native#text/plain diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index f82b936fee5..2574aa942bd 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -8993,7 +8993,7 @@ public class Card extends GameEntity implements Comparable { if (getController() == null) { return false; } - return getController().getCardsIn(ZoneType.Battlefield).contains(this); + return getController().getZone(ZoneType.Battlefield).contains(this); } public void onCleanupPhase(final Player turn) { diff --git a/src/main/java/forge/CardLists.java b/src/main/java/forge/CardLists.java index fe8e16c4cff..428e4508b2a 100644 --- a/src/main/java/forge/CardLists.java +++ b/src/main/java/forge/CardLists.java @@ -254,6 +254,10 @@ public class CardLists { return Lists.newArrayList(Iterables.filter(cardList, filt)); } + public static List filter(Iterable cardList, Predicate f1, Predicate f2) { + return Lists.newArrayList(Iterables.filter(Iterables.filter(cardList, f1), f2)); + } + public static List createCardList(Card c) { List res = new ArrayList(); res.add(c); diff --git a/src/main/java/forge/FThreads.java b/src/main/java/forge/FThreads.java index 7e0f0bb9caa..1c0a0aaa86a 100644 --- a/src/main/java/forge/FThreads.java +++ b/src/main/java/forge/FThreads.java @@ -21,11 +21,11 @@ public class FThreads { private FThreads() { } // no instances supposed - private final static ExecutorService threadPool = Executors.newCachedThreadPool(); - private static ExecutorService getCachedPool() { return threadPool; } - private final static ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(1); + private final static ExecutorService cachedPool = Executors.newCachedThreadPool(); + private static ExecutorService getCachedPool() { return cachedPool; } + private final static ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2); 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 public final static ExecutorService getComputingPool(float loadFactor) { return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor))); diff --git a/src/main/java/forge/card/ability/ai/DrawAi.java b/src/main/java/forge/card/ability/ai/DrawAi.java index c77833996f0..decde1c2179 100644 --- a/src/main/java/forge/card/ability/ai/DrawAi.java +++ b/src/main/java/forge/card/ability/ai/DrawAi.java @@ -28,6 +28,7 @@ import forge.card.ability.SpellAbilityAi; import forge.card.cost.Cost; import forge.card.cost.CostDiscard; import forge.card.cost.CostPart; +import forge.card.cost.PaymentDecision; import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; @@ -70,8 +71,10 @@ public class DrawAi extends SpellAbilityAi { for (final CostPart part : abCost.getCostParts()) { if (part instanceof CostDiscard) { CostDiscard cd = (CostDiscard) part; - cd.decideAIPayment((AIPlayer) ai, sa, sa.getSourceCard(), null); - for (Card discard : cd.getList()) { + PaymentDecision decision = cd.decideAIPayment((AIPlayer) ai, sa, sa.getSourceCard()); + if ( null == decision ) + return false; + for (Card discard : decision.cards) { if (!ComputerUtil.isWorseThanDraw(ai, discard)) { return false; } diff --git a/src/main/java/forge/card/cost/Cost.java b/src/main/java/forge/card/cost/Cost.java index f88f42e08b3..a6630fb1ded 100644 --- a/src/main/java/forge/card/cost/Cost.java +++ b/src/main/java/forge/card/cost/Cost.java @@ -145,13 +145,22 @@ public class Cost { StringBuilder manaParts = new StringBuilder(); 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) { if( "XCantBe0".equals(part) ) xCantBe0 = true; else if ( "X".equals(part) ) amountX++; else { - CostPart cp = parseCostPart(part); + CostPart cp = parseCostPart(part, tapCost, untapCost); if ( null != cp ) this.costParts.add(cp); else @@ -175,22 +184,20 @@ public class Cost { if( cp instanceof CostUntap ) { costParts.remove(iCp); costParts.add(0, cp); - untapCost = true; } if( cp instanceof CostTap ) { costParts.remove(iCp); 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<")) { final String[] splitStr = abCostParse(parse, 3); 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<")) { @@ -337,9 +344,9 @@ public class Cost { * @return an array of {@link java.lang.String} objects. */ 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); - String str = parse.substring(startPos + 1, endPos); + String str = parse.substring(startPos, endPos); final String[] splitStr = str.split("/", numParse); return splitStr; diff --git a/src/main/java/forge/card/cost/CostDamage.java b/src/main/java/forge/card/cost/CostDamage.java index 7f290b3705d..f26324df966 100644 --- a/src/main/java/forge/card/cost/CostDamage.java +++ b/src/main/java/forge/card/cost/CostDamage.java @@ -29,33 +29,7 @@ import forge.gui.GuiDialog; * The Class CostPayLife. */ 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) { this.setAmount(amount); } @@ -91,8 +65,8 @@ public class CostDamage extends CostPart { * 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) { - ability.getActivatingPlayer().addDamage(this.getLastPaidAmount(), source); + public final void payAI(final PaymentDecision decision, final AIPlayer ai, SpellAbility ability, Card source) { + ability.getActivatingPlayer().addDamage(decision.c, source); } /* @@ -139,21 +113,19 @@ public class CostDamage extends CostPart { * , forge.Card, forge.card.cost.Cost_Payment) */ @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(); if (c == null) { final String sVar = ability.getSVar(this.getAmount()); // Generalize this if (sVar.equals("XChoice")) { - return false; + return null; // cannot pay } else { c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); } } - // activator.payLife(c, null); - this.setLastPaidAmount(c); - return true; + return new PaymentDecision(c); } } diff --git a/src/main/java/forge/card/cost/CostDiscard.java b/src/main/java/forge/card/cost/CostDiscard.java index 13f38299899..5d07264d3ac 100644 --- a/src/main/java/forge/card/cost/CostDiscard.java +++ b/src/main/java/forge/card/cost/CostDiscard.java @@ -33,6 +33,7 @@ import forge.game.GameState; import forge.game.player.AIPlayer; import forge.game.player.Player; import forge.game.zone.ZoneType; +import forge.util.Aggregates; /** * The Class CostDiscard. @@ -145,19 +146,6 @@ public class CostDiscard extends CostPartWithList { 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) * @@ -172,149 +160,131 @@ public class CostDiscard extends CostPartWithList { List handList = new ArrayList(activator.getCardsIn(ZoneType.Hand)); String discardType = this.getType(); final String amount = this.getAmount(); - this.resetList(); if (this.payCostFromSource()) { - if (!handList.contains(source)) { - return false; - } - executePayment(ability, source); - return true; - //this.addToList(source); - } else if (discardType.equals("Hand")) { - this.setList(handList); - activator.discardHand(ability); - return true; - } else if (discardType.equals("LastDrawn")) { + return handList.contains(source) && executePayment(ability, source); + } + + if (discardType.equals("Hand")) { + return executePayment(ability, handList); + } + + if (discardType.equals("LastDrawn")) { final Card lastDrawn = activator.getLastDrawnCard(); - if (!handList.contains(lastDrawn)) { - return false; + return handList.contains(lastDrawn) && executePayment(ability, lastDrawn); + } + + 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 { - 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); - } - } - - 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 landList2 = handList; - if (sameName) { - handList = CardLists.filter(handList, new Predicate() { - @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; + 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 landList2 = handList; + if (sameName) { + handList = CardLists.filter(handList, new Predicate() { + @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) - * - * @see - * forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility - * , forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @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 List hand = ai.getCardsIn(ZoneType.Hand); - this.resetList(); if (type.equals("LastDrawn")) { if (!hand.contains(ai.getLastDrawnCard())) { - return false; + return null; } - this.addToList(ai.getLastDrawnCard()); + return new PaymentDecision(ai.getLastDrawnCard()); } else if (this.payCostFromSource()) { if (!hand.contains(source)) { - return false; + return null; } - this.addToList(source); + return new PaymentDecision(source); } else if (type.equals("Hand")) { - this.getList().addAll(hand); + return new PaymentDecision(hand); } - else { - if (type.contains("WithSameName")) { - return false; - } - Integer c = this.convertAmount(); - if (c == null) { - final String sVar = ability.getSVar(this.getAmount()); - if (sVar.equals("XChoice")) { - return false; - } - c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); - } - - if (type.equals("Random")) { - this.setList(CardLists.getRandomSubList(hand, c)); - } else { - this.setList(ai.getAi().getCardsToDiscard(c, type.split(";"), ability)); - } + if (type.contains("WithSameName")) { + return null; + } + Integer c = this.convertAmount(); + if (c == null) { + final String sVar = ability.getSVar(this.getAmount()); + if (sVar.equals("XChoice")) { + return null; + } + c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); + } + + if (type.equals("Random")) { + return new PaymentDecision(CardLists.getRandomSubList(hand, c)); + } else { + return new PaymentDecision(ai.getAi().getCardsToDiscard(c, type.split(";"), ability)); } - return this.getList() != null; } /* (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); + protected void doPayment(SpellAbility ability, Card targetCard) { targetCard.getController().discard(targetCard, ability); } @@ -326,6 +296,14 @@ public class CostDiscard extends CostPartWithList { 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 } diff --git a/src/main/java/forge/card/cost/CostExile.java b/src/main/java/forge/card/cost/CostExile.java index 9f1e1fef2c0..4ca0c1d47b4 100644 --- a/src/main/java/forge/card/cost/CostExile.java +++ b/src/main/java/forge/card/cost/CostExile.java @@ -505,28 +505,6 @@ public class CostExile extends CostPartWithList { 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 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) * @@ -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(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 @@ -683,8 +617,7 @@ public class CostExile extends CostPartWithList { * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ @Override - public void executePayment(SpellAbility ability, Card targetCard) { - addToList(targetCard); + protected void doPayment(SpellAbility ability, Card targetCard) { ability.getActivatingPlayer().getGame().getAction().exile(targetCard); } @@ -696,4 +629,57 @@ public class CostExile extends CostPartWithList { // TODO Auto-generated method stub 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(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 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); + } + } + } + } + } } diff --git a/src/main/java/forge/card/cost/CostGainLife.java b/src/main/java/forge/card/cost/CostGainLife.java index f48fb1508ab..dd4350e7c75 100644 --- a/src/main/java/forge/card/cost/CostGainLife.java +++ b/src/main/java/forge/card/cost/CostGainLife.java @@ -33,26 +33,6 @@ import forge.gui.GuiChoose; */ public class CostGainLife extends CostPart { 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. @@ -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) */ @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; - for (final Player opp : getPotentialTargets(game, ai, source)) { + for (final Player opp : getPotentialTargets(ai.getGame(), ai, source)) { if (opp.canGainLife() && playersLeft > 0) { 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) */ @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(); if (c == null) { final String sVar = ability.getSVar(this.getAmount()); // Generalize this if (sVar.equals("XChoice")) { - return false; + return null; } else { c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); } @@ -212,9 +190,9 @@ public class CostGainLife extends CostPart { } if (oppsThatCanGainLife.size() == 0) { - return false; + return null; } - this.setLastPaidAmount(c); - return true; + + return new PaymentDecision(c); } } diff --git a/src/main/java/forge/card/cost/CostMill.java b/src/main/java/forge/card/cost/CostMill.java index 54e1da824bf..68b55dd44f9 100644 --- a/src/main/java/forge/card/cost/CostMill.java +++ b/src/main/java/forge/card/cost/CostMill.java @@ -17,12 +17,9 @@ */ package forge.card.cost; -import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import forge.Card; -import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; import forge.game.GameState; @@ -49,6 +46,14 @@ public class CostMill extends CostPartWithList { this.setAmount(amount); } + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#getHashForList() + */ + @Override + public String getHashForList() { + return "Milled"; + } + /* * (non-Javadoc) * @@ -74,50 +79,6 @@ public class CostMill extends CostPartWithList { 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(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) * @@ -143,21 +104,13 @@ public class CostMill extends CostPartWithList { } final List 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(); sb.append("Mill ").append(c).append(" cards from your library?"); if ( false == GuiDialog.confirm(source, sb.toString()) ) return false; - - this.resetList(); - final Iterator itr = list.iterator(); - while (itr.hasNext()) { - final Card card = itr.next(); + + for(final Card card : list) { // this list is a copy, no exception expected executePayment(ability, card); } return true; @@ -193,17 +146,27 @@ public class CostMill extends CostPartWithList { * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ @Override - public void executePayment(SpellAbility ability, Card targetCard) { - this.addToList(targetCard); + protected void doPayment(SpellAbility ability, Card targetCard) { ability.getActivatingPlayer().getGame().getAction().moveToGraveyard(targetCard); } /* (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 - public String getHashForList() { - return "Milled"; + public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) { + Integer c = this.convertAmount(); + if (c == null) { + final String sVar = ability.getSVar(this.getAmount()); + // Generalize this + if (sVar.equals("XChoice")) { + return null; + } + + c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); + } + + List topLib = ai.getCardsIn(ZoneType.Library, c); + return topLib.size() < c ? null : new PaymentDecision(topLib); } - } diff --git a/src/main/java/forge/card/cost/CostPart.java b/src/main/java/forge/card/cost/CostPart.java index 0c9edb55ed6..9e4bada7f7a 100644 --- a/src/main/java/forge/card/cost/CostPart.java +++ b/src/main/java/forge/card/cost/CostPart.java @@ -28,17 +28,8 @@ import forge.game.player.Player; * The Class CostPart. */ public abstract class CostPart { - - /** The optional. */ - // private boolean optional = false; - - /** The amount. */ private String amount = "1"; - - /** The type. */ private final String type; - - /** The type description. */ private final String typeDescription; /** @@ -171,7 +162,7 @@ public abstract class CostPart { * {@link forge.card.cost.CostPayment} * @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. @@ -186,7 +177,7 @@ public abstract class CostPart { * {@link forge.card.cost.CostPayment} * @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. diff --git a/src/main/java/forge/card/cost/CostPartMana.java b/src/main/java/forge/card/cost/CostPartMana.java index 948c5012f6b..8aa0a52ee65 100644 --- a/src/main/java/forge/card/cost/CostPartMana.java +++ b/src/main/java/forge/card/cost/CostPartMana.java @@ -187,14 +187,11 @@ public class CostPartMana extends CostPart { return true; } - /* - * (non-Javadoc) - * - * @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @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); } @@ -235,16 +232,12 @@ public class CostPartMana extends CostPart { } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility - * , forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @Override - public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { - return true; + public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) { + return new PaymentDecision(0); } // Inputs diff --git a/src/main/java/forge/card/cost/CostPartWithList.java b/src/main/java/forge/card/cost/CostPartWithList.java index e9682e08ce8..7c09df82bf5 100644 --- a/src/main/java/forge/card/cost/CostPartWithList.java +++ b/src/main/java/forge/card/cost/CostPartWithList.java @@ -17,13 +17,13 @@ */ package forge.card.cost; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; -import java.util.Set; - import forge.Card; import forge.CardUtil; import forge.card.spellability.SpellAbility; +import forge.game.player.AIPlayer; /** * The Class CostPartWithList. @@ -31,7 +31,7 @@ import forge.card.spellability.SpellAbility; public abstract class CostPartWithList extends CostPart { /** The list. */ - private Set list = new HashSet(); + private final List list = new ArrayList(); // set is here because executePayment() adds card to list, while ai's decide payment does the same thing. // set allows to avoid duplication @@ -40,21 +40,10 @@ public abstract class CostPartWithList extends CostPart { * * @return the list */ - public final Set getList() { + public final List getList() { return this.list; } - /** - * Sets the list. - * - * @param setList - * the new list - */ - public final void setList(final List setList) { - this.list.clear(); - list.addAll(setList); - } - /** * Reset list. */ @@ -62,13 +51,7 @@ public abstract class CostPartWithList extends CostPart { this.list.clear(); } - /** - * Adds the to list. - * - * @param c - * the c - */ - public final void addToList(final Card c) { + protected final void addToList(final Card 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) { 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 targetCards) { + for(Card c: targetCards) + executePayment(ability, c); + return true; + } + + protected abstract void doPayment(SpellAbility ability, Card targetCard); /** * TODO: Write javadoc for this method. * @return */ public abstract String getHashForList(); + + @Override + public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) { + executePayment(ability, decision.cards); + reportPaidCardsTo(ability); + } + } diff --git a/src/main/java/forge/card/cost/CostPayLife.java b/src/main/java/forge/card/cost/CostPayLife.java index 391f3de9af7..6f1c38036c7 100644 --- a/src/main/java/forge/card/cost/CostPayLife.java +++ b/src/main/java/forge/card/cost/CostPayLife.java @@ -29,27 +29,8 @@ import forge.gui.GuiDialog; * The Class CostPayLife. */ public class CostPayLife 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; - } - + int paidAmount = 0; + /** * Instantiates a new cost pay life. * @@ -80,7 +61,7 @@ public class CostPayLife extends CostPart { @Override public final void refund(final Card source) { // 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; } - /* - * (non-Javadoc) - * - * @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @Override - public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { - ai.payLife(this.getLastPaidAmount(), null); + public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) { + // TODO Auto-generated method stub + paidAmount = decision.c; + ai.payLife(paidAmount, null); } /* @@ -153,34 +133,29 @@ public class CostPayLife extends CostPart { } else { return false; } + paidAmount = c; return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility - * , forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @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(); if (c == null) { final String sVar = ability.getSVar(this.getAmount()); // Generalize this if (sVar.equals("XChoice")) { - return false; + return null; } else { c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); } } if (!ai.canPayLife(c)) { - return false; + return null; } // activator.payLife(c, null); - this.setLastPaidAmount(c); - return true; + return new PaymentDecision(c); } } diff --git a/src/main/java/forge/card/cost/CostPayment.java b/src/main/java/forge/card/cost/CostPayment.java index 6017dbde1f1..1c08e5d61ac 100644 --- a/src/main/java/forge/card/cost/CostPayment.java +++ b/src/main/java/forge/card/cost/CostPayment.java @@ -18,8 +18,9 @@ package forge.card.cost; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; - +import java.util.Map; import forge.Card; import forge.card.spellability.SpellAbility; import forge.game.GameState; @@ -181,15 +182,17 @@ public class CostPayment { parts.add(new CostPartMana("0", 0, false)); } + Map, PaymentDecision> decisions = new HashMap, PaymentDecision>(); + // Set all of the decisions before attempting to pay anything for (final CostPart part : parts) { - if (!part.decideAIPayment(ai, this.ability, source, this)) { - return false; - } + PaymentDecision decision = part.decideAIPayment(ai, this.ability, source); + if ( null == decision ) return false; + decisions.put(part.getClass(), decision); } 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; } diff --git a/src/main/java/forge/card/cost/CostPutCounter.java b/src/main/java/forge/card/cost/CostPutCounter.java index 4d2aeb8b6c5..a20dbc02452 100644 --- a/src/main/java/forge/card/cost/CostPutCounter.java +++ b/src/main/java/forge/card/cost/CostPutCounter.java @@ -217,16 +217,14 @@ public class CostPutCounter extends CostPartWithList { * 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) { + public void payAI(PaymentDecision decision, AIPlayer ai, SpellAbility ability, Card source) { Integer c = getNumberOfCounters(ability); if (this.payCostFromSource()) { executePayment(ability, source, c); } else { // Put counter on chosen card - for (final Card card : this.getList()) { - executePayment(ability, card); - } + executePayment(ability, decision.cards); } } @@ -260,44 +258,23 @@ public class CostPutCounter extends CostPartWithList { 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 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) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ @Override - public void executePayment(SpellAbility ability, Card targetCard){ - executePayment(ability, targetCard, 1); + protected void doPayment(SpellAbility ability, Card targetCard){ + targetCard.addCounter(this.getCounter(), 1, false); } - public void executePayment(SpellAbility ability, Card targetCard, int c) { - targetCard.addCounter(this.getCounter(), c, false); - this.addToList(targetCard); + protected void executePayment(SpellAbility ability, Card targetCard, int c) { + CounterType counterType = this.getCounter(); + 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() { 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 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); + } } diff --git a/src/main/java/forge/card/cost/CostRemoveCounter.java b/src/main/java/forge/card/cost/CostRemoveCounter.java index c802168f551..592e53dd1d9 100644 --- a/src/main/java/forge/card/cost/CostRemoveCounter.java +++ b/src/main/java/forge/card/cost/CostRemoveCounter.java @@ -183,6 +183,7 @@ public class CostRemoveCounter extends CostPartWithList { private final CounterType counter; private int lastPaidAmount = 0; + private int cntCounters = 1; private ZoneType zone; /** @@ -320,14 +321,11 @@ public class CostRemoveCounter extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @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(); Integer c = this.convertAmount(); if (c == null) { @@ -339,10 +337,10 @@ public class CostRemoveCounter extends CostPartWithList { } if (this.payCostFromSource()) { - source.subtractCounter(this.counter, c); + executePayment(ability, source); } else { - for (final Card card : this.getList()) { - card.subtractCounter(this.counter, c); + for (final Card card : decision.cards) { + executePayment(ability, card); } } source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c)); @@ -361,108 +359,45 @@ public class CostRemoveCounter extends CostPartWithList { final Card source = ability.getSourceCard(); Integer c = this.convertAmount(); int maxCounters = 0; - - if (!this.payCostFromSource()) { - 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 (amount.equals("All")) + cntCounters = maxCounters; + else if (c == null) { + final String sVar = ability.getSVar(amount); + if (sVar.equals("XChoice")) { + cntCounters = CostUtil.chooseXValue(source, ability, maxCounters); + } else { + cntCounters = AbilityUtils.calculateAmount(source, amount, ability); } + } else cntCounters = c; + + + if (!this.payCostFromSource()) { final InputPayment inp; if (this.getZone().equals(ZoneType.Battlefield)) { - inp = new InputPayCostRemoveCounterType(c, ability, this.getType(), this); + inp = new InputPayCostRemoveCounterType(cntCounters, ability, this.getType(), this); } else { - inp = new InputPayCostRemoveCounterFrom(this, this.getType(), ability, c); + inp = new InputPayCostRemoveCounterFrom(this, this.getType(), ability, cntCounters); } FThreads.setInputAndWait(inp); + if( inp.isPaid() ) source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c)); return inp.isPaid(); } 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; - - this.addToList(source); source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c)); - executePayment(ability, source, c); + executePayment(ability, source); return true; } - /* - * (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) { - final String amount = this.getAmount(); - 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 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); + protected void doPayment(SpellAbility ability, Card targetCard){ + targetCard.subtractCounter(this.getCounter(), cntCounters); } + /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#getHashForList() */ @@ -470,4 +405,43 @@ public class CostRemoveCounter extends CostPartWithList { public String getHashForList() { 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 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); + } } diff --git a/src/main/java/forge/card/cost/CostReturn.java b/src/main/java/forge/card/cost/CostReturn.java index f350104dc04..0aea94af567 100644 --- a/src/main/java/forge/card/cost/CostReturn.java +++ b/src/main/java/forge/card/cost/CostReturn.java @@ -111,19 +111,6 @@ public class CostReturn extends CostPartWithList { 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) * @@ -180,30 +167,24 @@ public class CostReturn extends CostPartWithList { * , 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 { - Integer c = this.convertAmount(); - if (c == null) { - c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); - } - - this.setList(ComputerUtil.chooseReturnType(ai, this.getType(), source, ability.getTargetCard(), c)); - if (this.getList().isEmpty()) { - return false; - } + public final PaymentDecision decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source) { + if (this.payCostFromSource()) + return new PaymentDecision(source); + + Integer c = this.convertAmount(); + if (c == null) { + c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); } - return true; + + List res = ComputerUtil.chooseReturnType(ai, this.getType(), source, ability.getTargetCard(), c); + return res.isEmpty() ? null : new PaymentDecision(res); } /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ @Override - public void executePayment(SpellAbility ability, Card targetCard) { - addToList(targetCard); + protected void doPayment(SpellAbility ability, Card targetCard) { ability.getActivatingPlayer().getGame().getAction().moveToHand(targetCard); } @@ -215,6 +196,16 @@ public class CostReturn extends CostPartWithList { 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 diff --git a/src/main/java/forge/card/cost/CostReveal.java b/src/main/java/forge/card/cost/CostReveal.java index a4920553bd0..34454f54042 100644 --- a/src/main/java/forge/card/cost/CostReveal.java +++ b/src/main/java/forge/card/cost/CostReveal.java @@ -31,7 +31,6 @@ import forge.game.player.AIPlayer; import forge.game.player.Player; import forge.game.zone.Zone; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; import forge.view.ButtonUtil; /** @@ -172,56 +171,37 @@ public class CostReveal extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility - * , forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @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(); List hand = new ArrayList(ai.getCardsIn(ZoneType.Hand)); - this.resetList(); if (this.payCostFromSource()) { if (!hand.contains(source)) { - return false; + return null; } - - this.getList().add(source); - } else if (this.getType().equals("Hand")) { - this.setList(new ArrayList(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 new PaymentDecision(source); } - return this.getList() != null; - } - /* - * (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) { - GuiChoose.oneOrNone("Revealed cards:", this.getList()); - for(Card c: getList()) // should not throw concurrent modification here - no items should be added. - executePayment(ability, c); + if (this.getType().equals("Hand")) + return new PaymentDecision(new ArrayList(ai.getCardsIn(ZoneType.Hand))); + + 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); + } + } + + 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 Card source = ability.getSourceCard(); final String amount = this.getAmount(); - this.resetList(); if (this.payCostFromSource()) { executePayment(ability, source); @@ -304,8 +283,7 @@ public class CostReveal extends CostPartWithList { * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ @Override - public void executePayment(SpellAbility ability, Card targetCard) { - addToList(targetCard); + protected void doPayment(SpellAbility ability, Card targetCard) { // write code to actually reveal card } diff --git a/src/main/java/forge/card/cost/CostSacrifice.java b/src/main/java/forge/card/cost/CostSacrifice.java index 321ce2c2c99..6994e4c7aca 100644 --- a/src/main/java/forge/card/cost/CostSacrifice.java +++ b/src/main/java/forge/card/cost/CostSacrifice.java @@ -184,20 +184,6 @@ public class CostSacrifice extends CostPartWithList { 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) * @@ -219,18 +205,11 @@ public class CostSacrifice extends CostPartWithList { if (this.payCostFromSource()) { if (source.getController() == ability.getActivatingPlayer() && source.isInPlay()) { - if (!GuiDialog.confirm(source, source.getName() + " - Sacrifice?")) - return false; - executePayment(ability, source); - return true; + return GuiDialog.confirm(source, source.getName() + " - Sacrifice?") && executePayment(ability, source); + } } else if (amount.equals("All")) { - this.setList(list); - // TODO Ask First - for (final Card card : list) { - executePayment(ability, card); - } - return true; + return executePayment(ability, list); } else { Integer c = this.convertAmount(); if (c == null) { @@ -251,50 +230,8 @@ public class CostSacrifice extends CostPartWithList { return false; } - /* - * (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(); - final Player activator = ability.getActivatingPlayer(); - if (this.payCostFromSource()) { - this.getList().add(source); - } else if (this.getAmount().equals("All")) { - /*List typeList = new ArrayList(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); + protected void doPayment(SpellAbility ability, Card targetCard) { ability.getActivatingPlayer().getGame().getAction().sacrifice(targetCard, ability); } @@ -306,6 +243,37 @@ public class CostSacrifice extends CostPartWithList { 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 typeList = new ArrayList(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 list = ComputerUtil.chooseSacrificeType(ai, this.getType(), source, ability.getTargetCard(), c); + return new PaymentDecision(list); + } + // Inputs } diff --git a/src/main/java/forge/card/cost/CostTap.java b/src/main/java/forge/card/cost/CostTap.java index 0a1e19c2fd4..9081fba4086 100644 --- a/src/main/java/forge/card/cost/CostTap.java +++ b/src/main/java/forge/card/cost/CostTap.java @@ -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.")); } - /* - * (non-Javadoc) - * - * @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @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(); } @@ -102,15 +99,11 @@ public class CostTap extends CostPart { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility - * , forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @Override - public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { - return true; + public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) { + return new PaymentDecision(0); } } diff --git a/src/main/java/forge/card/cost/CostTapType.java b/src/main/java/forge/card/cost/CostTapType.java index c9ea3369ce0..26969477c28 100644 --- a/src/main/java/forge/card/cost/CostTapType.java +++ b/src/main/java/forge/card/cost/CostTapType.java @@ -100,6 +100,8 @@ public class CostTapType extends CostPartWithList { } } + private final boolean canTapSource; + /** * Instantiates a new cost tap type. * @@ -110,8 +112,9 @@ public class CostTapType extends CostPartWithList { * @param 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); + canTapSource = !costHasTapSource; } @Override @@ -187,19 +190,6 @@ public class CostTapType extends CostPartWithList { 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) * @@ -229,16 +219,11 @@ public class CostTapType extends CostPartWithList { return inp.isPaid(); } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility - * , forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @Override - public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { - final boolean tap = payment.getCost().hasTapCost(); + public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) { final String amount = this.getAmount(); Integer c = this.convertAmount(); if (c == null) { @@ -254,22 +239,22 @@ public class CostTapType extends CostPartWithList { } } - this.setList(ComputerUtil.chooseTapType(ai, this.getType(), source, tap, c)); + List 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()); - return false; + return null; } - return true; + return new PaymentDecision(totap); } /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ @Override - public void executePayment(SpellAbility ability, Card targetCard) { - addToList(targetCard); + protected void doPayment(SpellAbility ability, Card targetCard) { targetCard.tap(); } diff --git a/src/main/java/forge/card/cost/CostUnattach.java b/src/main/java/forge/card/cost/CostUnattach.java index a3b232df141..5e9e6fe0f49 100644 --- a/src/main/java/forge/card/cost/CostUnattach.java +++ b/src/main/java/forge/card/cost/CostUnattach.java @@ -86,26 +86,6 @@ public class CostUnattach extends CostPartWithList { 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) * @@ -146,30 +126,29 @@ public class CostUnattach extends CostPartWithList { 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) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ @Override - public void executePayment(SpellAbility ability, Card targetCard) { + protected void doPayment(SpellAbility ability, Card targetCard) { targetCard.unEquipCard(targetCard.getEquipping().get(0)); - this.addToList(targetCard); } @Override public String getHashForList() { 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()); + } } diff --git a/src/main/java/forge/card/cost/CostUntap.java b/src/main/java/forge/card/cost/CostUntap.java index d3355e9bc49..2d8eda4bebb 100644 --- a/src/main/java/forge/card/cost/CostUntap.java +++ b/src/main/java/forge/card/cost/CostUntap.java @@ -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.")); } - /* - * (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) * @@ -101,15 +90,19 @@ public class CostUntap extends CostPart { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility - * , forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPart#decideAIPayment(forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ @Override - public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { - return true; + public PaymentDecision decideAIPayment(AIPlayer ai, SpellAbility ability, Card source) { + 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(); } } diff --git a/src/main/java/forge/card/cost/CostUntapType.java b/src/main/java/forge/card/cost/CostUntapType.java index d33a89556e3..07531c2d828 100644 --- a/src/main/java/forge/card/cost/CostUntapType.java +++ b/src/main/java/forge/card/cost/CostUntapType.java @@ -49,6 +49,7 @@ public class CostUntapType extends CostPartWithList { private final CostUntapType untapType; private static final long serialVersionUID = -7151144318287088542L; private int nUntapped = 0; + private final SpellAbility sa; /** @@ -56,13 +57,15 @@ public class CostUntapType extends CostPartWithList { * @param nCards * @param cardList * @param untapType + * @param ability * @param sa * @param payment */ - public InputPayCostUntapY(int nCards, List cardList, CostUntapType untapType) { + public InputPayCostUntapY(int nCards, List cardList, CostUntapType untapType, SpellAbility ability) { this.nCards = nCards; this.cardList = cardList; this.untapType = untapType; + this.sa = ability; } @Override @@ -81,14 +84,12 @@ public class CostUntapType extends CostPartWithList { } - @Override public void selectCard(final Card card) { Zone zone = Singletons.getModel().getGame().getZoneOf(card); if (zone.is(ZoneType.Battlefield) && cardList.contains(card) && card.isTapped()) { // send in List for Typing - card.untap(); - untapType.addToList(card); + untapType.executePayment(sa, card); cardList.remove(card); this.nUntapped++; @@ -154,17 +155,6 @@ public class CostUntapType extends CostPartWithList { 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) * @@ -205,19 +195,6 @@ public class CostUntapType extends CostPartWithList { 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) * @@ -246,21 +223,33 @@ public class CostUntapType extends CostPartWithList { c = AbilityUtils.calculateAmount(source, amount, ability); } } - InputPayment inp = new InputPayCostUntapY(c, typeList, this); + InputPayment inp = new InputPayCostUntapY(c, typeList, this, ability); FThreads.setInputAndWait(inp); return inp.isPaid(); } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#decideAIPayment(forge.card.spellability.SpellAbility - * , forge.Card, forge.card.cost.Cost_Payment) + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ @Override - public final boolean decideAIPayment(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment) { - final boolean untap = payment.getCost().hasUntapCost(); + protected void doPayment(SpellAbility ability, Card targetCard) { + 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(); Integer c = this.convertAmount(); if (c == null) { @@ -278,32 +267,15 @@ public class CostUntapType extends CostPartWithList { c = AbilityUtils.calculateAmount(source, amount, ability); } } - - this.setList(ComputerUtil.chooseUntapType(ai, this.getType(), source, untap, c)); - - if (this.getList() == null) { + + List list = ComputerUtil.chooseUntapType(ai, this.getType(), source, untap, c); + + if (list == null) { System.out.println("Couldn't find a valid card to untap for: " + source.getName()); - return false; + return null; } - - return true; - } - - /* (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"; + + return new PaymentDecision(list); } // Inputs diff --git a/src/main/java/forge/card/cost/PaymentDecision.java b/src/main/java/forge/card/cost/PaymentDecision.java new file mode 100644 index 00000000000..79c29ca7f50 --- /dev/null +++ b/src/main/java/forge/card/cost/PaymentDecision.java @@ -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 cards = new ArrayList(); + + public PaymentDecision(int cnt) { + c = cnt; + } + + public PaymentDecision(List 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); + } +} diff --git a/src/main/java/forge/game/phase/Untap.java b/src/main/java/forge/game/phase/Untap.java index df70fb7bcc3..f7dcbe7ff42 100644 --- a/src/main/java/forge/game/phase/Untap.java +++ b/src/main/java/forge/game/phase/Untap.java @@ -28,9 +28,13 @@ import forge.CardLists; import forge.CardPredicates; import forge.CardPredicates.Presets; import forge.CounterType; +import forge.FThreads; import forge.GameEntity; import forge.Singletons; 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.ai.ComputerUtilCard; import forge.game.player.Player; @@ -106,20 +110,28 @@ public class Untap extends Phase { return true; } + + public static final Predicate CANUNTAP = new Predicate() { + @Override + public boolean apply(Card c) { + return Untap.canUntap(c); + } + }; + /** *

* doUntap. *

*/ private void doUntap() { - final Player player = Singletons.getModel().getGame().getPhaseHandler().getPlayerTurn(); - final Predicate tappedCanUntap = Predicates.and(Presets.TAPPED, Presets.CANUNTAP); + final Player player = game.getPhaseHandler().getPlayerTurn(); + final Predicate tappedCanUntap = Predicates.and(Presets.TAPPED, CANUNTAP); List list = new ArrayList(player.getCardsIn(ZoneType.Battlefield)); List 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) { - Singletons.getModel().getGame().getAction().moveToHand(c); + game.getAction().moveToHand(c); } list = CardLists.filter(list, new Predicate() { @@ -128,16 +140,13 @@ public class Untap extends Phase { if (!Untap.canUntap(c)) { return false; } - if (Untap.canOnlyUntapOneLand() && c.isLand()) { + if (canOnlyUntapOneLand() && c.isLand()) { return false; } - if (c.isArtifact() - && (Singletons.getModel().getGame().isCardInPlay("Damping Field") || Singletons.getModel().getGame().isCardInPlay("Imi Statue"))) { + if (c.isArtifact() && (game.isCardInPlay("Damping Field") || game.isCardInPlay("Imi Statue"))) { return false; } - if (c.isCreature() - && (Singletons.getModel().getGame().isCardInPlay("Smoke") || Singletons.getModel().getGame().isCardInPlay("Stoic Angel") - || Singletons.getModel().getGame().isCardInPlay("Intruder Alarm"))) { + if (c.isCreature() && (game.isCardInPlay("Smoke") || game.isCardInPlay("Stoic Angel") || game.isCardInPlay("Intruder Alarm"))) { return false; } 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 c.subtractCounter(CounterType.WIND, 1); } else { @@ -190,7 +199,7 @@ public class Untap extends Phase { } // other players untapping during your untap phase - final List cardsWithKW = CardLists.getKeyword(Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield), + final List cardsWithKW = CardLists.getKeyword(game.getCardsIn(ZoneType.Battlefield), "CARDNAME untaps during each other player's untap step."); final List otherPlayers = player.getOpponents(); otherPlayers.addAll(player.getAllies()); @@ -200,123 +209,49 @@ public class Untap extends Phase { } // end other players untapping during your untap phase - if (Untap.canOnlyUntapOneLand()) { - if (player.isComputer()) { - // search for lands the computer has and only untap 1 - List landList = player.getLandsInPlay(); - - landList = CardLists.filter(landList, tappedCanUntap); - if (landList.size() > 0) { + if (canOnlyUntapOneLand()) { + final List landList = CardLists.filter(player.getLandsInPlay(), tappedCanUntap); + + if (!landList.isEmpty()) { + if (player.isComputer()) { + // search for lands the computer has and only untap 1 landList.get(0).untap(); - } - } else { - final InputBase target = new InputBase() { - private static final long serialVersionUID = 6653677835629939465L; - - @Override - 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 landList = player.getLandsInPlay(); - landList = CardLists.filter(landList, tappedCanUntap); - if (landList.size() > 0) { - Singletons.getModel().getMatch().getInput().setInput(target); + } else { + final InputSelectCards target = new InputSelectCardsFromList(1,1, landList); + target.setMessage("Select one tapped land to untap"); + FThreads.setInputAndWait(target); + if( !target.hasCancelled() && !target.getSelected().isEmpty()) + target.getSelected().get(0).untap(); } } } - if (Singletons.getModel().getGame().isCardInPlay("Damping Field") || Singletons.getModel().getGame().isCardInPlay("Imi Statue")) { - final Player turnOwner = Singletons.getModel().getGame().getPhaseHandler().getPlayerTurn(); - if (turnOwner.isComputer()) { - List artList = new ArrayList(turnOwner.getCardsIn(ZoneType.Battlefield)); - artList = CardLists.filter(artList, Presets.ARTIFACTS); - artList = CardLists.filter(artList, tappedCanUntap); - if (artList.size() > 0) { + if (game.isCardInPlay("Damping Field") || game.isCardInPlay("Imi Statue")) { + final Player turnOwner = game.getPhaseHandler().getPlayerTurn(); + final List artList = CardLists.filter(turnOwner.getCardsIn(ZoneType.Battlefield), Presets.ARTIFACTS, tappedCanUntap); + + if (!artList.isEmpty()) { + if (turnOwner.isComputer()) { ComputerUtilCard.getBestArtifactAI(artList).untap(); - } - } else { - final InputBase target = new InputBase() { - private static final long serialVersionUID = 5555427219659889707L; - - @Override - 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 artList = new ArrayList(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); + } else { + final InputSelectCards target = new InputSelectCardsFromList(1,1, artList); + target.setMessage("Select one tapped artifact to untap"); + FThreads.setInputAndWait(target); + if( !target.hasCancelled() && !target.getSelected().isEmpty()) + target.getSelected().get(0).untap(); } } } - if ((Singletons.getModel().getGame().isCardInPlay("Smoke") || Singletons.getModel().getGame().isCardInPlay("Stoic Angel"))) { - if (player.isComputer()) { - List creatures = player.getCreaturesInPlay(); - creatures = CardLists.filter(creatures, tappedCanUntap); - if (creatures.size() > 0) { + if ((game.isCardInPlay("Smoke") || game.isCardInPlay("Stoic Angel"))) { + final List creatures = CardLists.filter(player.getCreaturesInPlay(), tappedCanUntap); + if (!creatures.isEmpty()) { + if (player.isComputer()) { creatures.get(0).untap(); - } - } else { - final InputBase target = new InputBase() { - private static final long serialVersionUID = 5555427219659889707L; - - @Override - 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 creatures = CardLists.filter(player.getCreaturesInPlay(), tappedCanUntap); - if (creatures.size() > 0) { - Singletons.getModel().getMatch().getInput().setInput(target); + } else { + final InputSelectCards target = new InputSelectCardsFromList(1, 1, creatures); + target.setMessage("Select one creature to untap"); + FThreads.setInputAndWait(target); + if( !target.hasCancelled() && !target.getSelected().isEmpty()) + target.getSelected().get(0).untap(); } } } @@ -334,11 +269,10 @@ public class Untap extends Phase { game.getStack().chooseOrderOfSimultaneousStackEntryAll(); } // end doUntap - private static boolean canOnlyUntapOneLand() { + private boolean canOnlyUntapOneLand() { // Winter Orb was given errata so it no longer matters if it's tapped or // not - if (Singletons.getModel().getGame().isCardInPlay("Winter Orb") - || Singletons.getModel().getGame().getPhaseHandler().getPlayerTurn().isCardInPlay("Mungha Wurm")) { + if (game.isCardInPlay("Winter Orb") || game.getPhaseHandler().getPlayerTurn().isCardInPlay("Mungha Wurm")) { return true; } diff --git a/src/main/java/forge/gui/InputProxy.java b/src/main/java/forge/gui/InputProxy.java index f56bc619f10..68c6c9a1f0e 100644 --- a/src/main/java/forge/gui/InputProxy.java +++ b/src/main/java/forge/gui/InputProxy.java @@ -19,6 +19,7 @@ package forge.gui; import java.util.Observable; import java.util.Observer; +import java.util.concurrent.atomic.AtomicReference; import forge.Card; import forge.FThreads; @@ -39,7 +40,7 @@ import forge.game.player.Player; public class InputProxy implements Observer { /** The input. */ - private Input input; + private AtomicReference input = new AtomicReference(); private MatchController match = null; public void setMatch(MatchController matchController) { @@ -48,7 +49,7 @@ public class InputProxy implements Observer { @Override public final synchronized void update(final Observable observable, final Object obj) { - this.input = null; + this.input.set(null); final GameState game = match.getCurrentGame(); 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()); if (nextInput != null) { - this.input = nextInput; + this.input.set(nextInput); FThreads.invokeInEDT(new Runnable() { @Override public void run() { nextInput.showMessage(); } }); } else if (!ph.isPlayerPriorityAllowed()) { ph.getPriorityPlayer().getController().passPriority(); @@ -80,8 +81,9 @@ public class InputProxy implements Observer { *

*/ public final void selectButtonOK() { - if ( null == input ) return; - input.selectButtonOK(); + Input inp = getInput(); + if ( null != inp ) + inp.selectButtonOK(); } /** @@ -90,8 +92,9 @@ public class InputProxy implements Observer { *

*/ public final void selectButtonCancel() { - if ( null == input ) return; - input.selectButtonCancel(); + Input inp = getInput(); + if ( null != inp ) + inp.selectButtonCancel(); } /** @@ -103,8 +106,9 @@ public class InputProxy implements Observer { * a {@link forge.game.player.Player} object. */ public final void selectPlayer(final Player player) { - if ( null == input ) return; - input.selectPlayer(player); + Input inp = getInput(); + if ( null != inp ) + inp.selectPlayer(player); } /** @@ -118,19 +122,20 @@ public class InputProxy implements Observer { * a {@link forge.game.zone.PlayerZone} object. */ public final void selectCard(final Card card) { - if ( null == input ) return; - input.selectCard(card); + Input inp = getInput(); + if ( null != inp ) + inp.selectCard(card); } /** {@inheritDoc} */ @Override public final String toString() { - if ( null == input ) return "(null)"; - return this.input.toString(); + Input inp = getInput(); + return null == inp ? "(null)" : inp.toString(); } /** @return {@link forge.gui.InputProxy.InputBase} */ public Input getInput() { - return this.input; + return this.input.get(); } }