diff --git a/.gitattributes b/.gitattributes index 2b5ee1d0721..b4335b6bc3a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15438,6 +15438,7 @@ forge-gui/src/main/java/forge/gui/menus/IMenuProvider.java -text forge-gui/src/main/java/forge/gui/menus/LayoutMenu.java -text forge-gui/src/main/java/forge/gui/menus/MenuUtil.java -text forge-gui/src/main/java/forge/gui/package-info.java svneol=native#text/plain +forge-gui/src/main/java/forge/gui/player/HumanCostDecision.java -text forge-gui/src/main/java/forge/gui/player/HumanPlay.java -text forge-gui/src/main/java/forge/gui/player/LobbyPlayerHuman.java -text forge-gui/src/main/java/forge/gui/player/PlayerControllerHuman.java -text diff --git a/forge-gui/src/main/java/forge/ai/AiCostDecision.java b/forge-gui/src/main/java/forge/ai/AiCostDecision.java index 254dffb770b..96b0eee2e68 100644 --- a/forge-gui/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-gui/src/main/java/forge/ai/AiCostDecision.java @@ -525,7 +525,7 @@ public class AiCostDecision implements ICostVisitor { return null; } if (amount.equals("All")) { - c = source.getCounters(cost.getCounter()); + c = source.getCounters(cost.counter); } else { c = AbilityUtils.calculateAmount(source, amount, ability); } @@ -536,18 +536,18 @@ public class AiCostDecision implements ICostVisitor { if (type.equals("OriginalHost")) { typeList = Lists.newArrayList(ability.getOriginalHost()); } else { - typeList = CardLists.getValidCards(ai.getCardsIn(cost.getZone()), type.split(";"), ai, source); + typeList = CardLists.getValidCards(ai.getCardsIn(cost.zone), type.split(";"), ai, source); } for (Card card : typeList) { - if (card.getCounters(cost.getCounter()) >= c) { + if (card.getCounters(cost.counter) >= c) { return PaymentDecision.card(card); } } return null; } - if (c > source.getCounters(cost.getCounter())) { - System.out.println("Not enough " + cost.getCounter() + " on " + source.getName()); + if (c > source.getCounters(cost.counter)) { + System.out.println("Not enough " + cost.counter + " on " + source.getName()); return null; } diff --git a/forge-gui/src/main/java/forge/ai/ComputerUtilCost.java b/forge-gui/src/main/java/forge/ai/ComputerUtilCost.java index c0be6ce791a..32b7e49d45d 100644 --- a/forge-gui/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-gui/src/main/java/forge/ai/ComputerUtilCost.java @@ -73,7 +73,7 @@ public class ComputerUtilCost { if (part instanceof CostRemoveCounter) { final CostRemoveCounter remCounter = (CostRemoveCounter) part; - final CounterType type = remCounter.getCounter(); + final CounterType type = remCounter.counter; if (!part.payCostFromSource()) { if (type.name().equals("P1P1")) { return false; diff --git a/forge-gui/src/main/java/forge/game/cost/CostAddMana.java b/forge-gui/src/main/java/forge/game/cost/CostAddMana.java index 68f1c864359..1c5cbefabf3 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostAddMana.java +++ b/forge-gui/src/main/java/forge/game/cost/CostAddMana.java @@ -23,7 +23,6 @@ import org.apache.commons.lang3.StringUtils; import forge.card.ColorSet; import forge.card.MagicColor; import forge.game.GameType; -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.mana.Mana; import forge.game.player.Player; @@ -51,21 +50,10 @@ public class CostAddMana extends CostPart { public final String toString() { final StringBuilder sb = new StringBuilder(); final Integer i = this.convertAmount(); - sb.append("Add ").append(convertManaAmountType(i, this.getType())); - sb.append(" to your mana pool"); + sb.append("Add ").append(StringUtils.repeat("{" + this.getType() + "}", i)).append(" to your mana pool"); return sb.toString(); } - /** - * convertManaAmountType. - * @param i - * @param type - * @return a String - */ - private String convertManaAmountType(Integer i, String type) { - return StringUtils.repeat("{" + type + "}", i); - } - /* * (non-Javadoc) * @@ -78,23 +66,6 @@ public class CostAddMana extends CostPart { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final Card source = ability.getSourceCard(); - Integer c = this.convertAmount(); - if (c == null) { - c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); - } - return PaymentDecision.number(c); - } - @Override public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility sa) { Card source = sa.getSourceCard(); diff --git a/forge-gui/src/main/java/forge/game/cost/CostChooseCreatureType.java b/forge-gui/src/main/java/forge/game/cost/CostChooseCreatureType.java index 2e882d87629..5f3968b3353 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostChooseCreatureType.java +++ b/forge-gui/src/main/java/forge/game/cost/CostChooseCreatureType.java @@ -17,9 +17,6 @@ */ package forge.game.cost; -import java.util.ArrayList; - -import forge.card.CardType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -50,20 +47,6 @@ public class CostChooseCreatureType extends CostPart { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player payer) { - String choice = payer.getController().chooseSomeType("Creature", ability, new ArrayList(CardType.getCreatureTypes()), new ArrayList(), true); - if( null == choice ) - return null; - return PaymentDecision.type(choice); - } @Override public boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa) { diff --git a/forge-gui/src/main/java/forge/game/cost/CostDamage.java b/forge-gui/src/main/java/forge/game/cost/CostDamage.java index 409849b5da3..3db23c5bbba 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostDamage.java +++ b/forge-gui/src/main/java/forge/game/cost/CostDamage.java @@ -17,8 +17,6 @@ */ package forge.game.cost; -import forge.game.ability.AbilityUtils; -import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -60,37 +58,6 @@ public class CostDamage extends CostPart { return payer.addDamage(decision.c, sa.getSourceCard()); } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final String amount = this.getAmount(); - final int life = activator.getLife(); - final Card source = ability.getSourceCard(); - - Integer c = this.convertAmount(); - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, life); - } - else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - - if (activator.canPayLife(c) && activator.getController().confirmPayment(this, "Pay " + c + " Life?")) { - return PaymentDecision.number(c); - } - return null; - } - @Override public T accept(ICostVisitor visitor) { return visitor.visit(this); diff --git a/forge-gui/src/main/java/forge/game/cost/CostDiscard.java b/forge-gui/src/main/java/forge/game/cost/CostDiscard.java index 0c902f69c3e..05ed0fff88c 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostDiscard.java +++ b/forge-gui/src/main/java/forge/game/cost/CostDiscard.java @@ -19,18 +19,12 @@ package forge.game.cost; import java.util.ArrayList; import java.util.List; -import com.google.common.base.Predicate; -import com.google.common.collect.Lists; - -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CardPredicates; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.input.InputSelectCardsFromList; -import forge.util.Aggregates; /** * The Class CostDiscard. @@ -82,8 +76,7 @@ public class CostDiscard extends CostPartWithList { desc.append("card"); } else { - desc.append(this.getTypeDescription() == null ? this.getType() : this.getTypeDescription()).append( - " card"); + desc.append(this.getTypeDescription() == null ? this.getType() : this.getTypeDescription()).append(" card"); } sb.append(Cost.convertAmountTypeToWords(i, this.getAmount(), desc.toString())); @@ -151,111 +144,6 @@ public class CostDiscard extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player payer) { - final Card source = ability.getSourceCard(); - - List handList = new ArrayList(payer.getCardsIn(ZoneType.Hand)); - String discardType = this.getType(); - final String amount = this.getAmount(); - - if (this.payCostFromSource()) { - return handList.contains(source) ? PaymentDecision.card(source) : null; - } - - if (discardType.equals("Hand")) { - return PaymentDecision.card(handList); - } - - if (discardType.equals("LastDrawn")) { - final Card lastDrawn = payer.getLastDrawnCard(); - return handList.contains(lastDrawn) ? PaymentDecision.card(lastDrawn) : null; - } - - Integer c = this.convertAmount(); - - if (discardType.equals("Random")) { - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, handList.size()); - } - else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - - return PaymentDecision.card(Aggregates.random(handList, c)); - } - if (discardType.contains("+WithSameName")) { - String type = discardType.replace("+WithSameName", ""); - handList = CardLists.getValidCards(handList, type.split(";"), payer, source); - final List landList2 = handList; - 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 == 0) { return PaymentDecision.card(Lists.newArrayList()); } - List discarded = new ArrayList(); - while (c > 0) { - InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, handList); - inp.setMessage("Select one of the cards with the same name to discard. Already chosen: " + discarded); - inp.setCancelAllowed(true); - inp.showAndWait(); - if (inp.hasCancelled()) { - return null; - } - final Card first = inp.getFirstSelected(); - discarded.add(first); - handList = CardLists.filter(handList, CardPredicates.nameEquals(first.getName())); - handList.remove(first); - c--; - } - return PaymentDecision.card(discarded); - } - else { - String type = new String(discardType); - final String[] validType = type.split(";"); - handList = CardLists.getValidCards(handList, validType, payer, source); - - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, handList.size()); - } - else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - - InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, handList); - inp.setMessage("Select %d more " + getDescriptiveType() + " to discard."); - inp.setCancelAllowed(true); - inp.showAndWait(); - if (inp.hasCancelled() || inp.getSelected().size() != c) { - return null; - } - - return PaymentDecision.card(inp.getSelected()); - } - } - /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ diff --git a/forge-gui/src/main/java/forge/game/cost/CostDraw.java b/forge-gui/src/main/java/forge/game/cost/CostDraw.java index 6af17f06157..a1b4d7002e5 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostDraw.java +++ b/forge-gui/src/main/java/forge/game/cost/CostDraw.java @@ -20,7 +20,6 @@ package forge.game.cost; import java.util.ArrayList; import java.util.List; -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -94,30 +93,6 @@ public class CostDraw extends CostPart { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player payer) { - final String amount = this.getAmount(); - final Card source = ability.getSourceCard(); - - Integer c = this.convertAmount(); - if (c == null) { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - - if (!payer.getController().confirmPayment(this, "Draw " + c + " Card" + (c == 1 ? "" : "s"))) { - return null; - } - - return PaymentDecision.number(c); - } - @Override public T accept(ICostVisitor visitor) { return visitor.visit(this); diff --git a/forge-gui/src/main/java/forge/game/cost/CostExile.java b/forge-gui/src/main/java/forge/game/cost/CostExile.java index 5d6ff5abc43..c36cfb6d43e 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostExile.java +++ b/forge-gui/src/main/java/forge/game/cost/CostExile.java @@ -18,14 +18,9 @@ package forge.game.cost; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - import forge.game.Game; -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CardPredicates; @@ -33,9 +28,6 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; -import forge.gui.input.InputSelectCardsFromList; -import forge.util.Lang; /** * The Class CostExile. @@ -52,7 +44,7 @@ public class CostExile extends CostPartWithList { * */ - private ZoneType from = ZoneType.Battlefield; + public final ZoneType from; public final boolean sameZone; /** @@ -82,9 +74,7 @@ public class CostExile extends CostPartWithList { public CostExile(final String amount, final String type, final String description, final ZoneType from, final boolean sameZone) { super(amount, type, description); - if (from != null) { - this.from = from; - } + this.from = from != null ? from : ZoneType.Battlefield; this.sameZone = sameZone; } @@ -206,206 +196,6 @@ public class CostExile extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final String amount = this.getAmount(); - final Card source = ability.getSourceCard(); - final Game game = activator.getGame(); - - Integer c = this.convertAmount(); - String type = this.getType(); - boolean fromTopGrave = false; - if (type.contains("FromTopGrave")) { - type = type.replace("FromTopGrave", ""); - fromTopGrave = true; - } - - List list; - if (this.from.equals(ZoneType.Stack)) { - list = new ArrayList(); - for (SpellAbilityStackInstance si : game.getStack()) { - list.add(si.getSourceCard()); - } - } - else if (this.sameZone) { - list = new ArrayList(game.getCardsIn(this.from)); - } - else { - list = new ArrayList(activator.getCardsIn(this.from)); - } - - if (this.payCostFromSource()) { - return source.getZone() == activator.getZone(from) && activator.getController().confirmPayment(this, "Exile " + source.getName() + "?") ? PaymentDecision.card(source) : null; - - } - - if (type.equals("All")) { - return PaymentDecision.card(list); - } - list = CardLists.getValidCards(list, type.split(";"), activator, source); - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, list.size()); - } - else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - - if (this.from == ZoneType.Battlefield || this.from == ZoneType.Hand) { - InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, list); - inp.setMessage("Exile %d card(s) from your" + from); - inp.setCancelAllowed(true); - inp.showAndWait(); - return inp.hasCancelled() ? null : PaymentDecision.card(inp.getSelected()); - } - - if (this.from == ZoneType.Stack) { return exileFromStack(ability, c); } - if (this.from == ZoneType.Library) { return exileFromTop(ability, activator, c); } - if (fromTopGrave) { return exileFromTopGraveType(ability, c, list); } - if (!this.sameZone) { return exileFromMiscZone(ability, c, list); } - - List players = game.getPlayers(); - List payableZone = new ArrayList(); - for (Player p : players) { - List enoughType = CardLists.filter(list, CardPredicates.isOwner(p)); - if (enoughType.size() < c) { - list.removeAll(enoughType); - } - else { - payableZone.add(p); - } - } - return exileFromSame(list, c, payableZone); - } - - // Inputs - - // Exile - // ExileFromHand - // ExileFromGrave - // ExileFromTop (of library) - // ExileSameGrave - - private PaymentDecision exileFromSame(List list, int nNeeded, List payableZone) { - if (nNeeded == 0) { - return PaymentDecision.number(0); - } - - final Player p = GuiChoose.oneOrNone(String.format("Exile from whose %s?", getFrom()), payableZone); - if (p == null) { - return null; - } - - List typeList = CardLists.filter(list, CardPredicates.isOwner(p)); - if(typeList.size() < nNeeded) - return null; - - List toExile = GuiChoose.many("Exile from " + getFrom(), "To be exiled", nNeeded, typeList, null); - return PaymentDecision.card(toExile); - } - - /** - * TODO: Write javadoc for Constructor. - * @param payment - * @param sa - * @param type - * @param nNeeded - * @param part - */ - private PaymentDecision exileFromStack(SpellAbility sa, int nNeeded) { - if (nNeeded == 0) { - return PaymentDecision.number(0); - } - - final Game game = sa.getActivatingPlayer().getGame(); - ArrayList saList = new ArrayList(); - ArrayList descList = new ArrayList(); - - for (SpellAbilityStackInstance si : game.getStack()) { - final Card stC = si.getSourceCard(); - final SpellAbility stSA = si.getSpellAbility().getRootAbility(); - if (stC.isValid(getType().split(";"), sa.getActivatingPlayer(), sa.getSourceCard()) && stSA.isSpell()) { - saList.add(stSA); - if (stC.isCopiedSpell()) { - descList.add(stSA.getStackDescription() + " (Copied Spell)"); - } else { - descList.add(stSA.getStackDescription()); - } - } - } - - if (saList.size() < nNeeded) { - return null; - } - - List exiled = new ArrayList(); - for (int i = 0; i < nNeeded; i++) { - //Have to use the stack descriptions here because some copied spells have no description otherwise - final String o = GuiChoose.oneOrNone("Exile from " + getFrom(), descList); - - if (o != null) { - final SpellAbility toExile = saList.get(descList.indexOf(o)); - final Card c = toExile.getSourceCard(); - - saList.remove(toExile); - descList.remove(o); - - exiled.add(c); - } - else { - return null; - } - } - return PaymentDecision.card(exiled); - } - - private PaymentDecision exileFromTop(final SpellAbility sa, final Player payer, final int nNeeded) { - final StringBuilder sb = new StringBuilder(); - sb.append("Exile ").append(nNeeded).append(" cards from the top of your library?"); - final List list = payer.getCardsIn(ZoneType.Library, nNeeded); - - if (list.size() > nNeeded || !payer.getController().confirmPayment(this, "Exile " + Lang.nounWithAmount(nNeeded, "card") + " from the top of your library?")) { - return null; - } - - return PaymentDecision.card(list); - } - - private PaymentDecision exileFromMiscZone(SpellAbility sa, int nNeeded, List typeList) { - if (typeList.size() < nNeeded) - return null; - - List exiled = new ArrayList(); - for (int i = 0; i < nNeeded; i++) { - final Card c = GuiChoose.oneOrNone("Exile from " + getFrom(), typeList); - - if (c != null) { - typeList.remove(c); - exiled.add(c); - } else { - return null; - } - } - return PaymentDecision.card(exiled); - } - - private PaymentDecision exileFromTopGraveType(SpellAbility sa, int nNeeded, List typeList) { - if (typeList.size() < nNeeded) - return null; - - Collections.reverse(typeList); - return PaymentDecision.card(Lists.newArrayList(Iterables.limit(typeList, nNeeded))); - } /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) diff --git a/forge-gui/src/main/java/forge/game/cost/CostExiledMoveToGrave.java b/forge-gui/src/main/java/forge/game/cost/CostExiledMoveToGrave.java index 489e49fc871..f27cc75cab9 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostExiledMoveToGrave.java +++ b/forge-gui/src/main/java/forge/game/cost/CostExiledMoveToGrave.java @@ -25,7 +25,6 @@ import forge.game.card.CardLists; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; /** * This is for the "ExiledMoveToGrave" Cost. @@ -104,32 +103,6 @@ public class CostExiledMoveToGrave extends CostPartWithList { } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player payer) { - - final Card source = ability.getSourceCard(); - Integer c = this.convertAmount(); - if (c == null) { - c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); - } - - final Player activator = ability.getActivatingPlayer(); - List list = activator.getGame().getCardsIn(ZoneType.Exile); - list = CardLists.getValidCards(list, this.getType().split(";"), activator, source); - - if (list.size() < c) - return null; - - return PaymentDecision.card(GuiChoose.many("Choose an exiled card to put into graveyard", "To graveyard", c, list, source)); - } - /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ diff --git a/forge-gui/src/main/java/forge/game/cost/CostFlipCoin.java b/forge-gui/src/main/java/forge/game/cost/CostFlipCoin.java index ca414f742d2..16199967055 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostFlipCoin.java +++ b/forge-gui/src/main/java/forge/game/cost/CostFlipCoin.java @@ -17,7 +17,6 @@ */ package forge.game.cost; -import forge.game.ability.AbilityUtils; import forge.game.ability.effects.FlipCoinEffect; import forge.game.card.Card; import forge.game.player.Player; @@ -58,36 +57,6 @@ public class CostFlipCoin extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final String amount = this.getAmount(); - Integer c = this.convertAmount(); - final Card source = ability.getSourceCard(); - - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, this.getList().size()); - } else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - return PaymentDecision.number(c); - } - - /* - * (non-Javadoc) - * - * @see forge.card.cost.CostPart#toString() - */ @Override public final String toString() { return Cost.convertAmountTypeToWords(this.convertAmount(), this.getAmount(), "Coin"); diff --git a/forge-gui/src/main/java/forge/game/cost/CostGainControl.java b/forge-gui/src/main/java/forge/game/cost/CostGainControl.java index 9930bcd4590..701beabc7bb 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostGainControl.java +++ b/forge-gui/src/main/java/forge/game/cost/CostGainControl.java @@ -25,7 +25,6 @@ import forge.game.card.CardLists; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.input.InputSelectCardsFromList; /** * The Class CostReturn. @@ -84,35 +83,6 @@ public class CostGainControl extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player payer) { - final String amount = this.getAmount(); - final Card source = ability.getSourceCard(); - - Integer c = this.convertAmount(); - if (c == null) { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - final List list = payer.getCardsIn(ZoneType.Battlefield); - List validCards = CardLists.getValidCards(list, this.getType().split(";"), payer, source); - - InputSelectCardsFromList inp = new InputSelectCardsFromList(c, validCards); - final String desc = this.getTypeDescription() == null ? this.getType() : this.getTypeDescription(); - inp.setMessage("Gain control of %d " + desc); - inp.showAndWait(); - if (inp.hasCancelled()) { - return null; - } - return PaymentDecision.card(inp.getSelected()); - } - /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ diff --git a/forge-gui/src/main/java/forge/game/cost/CostGainLife.java b/forge-gui/src/main/java/forge/game/cost/CostGainLife.java index 1435fb5a482..b3519bafe72 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostGainLife.java +++ b/forge-gui/src/main/java/forge/game/cost/CostGainLife.java @@ -20,13 +20,9 @@ package forge.game.cost; import java.util.ArrayList; import java.util.List; -import com.google.common.collect.Lists; - -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.gui.GuiChoose; /** * The Class CostGainLife. @@ -45,6 +41,13 @@ public class CostGainLife extends CostPart { cntPlayers = qty; } + /** + * @return the cntPlayers + */ + public int getCntPlayers() { + return cntPlayers; + } + /* * (non-Javadoc) * @@ -116,51 +119,6 @@ public class CostGainLife extends CostPart { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final Card source = ability.getSourceCard(); - final String amount = this.getAmount(); - - final int life = activator.getLife(); - - Integer c = this.convertAmount(); - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, life); - } else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - - final List oppsThatCanGainLife = new ArrayList(); - for (final Player opp : getPotentialTargets(activator, source)) { - if (opp.canGainLife()) { - oppsThatCanGainLife.add(opp); - } - } - - if (cntPlayers == Integer.MAX_VALUE) // applied to all players who can gain - return PaymentDecision.players(oppsThatCanGainLife); - - final StringBuilder sb = new StringBuilder(); - sb.append(source.getName()).append(" - Choose an opponent to gain ").append(c).append(" life:"); - - final Player chosenToGain = GuiChoose.oneOrNone(sb.toString(), oppsThatCanGainLife); - if (null == chosenToGain) - return null; - else - return PaymentDecision.players(Lists.newArrayList(chosenToGain)); - } - public T accept(ICostVisitor visitor) { return visitor.visit(this); diff --git a/forge-gui/src/main/java/forge/game/cost/CostMill.java b/forge-gui/src/main/java/forge/game/cost/CostMill.java index 588af21383d..955798146fd 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostMill.java +++ b/forge-gui/src/main/java/forge/game/cost/CostMill.java @@ -76,36 +76,6 @@ public class CostMill extends CostPartWithList { return i < zone.size(); } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final String amount = this.getAmount(); - Integer c = this.convertAmount(); - final Card source = ability.getSourceCard(); - - - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, this.getList().size()); - } else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - - if (!activator.getController().confirmPayment(this, "Mill " + c + " card" + (c == 1 ? "" : "s") + " from your library?")) { - return null; - } - return PaymentDecision.card(activator.getCardsIn(ZoneType.Library, c)); - } - /* * (non-Javadoc) * diff --git a/forge-gui/src/main/java/forge/game/cost/CostPart.java b/forge-gui/src/main/java/forge/game/cost/CostPart.java index 27907d971db..49b11dddd57 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostPart.java +++ b/forge-gui/src/main/java/forge/game/cost/CostPart.java @@ -151,20 +151,6 @@ public abstract class CostPart { public abstract T accept(final ICostVisitor visitor); - /** - * Pay human. - * - * @param ability - * {@link forge.game.spellability.SpellAbility} - * @param source - * {@link forge.game.card.Card} - * @param payment - * {@link forge.game.cost.CostPayment} - * @param game - * @return true, if successful - */ - public abstract PaymentDecision payHuman(SpellAbility ability, Player humanPayer); - /* * (non-Javadoc) * @@ -191,18 +177,7 @@ public abstract class CostPart { public void setAmount(final String amountIn) { this.amount = amountIn; } - - protected int chooseXValue(final Card card, final SpellAbility sa, final int maxValue) { - /*final String chosen = sa.getSVar("ChosenX"); - if (chosen.length() > 0) { - return AbilityFactory.calculateAmount(card, "ChosenX", null); - }*/ - int chosenX = sa.getActivatingPlayer().getController().chooseNumber(sa, card.toString() + " - Choose a Value for X", 0, maxValue); - sa.setSVar("ChosenX", Integer.toString(chosenX)); - card.setSVar("ChosenX", Integer.toString(chosenX)); - return chosenX; - } public abstract boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa); } diff --git a/forge-gui/src/main/java/forge/game/cost/CostPartMana.java b/forge-gui/src/main/java/forge/game/cost/CostPartMana.java index 2986e9f2672..3c776f10436 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostPartMana.java +++ b/forge-gui/src/main/java/forge/game/cost/CostPartMana.java @@ -111,10 +111,4 @@ public class CostPartMana extends CostPart { return payer.getController().payManaCost(this, pd, sa); } - @Override - public PaymentDecision payHuman(SpellAbility ability, Player humanPayer) { - // TODO Auto-generated method stub - return new PaymentDecision(0); - } - } diff --git a/forge-gui/src/main/java/forge/game/cost/CostPayLife.java b/forge-gui/src/main/java/forge/game/cost/CostPayLife.java index e9b26e4b867..41308c67161 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostPayLife.java +++ b/forge-gui/src/main/java/forge/game/cost/CostPayLife.java @@ -101,41 +101,6 @@ public class CostPayLife extends CostPart { return ai.payLife(paidAmount, null); } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final Card source = ability.getSourceCard(); - final String amount = this.getAmount(); - final int life = activator.getLife(); - - Integer c = this.convertAmount(); - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.startsWith("XChoice")) { - int limit = life; - if (sVar.contains("LimitMax")) { - limit = AbilityUtils.calculateAmount(source, sVar.split("LimitMax.")[1], ability); - } - int maxLifePayment = limit < life ? limit : life; - c = chooseXValue(source, ability, maxLifePayment); - } else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - - if (activator.canPayLife(c) && activator.getController().confirmPayment(this, "Pay " + c + " Life?")) { - return PaymentDecision.number(c); - } - return null; - } - public T accept(ICostVisitor visitor) { return visitor.visit(this); } diff --git a/forge-gui/src/main/java/forge/game/cost/CostPayment.java b/forge-gui/src/main/java/forge/game/cost/CostPayment.java index 28433374628..f8dc59c4dc9 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostPayment.java +++ b/forge-gui/src/main/java/forge/game/cost/CostPayment.java @@ -27,6 +27,7 @@ import forge.game.Game; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; +import forge.gui.player.HumanCostDecision; /** *

@@ -107,14 +108,14 @@ public class CostPayment { * @return a boolean. */ public boolean payCost(final Player payer) { + HumanCostDecision hcd = new HumanCostDecision(payer, ability, ability.getSourceCard()); + for (final CostPart part : this.cost.getCostParts()) { - PaymentDecision pd = part.payHuman(this.ability, payer); + PaymentDecision pd = part.accept(hcd); - if ( null == pd ) { + if ( null == pd || !part.payAsDecided(payer, pd, ability)) return false; - } else - part.payAsDecided(payer, pd, ability); - + // abilities care what was used to pay for them if( part instanceof CostPartWithList ) ((CostPartWithList) part).reportPaidCardsTo(ability); diff --git a/forge-gui/src/main/java/forge/game/cost/CostPutCardToLib.java b/forge-gui/src/main/java/forge/game/cost/CostPutCardToLib.java index 45a953328f2..c71b5872040 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostPutCardToLib.java +++ b/forge-gui/src/main/java/forge/game/cost/CostPutCardToLib.java @@ -28,8 +28,6 @@ import forge.game.card.CardPredicates; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; -import forge.gui.input.InputSelectCardsFromList; /** * This is for the "PutCardToLib" Cost. @@ -39,8 +37,8 @@ public class CostPutCardToLib extends CostPartWithList { // PutCardToLibFromSameGrave // PutCardToLibFromGrave - private ZoneType from = ZoneType.Hand; - private boolean sameZone = false; + public final ZoneType from; + public final boolean sameZone; private String libPosition = "0"; /** @@ -82,18 +80,14 @@ public class CostPutCardToLib extends CostPartWithList { * @param from * the from */ - public CostPutCardToLib(final String amount, final String libpos, - final String type, final String description, final ZoneType from) { - super(amount, type, description); - if (from != null) { - this.from = from; - } - this.libPosition = libpos; + public CostPutCardToLib(final String amount, final String libpos, final String type, final String description, final ZoneType from) { + this(amount, libpos, type, description, from, false); } - public CostPutCardToLib(final String amount, final String libpos, final String type, - final String description, final ZoneType from, final boolean sameZone) { - this(amount, libpos, type, description, from); + public CostPutCardToLib(final String amount, final String libpos, final String type, final String description, final ZoneType from, final boolean sameZone) { + super(amount, type, description); + this.from = from == null ? ZoneType.Hand : from; + this.libPosition = libpos; this.sameZone = sameZone; } @@ -197,113 +191,6 @@ public class CostPutCardToLib extends CostPartWithList { } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final String amount = this.getAmount(); - Integer c = this.convertAmount(); - final Card source = ability.getSourceCard(); - - List list; - - if (this.sameZone) { - list = new ArrayList(activator.getGame().getCardsIn(this.getFrom())); - } else { - list = new ArrayList(activator.getCardsIn(this.getFrom())); - } - - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, this.getList().size()); - } else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - - list = CardLists.getValidCards(list, this.getType().split(";"), activator, source); - - if (this.from == ZoneType.Hand) { - InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, list); - inp.setMessage("Put %d card(s) from your " + from ); - inp.setCancelAllowed(true); - inp.showAndWait(); - return inp.hasCancelled() ? null : PaymentDecision.card(inp.getSelected()); - } - - if (this.sameZone){ - List players = activator.getGame().getPlayers(); - List payableZone = new ArrayList(); - for (Player p : players) { - List enoughType = CardLists.filter(list, CardPredicates.isOwner(p)); - if (enoughType.size() < c) { - list.removeAll(enoughType); - } else { - payableZone.add(p); - } - } - return putFromSame(list, c, payableZone); - } else {//Graveyard - return putFromMiscZone(ability, c, list); - } - } - - /** - * PutFromMiscZone - * @param sa - * @param nNeeded - * @param typeList - * @return a boolean - */ - private PaymentDecision putFromMiscZone(SpellAbility sa, int nNeeded, List typeList) { - if(typeList.size() < nNeeded) - return null; - - List chosen = new ArrayList<>(); - for (int i = 0; i < nNeeded; i++) { - final Card c = GuiChoose.oneOrNone("Put from " + getFrom() + " to library", typeList); - - if (c == null) - return null; - - typeList.remove(c); - chosen.add(c); - } - return PaymentDecision.card(chosen); - } - - private PaymentDecision putFromSame(List list, int nNeeded, List payableZone) { - if (nNeeded == 0) { - return PaymentDecision.number(0); - } - - final Player p = GuiChoose.oneOrNone(String.format("Put cards from whose %s?", getFrom()), payableZone); - if (p == null) { - return null; - } - - List typeList = CardLists.filter(list, CardPredicates.isOwner(p)); - if(typeList.size() < nNeeded) - return null; - - List chosen = new ArrayList<>(); - for (int i = 0; i < nNeeded; i++) { - final Card c = GuiChoose.oneOrNone("Put cards from " + getFrom() + " to Library", typeList); - if (c == null) - return null; - typeList.remove(c); - chosen.add(c); - } - return PaymentDecision.card(chosen); - } - /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ diff --git a/forge-gui/src/main/java/forge/game/cost/CostPutCounter.java b/forge-gui/src/main/java/forge/game/cost/CostPutCounter.java index 9b844fc20ef..0bbad3b281a 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostPutCounter.java +++ b/forge-gui/src/main/java/forge/game/cost/CostPutCounter.java @@ -25,8 +25,6 @@ import forge.game.card.CounterType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.input.InputSelectCardsFromList; -import forge.util.Lang; /** * The Class CostPutCounter. @@ -164,37 +162,7 @@ public class CostPutCounter extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - Integer c = getNumberOfCounters(ability); - - if (this.payCostFromSource()) { - lastPaidAmount = c; - return PaymentDecision.number(c); - } - - // Cards to use this branch: Scarscale Ritual, Wandering Mage - each adds only one counter - List typeList = CardLists.getValidCards(activator.getCardsIn(ZoneType.Battlefield), getType().split(";"), activator, ability.getSourceCard()); - - InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, typeList); - inp.setMessage("Put " + Lang.nounWithAmount(c, getCounter().getName() + " counter") + " on " + getDescriptiveType()); - inp.setCancelAllowed(true); - inp.showAndWait(); - - if(inp.hasCancelled()) - return null; - - return PaymentDecision.card(inp.getSelected()); - } - - private Integer getNumberOfCounters(final SpellAbility ability) { + public Integer getNumberOfCounters(final SpellAbility ability) { Integer c = this.convertAmount(); if (c == null) { c = AbilityUtils.calculateAmount(ability.getSourceCard(), this.getAmount(), ability); diff --git a/forge-gui/src/main/java/forge/game/cost/CostRemoveAnyCounter.java b/forge-gui/src/main/java/forge/game/cost/CostRemoveAnyCounter.java index a0d93e724f1..eba4aac891c 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostRemoveAnyCounter.java +++ b/forge-gui/src/main/java/forge/game/cost/CostRemoveAnyCounter.java @@ -30,8 +30,6 @@ import forge.game.card.CounterType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; -import forge.gui.input.InputSelectCardsFromList; /** * The Class CostRemoveAnyCounter. @@ -41,6 +39,13 @@ public class CostRemoveAnyCounter extends CostPartWithList { // Power Conduit and Chisei, Heart of Oceans // Both cards have "Remove a counter from a permanent you control" private CounterType counterType; + /** + * @param counterType the counterType to set + */ + public void setCounterType(CounterType counterType) { + this.counterType = counterType; + } + /** * Instantiates a new cost CostRemoveAnyCounter. * @@ -64,7 +69,7 @@ public class CostRemoveAnyCounter extends CostPartWithList { * * @return the counter */ - private CounterType getCounter() { + public CounterType getCounter() { return this.counterType; } @@ -106,45 +111,6 @@ public class CostRemoveAnyCounter extends CostPartWithList { return i <= allCounters; } - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final Card source = ability.getSourceCard(); - Integer c = this.convertAmount(); - final String type = this.getType(); - - if (c == null) { - c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); - } - - List list = new ArrayList(activator.getCardsIn(ZoneType.Battlefield)); - list = CardLists.getValidCards(list, type.split(";"), activator, source); - - - list = CardLists.filter(list, new Predicate() { - @Override - public boolean apply(final Card card) { - return card.hasCounters(); - } - }); - InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, list); - inp.setMessage("Select " + this.getDescriptiveType() + " to remove a counter"); - inp.setCancelAllowed(false); - inp.showAndWait(); - Card selected = inp.getFirstSelected(); - final Map tgtCounters = selected.getCounters(); - final ArrayList typeChoices = new ArrayList(); - for (CounterType key : tgtCounters.keySet()) { - if (tgtCounters.get(key) > 0) { - typeChoices.add(key); - } - } - - String prompt = "Select type counters to remove"; - counterType = GuiChoose.one(prompt, typeChoices); - - return PaymentDecision.card(selected, counterType); - } - /* * (non-Javadoc) * diff --git a/forge-gui/src/main/java/forge/game/cost/CostRemoveCounter.java b/forge-gui/src/main/java/forge/game/cost/CostRemoveCounter.java index 39ed75d05e0..c4c0726e1c6 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostRemoveCounter.java +++ b/forge-gui/src/main/java/forge/game/cost/CostRemoveCounter.java @@ -17,16 +17,9 @@ */ package forge.game.cost; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - import com.google.common.collect.Lists; -import forge.game.GameEntity; import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; @@ -34,8 +27,6 @@ import forge.game.card.CounterType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; -import forge.gui.input.InputSelectManyBase; /** * The Class CostRemoveCounter. @@ -48,170 +39,17 @@ public class CostRemoveCounter extends CostPartWithList { // Counter is tough), // Quillspike, Rift Elemental, Sage of Fables, Spike Rogue - /** - * TODO: Write javadoc for this type. - * - */ - public static final class InputSelectCardToRemoveCounter extends InputSelectManyBase { - private static final long serialVersionUID = 2685832214519141903L; - private final Map cardsChosen; - private final CounterType counterType; - private final List validChoices; - - public InputSelectCardToRemoveCounter(int cntCounters, CounterType cType, List validCards) { - super(cntCounters, cntCounters); - this.validChoices = validCards; - counterType = cType; - cardsChosen = cntCounters > 0 ? new HashMap() : null; - } - - @Override - protected void onCardSelected(Card c, java.awt.event.MouseEvent triggerEvent) { - if (!isValidChoice(c) || c.getCounters(counterType) <= getTimesSelected(c)) { - return; - } - - int tc = getTimesSelected(c); - cardsChosen.put(c, tc+1); - - onSelectStateChanged(c, true); - refresh(); - }; - - @Override - protected boolean hasEnoughTargets() { - return hasAllTargets(); - } - - @Override - protected boolean hasAllTargets() { - int sum = getDistibutedCounters(); - return sum >= max; - } - - protected String getMessage() { - return max == Integer.MAX_VALUE - ? String.format(message, getDistibutedCounters()) - : String.format(message, max - getDistibutedCounters()); - } - - private int getDistibutedCounters() { - int sum = 0; - for(Entry kv : cardsChosen.entrySet()) { - sum += kv.getValue().intValue(); - } - return sum; - } - - protected final boolean isValidChoice(GameEntity choice) { - return validChoices.contains(choice); - } - - public int getTimesSelected(Card c) { - return cardsChosen.containsKey(c) ? cardsChosen.get(c).intValue() : 0; - } - - @Override - public Collection getSelected() { - return cardsChosen.keySet(); - } - } - - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final String amount = this.getAmount(); - final Card source = ability.getSourceCard(); - Integer c = this.convertAmount(); - final String type = this.getType(); - - String sVarAmount = ability.getSVar(amount); - cntRemoved = 1; - if (c != null) - cntRemoved = c.intValue(); - else if (!"XChoice".equals(sVarAmount)) { - cntRemoved = AbilityUtils.calculateAmount(source, amount, ability); - } - - if (this.payCostFromSource()) { - int maxCounters = source.getCounters(this.counter); - if (amount.equals("All")) - cntRemoved = maxCounters; - else if ( c == null && "XChoice".equals(sVarAmount)) { - cntRemoved = chooseXValue(source, ability, maxCounters); - } - - if (maxCounters < cntRemoved) - return null; - PaymentDecision res = PaymentDecision.card(source); - res.c = cntRemoved >= 0 ? cntRemoved : maxCounters; - return res; - } else if (type.equals("OriginalHost")) { - int maxCounters = ability.getOriginalHost().getCounters(this.counter); - if (amount.equals("All")) { - cntRemoved = maxCounters; - } - if (maxCounters < cntRemoved) - return null; - - PaymentDecision res = PaymentDecision.card(ability.getOriginalHost()); - res.c = cntRemoved >= 0 ? cntRemoved : maxCounters; - return res; - } - - List validCards = CardLists.getValidCards(activator.getCardsIn(getZone()), type.split(";"), activator, source); - if (this.getZone().equals(ZoneType.Battlefield)) { - final InputSelectCardToRemoveCounter inp = new InputSelectCardToRemoveCounter(cntRemoved, getCounter(), validCards); - inp.setMessage("Remove %d " + getCounter().getName() + " counters from " + getDescriptiveType()); - inp.setCancelAllowed(true); - inp.showAndWait(); - if(inp.hasCancelled()) - return null; - - // Have to hack here: remove all counters minus one, without firing any triggers, - // triggers will fire when last is removed by executePayment. - // They don't care how many were removed anyway - // int sum = 0; - for(Card crd : inp.getSelected()) { - int removed = inp.getTimesSelected(crd); - // sum += removed; - if(removed < 2) continue; - int oldVal = crd.getCounters().get(getCounter()).intValue(); - crd.getCounters().put(getCounter(), Integer.valueOf(oldVal - removed + 1)); - } - cntRemoved = 1; - return PaymentDecision.card(inp.getSelected()); - } - - // Rift Elemental only - always removes 1 counter, so there will be no code for N counters. - List suspended = new ArrayList(); - for(Card crd : validCards) - if(crd.getCounters( getCounter()) > 0 ) - suspended.add(crd); - - final Card card = GuiChoose.oneOrNone("Remove counter(s) from a card in " + getZone(), suspended); - return null == card ? null : PaymentDecision.card(card); - } - - private final CounterType counter; - private final ZoneType zone; + public final CounterType counter; + public final ZoneType zone; private int cntRemoved; /** - * Gets the counter. - * - * @return the counter + * @param cntRemoved the cntRemoved to set */ - public final CounterType getCounter() { - return this.counter; - } - - /** - * @return the zone - */ - public final ZoneType getZone() { - return zone; + public void setCntRemoved(int cntRemoved) { + this.cntRemoved = cntRemoved; } /** @@ -293,7 +131,7 @@ public class CostRemoveCounter extends CostPartWithList { */ @Override public final boolean canPay(final SpellAbility ability) { - final CounterType cntrs = this.getCounter(); + final CounterType cntrs = this.counter; final Player activator = ability.getActivatingPlayer(); final Card source = ability.getSourceCard(); final String type = this.getType(); @@ -309,7 +147,7 @@ public class CostRemoveCounter extends CostPartWithList { if (type.equals("OriginalHost")) { typeList = Lists.newArrayList(ability.getOriginalHost()); } else { - typeList = CardLists.getValidCards(activator.getCardsIn(this.getZone()), type.split(";"), activator, source); + typeList = CardLists.getValidCards(activator.getCardsIn(this.zone), type.split(";"), activator, source); } if (amount != null) { for (Card c : typeList) { @@ -354,7 +192,7 @@ public class CostRemoveCounter extends CostPartWithList { @Override protected void doPayment(SpellAbility ability, Card targetCard){ - targetCard.subtractCounter(this.getCounter(), cntRemoved); + targetCard.subtractCounter(this.counter, cntRemoved); } /* (non-Javadoc) diff --git a/forge-gui/src/main/java/forge/game/cost/CostReturn.java b/forge-gui/src/main/java/forge/game/cost/CostReturn.java index f6e91b69093..0b379ae46c7 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostReturn.java +++ b/forge-gui/src/main/java/forge/game/cost/CostReturn.java @@ -20,13 +20,11 @@ package forge.game.cost; import java.util.ArrayList; import java.util.List; -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.input.InputSelectCardsFromList; /** * The Class CostReturn. @@ -108,49 +106,6 @@ public class CostReturn extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player payer) { - final String amount = this.getAmount(); - final Card source = ability.getSourceCard(); - Integer c = this.convertAmount(); - - final List list = payer.getCardsIn(ZoneType.Battlefield); - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, list.size()); - } else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - if (this.payCostFromSource()) { - final Card card = ability.getSourceCard(); - if (card.getController() == payer && card.isInPlay()) { - return payer.getController().confirmPayment(this, "Return " + card.getName() + " to hand?") ? PaymentDecision.card(card) : null; - } - } - else { - List validCards = CardLists.getValidCards(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); - - InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, validCards); - inp.setMessage("Return %d " + this.getType() + " " + this.getType() + " card(s) to hand"); - inp.showAndWait(); - if (inp.hasCancelled()) - return null; - - return PaymentDecision.card(inp.getSelected()); - } - return null; - } - /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ diff --git a/forge-gui/src/main/java/forge/game/cost/CostReveal.java b/forge-gui/src/main/java/forge/game/cost/CostReveal.java index dedf5d07b8f..d3bc9f62f40 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostReveal.java +++ b/forge-gui/src/main/java/forge/game/cost/CostReveal.java @@ -17,24 +17,18 @@ */ package forge.game.cost; -import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collection; import java.util.List; import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; -import forge.game.card.CardPredicates; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.input.InputSelectCardsFromList; -import forge.util.Lang; /** * The Class CostReveal. @@ -115,86 +109,6 @@ public class CostReveal extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final Card source = ability.getSourceCard(); - final String amount = this.getAmount(); - - if (this.payCostFromSource()) - return PaymentDecision.card(source); - - if (this.getType().equals("Hand")) - return PaymentDecision.card(activator.getCardsIn(ZoneType.Hand)); - - InputSelectCardsFromList inp = null; - if (this.getType().equals("SameColor")) { - Integer num = this.convertAmount(); - List handList = activator.getCardsIn(ZoneType.Hand); - final List handList2 = handList; - handList = CardLists.filter(handList, new Predicate() { - @Override - public boolean apply(final Card c) { - for (Card card : handList2) { - if (!card.equals(c) && card.sharesColorWith(c)) { - return true; - } - } - return false; - } - }); - if (num == 0) - return PaymentDecision.number(0); - - inp = new InputSelectCardsFromList(num, handList) { - private static final long serialVersionUID = 8338626212893374798L; - - @Override - protected void onCardSelected(Card c, MouseEvent triggerEvent) { - Card firstCard = Iterables.getFirst(this.selected, null); - if(firstCard != null && !CardPredicates.sharesColorWith(firstCard).apply(c)) - return; - super.onCardSelected(c, triggerEvent); - } - }; - inp.setMessage("Select " + Lang.nounWithAmount(num, "card" ) + " of same color to reveal."); - - } else { - Integer num = this.convertAmount(); - - List handList = activator.getCardsIn(ZoneType.Hand); - handList = CardLists.getValidCards(handList, this.getType().split(";"), activator, ability.getSourceCard()); - - if (num == null) { - final String sVar = ability.getSVar(amount); - if (sVar.equals("XChoice")) { - num = chooseXValue(source, ability, handList.size()); - } else { - num = AbilityUtils.calculateAmount(source, amount, ability); - } - } - if ( num == 0 ) - return PaymentDecision.number(0);; - - inp = new InputSelectCardsFromList(num, num, handList); - inp.setMessage("Select %d more " + getDescriptiveType() + " card(s) to reveal."); - } - inp.setCancelAllowed(true); - inp.showAndWait(); - if (inp.hasCancelled()) - return null; - - return PaymentDecision.card(inp.getSelected()); - - - } - /* * (non-Javadoc) * diff --git a/forge-gui/src/main/java/forge/game/cost/CostSacrifice.java b/forge-gui/src/main/java/forge/game/cost/CostSacrifice.java index 3eb602da256..6e300762627 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostSacrifice.java +++ b/forge-gui/src/main/java/forge/game/cost/CostSacrifice.java @@ -20,13 +20,11 @@ package forge.game.cost; import java.util.ArrayList; import java.util.List; -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.input.InputSelectCardsFromList; /** * The Class CostSacrifice. @@ -117,59 +115,6 @@ public class CostSacrifice extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final String amount = this.getAmount(); - final Card source = ability.getSourceCard(); - final String type = this.getType(); - - List list = new ArrayList(activator.getCardsIn(ZoneType.Battlefield)); - list = CardLists.getValidCards(list, type.split(";"), activator, source); - if (activator.hasKeyword("You can't sacrifice creatures to cast spells or activate abilities.")) { - list = CardLists.getNotType(list, "Creature"); - } - - if (this.payCostFromSource()) { - if (source.getController() == ability.getActivatingPlayer() && source.isInPlay()) { - return activator.getController().confirmPayment(this, "Sacrifice " + source.getName() + "?") ? PaymentDecision.card(source) : null; - } else - return null; - } - - if (amount.equals("All")) - return PaymentDecision.card(list); - - - Integer c = this.convertAmount(); - if (c == null) { - // Generalize this - if (ability.getSVar(amount).equals("XChoice")) { - c = chooseXValue(source, ability, list.size()); - } else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - if (0 == c.intValue()) { - return PaymentDecision.number(0); - } - - InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, list); - inp.setMessage("Select a " + this.getDescriptiveType() + " to sacrifice (%d left)"); - inp.setCancelAllowed(true); - inp.showAndWait(); - if ( inp.hasCancelled() ) - return null; - - return PaymentDecision.card(inp.getSelected()); - } - @Override protected void doPayment(SpellAbility ability, Card targetCard) { targetCard.getGame().getAction().sacrifice(targetCard, ability); diff --git a/forge-gui/src/main/java/forge/game/cost/CostTap.java b/forge-gui/src/main/java/forge/game/cost/CostTap.java index 41f9fd19578..8e0a92b83d4 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostTap.java +++ b/forge-gui/src/main/java/forge/game/cost/CostTap.java @@ -83,23 +83,6 @@ public class CostTap extends CostPart { ability.getSourceCard().tap(); return true; } - - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - // if (!canPay(ability, source, ability.getActivatingPlayer(), - // payment.getCost())) - // return false; - - return PaymentDecision.number(1); - } - public T accept(ICostVisitor visitor) { return visitor.visit(this); diff --git a/forge-gui/src/main/java/forge/game/cost/CostTapType.java b/forge-gui/src/main/java/forge/game/cost/CostTapType.java index 90ba583dcb3..2f01378ba36 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostTapType.java +++ b/forge-gui/src/main/java/forge/game/cost/CostTapType.java @@ -22,14 +22,12 @@ import java.util.List; import com.google.common.base.Predicate; -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CardPredicates.Presets; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.input.InputSelectCardsFromList; /** * The Class CostTapType. @@ -161,107 +159,6 @@ public class CostTapType extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - List typeList = new ArrayList(activator.getCardsIn(ZoneType.Battlefield)); - String type = this.getType(); - final String amount = this.getAmount(); - final Card source = ability.getSourceCard(); - Integer c = this.convertAmount(); - - boolean sameType = false; - if (type.contains("sharesCreatureTypeWith")) { - sameType = true; - type = type.replace("sharesCreatureTypeWith", ""); - } - - boolean totalPower = false; - String totalP = ""; - if (type.contains("+withTotalPowerGE")) { - totalPower = true; - totalP = type.split("withTotalPowerGE")[1]; - type = type.replace("+withTotalPowerGE" + totalP, ""); - } - - typeList = CardLists.getValidCards(typeList, type.split(";"), activator, ability.getSourceCard()); - typeList = CardLists.filter(typeList, Presets.UNTAPPED); - if (c == null && !amount.equals("Any")) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, typeList.size()); - } else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - - if (sameType) { - final List List2 = typeList; - typeList = CardLists.filter(typeList, new Predicate() { - @Override - public boolean apply(final Card c) { - for (Card card : List2) { - if (!card.equals(c) && card.sharesCreatureTypeWith(c)) { - return true; - } - } - return false; - } - }); - if (c == 0) return PaymentDecision.number(0); - List tapped = new ArrayList(); - while (c > 0) { - InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, typeList); - inp.setMessage("Select one of the cards to tap. Already chosen: " + tapped); - inp.setCancelAllowed(true); - inp.showAndWait(); - if (inp.hasCancelled()) - return null; - final Card first = inp.getFirstSelected(); - tapped.add(first); - typeList = CardLists.filter(typeList, new Predicate() { - @Override - public boolean apply(final Card c) { - return c.sharesCreatureTypeWith(first); - } - }); - typeList.remove(first); - c--; - } - return PaymentDecision.card(tapped); - } - - if (totalPower) { - int i = Integer.parseInt(totalP); - InputSelectCardsFromList inp = new InputSelectCardsFromList(0, typeList.size(), typeList); - inp.setMessage("Select a card to tap."); - inp.setUnselectAllowed(true); - inp.setCancelAllowed(true); - inp.showAndWait(); - - if (inp.hasCancelled() || CardLists.getTotalPower(inp.getSelected()) < i) { - return null; - } else { - return PaymentDecision.card(inp.getSelected()); - } - } - - InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, typeList); - inp.setMessage("Select a " + getDescriptiveType() + " to tap (%d left)"); - inp.showAndWait(); - if ( inp.hasCancelled() ) - return null; - - return PaymentDecision.card(inp.getSelected()); - } - /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ diff --git a/forge-gui/src/main/java/forge/game/cost/CostUnattach.java b/forge-gui/src/main/java/forge/game/cost/CostUnattach.java index 3203ceddb57..39e8afe875c 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostUnattach.java +++ b/forge-gui/src/main/java/forge/game/cost/CostUnattach.java @@ -86,24 +86,6 @@ public class CostUnattach extends CostPartWithList { return false; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - final Card source = ability.getSourceCard(); - - Card cardToUnattach = findCardToUnattach(source, activator, ability); - if (cardToUnattach != null && activator.getController().confirmPayment(this, "Unattach " + cardToUnattach.getName() + "?")) { - return PaymentDecision.card(cardToUnattach); - } - return null; - } - public Card findCardToUnattach(final Card source, Player activator, SpellAbility ability) { if (getType().equals("CARDNAME")) { if (source.isEquipping()) { diff --git a/forge-gui/src/main/java/forge/game/cost/CostUntap.java b/forge-gui/src/main/java/forge/game/cost/CostUntap.java index b80fa599fd0..4bb8cdb5235 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostUntap.java +++ b/forge-gui/src/main/java/forge/game/cost/CostUntap.java @@ -75,18 +75,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#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player activator) { - return PaymentDecision.number(1); - } - /* (non-Javadoc) * @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card) */ diff --git a/forge-gui/src/main/java/forge/game/cost/CostUntapType.java b/forge-gui/src/main/java/forge/game/cost/CostUntapType.java index 50ae3a20c03..1992bd28194 100644 --- a/forge-gui/src/main/java/forge/game/cost/CostUntapType.java +++ b/forge-gui/src/main/java/forge/game/cost/CostUntapType.java @@ -18,14 +18,12 @@ package forge.game.cost; import java.util.List; -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CardPredicates.Presets; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.gui.input.InputSelectCardsFromList; /** * The Class CostUntapType. @@ -120,41 +118,6 @@ public class CostUntapType extends CostPartWithList { return true; } - /* - * (non-Javadoc) - * - * @see - * forge.card.cost.CostPart#payHuman(forge.card.spellability.SpellAbility, - * forge.Card, forge.card.cost.Cost_Payment) - */ - @Override - public final PaymentDecision payHuman(final SpellAbility ability, final Player payer) { - List typeList = CardLists.getValidCards(payer.getGame().getCardsIn(ZoneType.Battlefield), this.getType().split(";"), - payer, ability.getSourceCard()); - typeList = CardLists.filter(typeList, Presets.TAPPED); - final Card source = ability.getSourceCard(); - if (!canUntapSource) { - typeList.remove(source); - } - final String amount = this.getAmount(); - Integer c = this.convertAmount(); - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = chooseXValue(source, ability, typeList.size()); - } else { - c = AbilityUtils.calculateAmount(source, amount, ability); - } - } - InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, typeList); - inp.setMessage("Select a " + getDescriptiveType() + " to untap (%d left)"); - inp.showAndWait(); - if( inp.hasCancelled() || inp.getSelected().size() != c ) - return null; - return PaymentDecision.card(inp.getSelected()); - } - /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ diff --git a/forge-gui/src/main/java/forge/gui/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/gui/player/HumanCostDecision.java new file mode 100644 index 00000000000..a1a068b3dd3 --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/player/HumanCostDecision.java @@ -0,0 +1,1156 @@ +package forge.gui.player; + +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import forge.card.CardType; +import forge.game.Game; +import forge.game.GameEntity; +import forge.game.ability.AbilityUtils; +import forge.game.card.Card; +import forge.game.card.CardLists; +import forge.game.card.CardPredicates; +import forge.game.card.CounterType; +import forge.game.card.CardPredicates.Presets; +import forge.game.cost.CostAddMana; +import forge.game.cost.CostChooseCreatureType; +import forge.game.cost.CostDamage; +import forge.game.cost.CostDiscard; +import forge.game.cost.CostDraw; +import forge.game.cost.CostExile; +import forge.game.cost.CostExiledMoveToGrave; +import forge.game.cost.CostFlipCoin; +import forge.game.cost.CostGainControl; +import forge.game.cost.CostGainLife; +import forge.game.cost.CostMill; +import forge.game.cost.CostPartMana; +import forge.game.cost.CostPayLife; +import forge.game.cost.CostPutCardToLib; +import forge.game.cost.CostPutCounter; +import forge.game.cost.CostRemoveAnyCounter; +import forge.game.cost.CostRemoveCounter; +import forge.game.cost.CostReturn; +import forge.game.cost.CostReveal; +import forge.game.cost.CostSacrifice; +import forge.game.cost.CostTap; +import forge.game.cost.CostTapType; +import forge.game.cost.CostUnattach; +import forge.game.cost.CostUntap; +import forge.game.cost.CostUntapType; +import forge.game.cost.ICostVisitor; +import forge.game.cost.PaymentDecision; +import forge.game.player.Player; +import forge.game.spellability.SpellAbility; +import forge.game.spellability.SpellAbilityStackInstance; +import forge.game.zone.ZoneType; +import forge.gui.GuiChoose; +import forge.gui.input.InputSelectCardsFromList; +import forge.gui.input.InputSelectManyBase; +import forge.util.Aggregates; +import forge.util.Lang; + +public class HumanCostDecision implements ICostVisitor { + + private final Player payer; + private final SpellAbility ability; + private final Card source; + + public HumanCostDecision(Player p, SpellAbility sa, Card source) { + payer = p; + ability = sa; + this.source = source; + } + + protected int chooseXValue(final int maxValue) { + /*final String chosen = sa.getSVar("ChosenX"); + if (chosen.length() > 0) { + return AbilityFactory.calculateAmount(card, "ChosenX", null); + }*/ + + int chosenX = payer.getController().chooseNumber(ability, source.toString() + " - Choose a Value for X", 0, maxValue); + ability.setSVar("ChosenX", Integer.toString(chosenX)); + source.setSVar("ChosenX", Integer.toString(chosenX)); + return chosenX; + } + + @Override + public PaymentDecision visit(CostAddMana cost) { + Integer c = cost.convertAmount(); + if (c == null) { + c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); + } + return PaymentDecision.number(c); + } + + @Override + public PaymentDecision visit(CostChooseCreatureType cost) { + String choice = payer.getController().chooseSomeType("Creature", ability, new ArrayList(CardType.getCreatureTypes()), new ArrayList(), true); + if( null == choice ) + return null; + return PaymentDecision.type(choice); + } + + @Override + public PaymentDecision visit(CostDiscard cost) { + List handList = new ArrayList(payer.getCardsIn(ZoneType.Hand)); + String discardType = cost.getType(); + final String amount = cost.getAmount(); + + if (cost.payCostFromSource()) { + return handList.contains(source) ? PaymentDecision.card(source) : null; + } + + if (discardType.equals("Hand")) { + return PaymentDecision.card(handList); + } + + if (discardType.equals("LastDrawn")) { + final Card lastDrawn = payer.getLastDrawnCard(); + return handList.contains(lastDrawn) ? PaymentDecision.card(lastDrawn) : null; + } + + Integer c = cost.convertAmount(); + + if (discardType.equals("Random")) { + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(handList.size()); + } + else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + + return PaymentDecision.card(Aggregates.random(handList, c)); + } + if (discardType.contains("+WithSameName")) { + String type = discardType.replace("+WithSameName", ""); + handList = CardLists.getValidCards(handList, type.split(";"), payer, source); + final List landList2 = handList; + 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 == 0) { return PaymentDecision.card(Lists.newArrayList()); } + List discarded = new ArrayList(); + while (c > 0) { + InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, handList); + inp.setMessage("Select one of the cards with the same name to discard. Already chosen: " + discarded); + inp.setCancelAllowed(true); + inp.showAndWait(); + if (inp.hasCancelled()) { + return null; + } + final Card first = inp.getFirstSelected(); + discarded.add(first); + handList = CardLists.filter(handList, CardPredicates.nameEquals(first.getName())); + handList.remove(first); + c--; + } + return PaymentDecision.card(discarded); + } + + String type = new String(discardType); + final String[] validType = type.split(";"); + handList = CardLists.getValidCards(handList, validType, payer, source); + + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(handList.size()); + } + else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + + InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, handList); + inp.setMessage("Select %d more " + cost.getDescriptiveType() + " to discard."); + inp.setCancelAllowed(true); + inp.showAndWait(); + if (inp.hasCancelled() || inp.getSelected().size() != c) { + return null; + } + + return PaymentDecision.card(inp.getSelected()); + } + + @Override + public PaymentDecision visit(CostDamage cost) { + final String amount = cost.getAmount(); + final int life = payer.getLife(); + + Integer c = cost.convertAmount(); + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(life); + } + else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + + if (payer.canPayLife(c) && payer.getController().confirmPayment(cost, "Pay " + c + " Life?")) { + return PaymentDecision.number(c); + } + return null; + } + + @Override + public PaymentDecision visit(CostDraw cost) { + final String amount = cost.getAmount(); + + Integer c = cost.convertAmount(); + if (c == null) { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + + if (!payer.getController().confirmPayment(cost, "Draw " + c + " Card" + (c == 1 ? "" : "s"))) { + return null; + } + + return PaymentDecision.number(c); + } + + @Override + public PaymentDecision visit(CostExile cost) { + final String amount = cost.getAmount(); + final Game game = payer.getGame(); + + Integer c = cost.convertAmount(); + String type = cost.getType(); + boolean fromTopGrave = false; + if (type.contains("FromTopGrave")) { + type = type.replace("FromTopGrave", ""); + fromTopGrave = true; + } + + List list; + if (cost.getFrom().equals(ZoneType.Stack)) { + list = new ArrayList(); + for (SpellAbilityStackInstance si : game.getStack()) { + list.add(si.getSourceCard()); + } + } + else if (cost.sameZone) { + list = new ArrayList(game.getCardsIn(cost.from)); + } + else { + list = new ArrayList(payer.getCardsIn(cost.from)); + } + + if (cost.payCostFromSource()) { + return source.getZone() == payer.getZone(cost.from) && payer.getController().confirmPayment(cost, "Exile " + source.getName() + "?") ? PaymentDecision.card(source) : null; + + } + + if (type.equals("All")) { + return PaymentDecision.card(list); + } + list = CardLists.getValidCards(list, type.split(";"), payer, source); + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(list.size()); + } + else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + + if (cost.from == ZoneType.Battlefield || cost.from == ZoneType.Hand) { + InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, list); + inp.setMessage("Exile %d card(s) from your" + cost.from); + inp.setCancelAllowed(true); + inp.showAndWait(); + return inp.hasCancelled() ? null : PaymentDecision.card(inp.getSelected()); + } + + if (cost.from == ZoneType.Stack) { return exileFromStack(cost, ability, c); } + if (cost.from == ZoneType.Library) { return exileFromTop(cost, ability, payer, c); } + if (fromTopGrave) { return exileFromTopGraveType(ability, c, list); } + if (!cost.sameZone) { return exileFromMiscZone(cost, ability, c, list); } + + List players = game.getPlayers(); + List payableZone = new ArrayList(); + for (Player p : players) { + List enoughType = CardLists.filter(list, CardPredicates.isOwner(p)); + if (enoughType.size() < c) { + list.removeAll(enoughType); + } + else { + payableZone.add(p); + } + } + return exileFromSame(cost, list, c, payableZone); + } + + + + // Inputs + + // Exile + // ExileFromHand + // ExileFromGrave + // ExileFromTop (of library) + // ExileSameGrave + + private PaymentDecision exileFromSame(CostExile cost, List list, int nNeeded, List payableZone) { + if (nNeeded == 0) { + return PaymentDecision.number(0); + } + + final Player p = GuiChoose.oneOrNone(String.format("Exile from whose %s?", cost.getFrom()), payableZone); + if (p == null) { + return null; + } + + List typeList = CardLists.filter(list, CardPredicates.isOwner(p)); + if(typeList.size() < nNeeded) + return null; + + List toExile = GuiChoose.many("Exile from " + cost.getFrom(), "To be exiled", nNeeded, typeList, null); + return PaymentDecision.card(toExile); + } + + private PaymentDecision exileFromStack(CostExile cost, SpellAbility sa, int nNeeded) { + if (nNeeded == 0) { + return PaymentDecision.number(0); + } + + final Game game = sa.getActivatingPlayer().getGame(); + ArrayList saList = new ArrayList(); + ArrayList descList = new ArrayList(); + + for (SpellAbilityStackInstance si : game.getStack()) { + final Card stC = si.getSourceCard(); + final SpellAbility stSA = si.getSpellAbility().getRootAbility(); + if (stC.isValid(cost.getType().split(";"), sa.getActivatingPlayer(), sa.getSourceCard()) && stSA.isSpell()) { + saList.add(stSA); + if (stC.isCopiedSpell()) { + descList.add(stSA.getStackDescription() + " (Copied Spell)"); + } else { + descList.add(stSA.getStackDescription()); + } + } + } + + if (saList.size() < nNeeded) { + return null; + } + + List exiled = new ArrayList(); + for (int i = 0; i < nNeeded; i++) { + //Have to use the stack descriptions here because some copied spells have no description otherwise + final String o = GuiChoose.oneOrNone("Exile from " + cost.getFrom(), descList); + + if (o != null) { + final SpellAbility toExile = saList.get(descList.indexOf(o)); + final Card c = toExile.getSourceCard(); + + saList.remove(toExile); + descList.remove(o); + + exiled.add(c); + } + else { + return null; + } + } + return PaymentDecision.card(exiled); + } + + private PaymentDecision exileFromTop(final CostExile cost, final SpellAbility sa, final Player payer, final int nNeeded) { + final StringBuilder sb = new StringBuilder(); + sb.append("Exile ").append(nNeeded).append(" cards from the top of your library?"); + final List list = payer.getCardsIn(ZoneType.Library, nNeeded); + + if (list.size() > nNeeded || !payer.getController().confirmPayment(cost, "Exile " + Lang.nounWithAmount(nNeeded, "card") + " from the top of your library?")) { + return null; + } + + return PaymentDecision.card(list); + } + + private PaymentDecision exileFromMiscZone(CostExile cost, SpellAbility sa, int nNeeded, List typeList) { + if (typeList.size() < nNeeded) + return null; + + List exiled = new ArrayList(); + for (int i = 0; i < nNeeded; i++) { + final Card c = GuiChoose.oneOrNone("Exile from " + cost.getFrom(), typeList); + + if (c != null) { + typeList.remove(c); + exiled.add(c); + } else { + return null; + } + } + return PaymentDecision.card(exiled); + } + + private PaymentDecision exileFromTopGraveType(SpellAbility sa, int nNeeded, List typeList) { + if (typeList.size() < nNeeded) + return null; + + Collections.reverse(typeList); + return PaymentDecision.card(Lists.newArrayList(Iterables.limit(typeList, nNeeded))); + } + + @Override + public PaymentDecision visit(CostExiledMoveToGrave cost) { + Integer c = cost.convertAmount(); + if (c == null) { + c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); + } + + final Player activator = ability.getActivatingPlayer(); + List list = activator.getGame().getCardsIn(ZoneType.Exile); + list = CardLists.getValidCards(list, cost.getType().split(";"), activator, source); + + if (list.size() < c) + return null; + + return PaymentDecision.card(GuiChoose.many("Choose an exiled card to put into graveyard", "To graveyard", c, list, source)); + } + + @Override + public PaymentDecision visit(CostFlipCoin cost) { + final String amount = cost.getAmount(); + Integer c = cost.convertAmount(); + + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(cost.getList().size()); + } else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + return PaymentDecision.number(c); + } + + @Override + public PaymentDecision visit(CostGainControl cost) { + final String amount = cost.getAmount(); + + Integer c = cost.convertAmount(); + if (c == null) { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + final List list = payer.getCardsIn(ZoneType.Battlefield); + List validCards = CardLists.getValidCards(list, cost.getType().split(";"), payer, source); + + InputSelectCardsFromList inp = new InputSelectCardsFromList(c, validCards); + final String desc = cost.getTypeDescription() == null ? cost.getType() : cost.getTypeDescription(); + inp.setMessage("Gain control of %d " + desc); + inp.showAndWait(); + if (inp.hasCancelled()) { + return null; + } + return PaymentDecision.card(inp.getSelected()); + } + + @Override + public PaymentDecision visit(CostGainLife cost) { + final String amount = cost.getAmount(); + + final int life = payer.getLife(); + + Integer c = cost.convertAmount(); + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(life); + } else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + + final List oppsThatCanGainLife = new ArrayList(); + for (final Player opp : cost.getPotentialTargets(payer, source)) { + if (opp.canGainLife()) { + oppsThatCanGainLife.add(opp); + } + } + + if (cost.getCntPlayers() == Integer.MAX_VALUE) // applied to all players who can gain + return PaymentDecision.players(oppsThatCanGainLife); + + final StringBuilder sb = new StringBuilder(); + sb.append(source.getName()).append(" - Choose an opponent to gain ").append(c).append(" life:"); + + final Player chosenToGain = GuiChoose.oneOrNone(sb.toString(), oppsThatCanGainLife); + if (null == chosenToGain) + return null; + else + return PaymentDecision.players(Lists.newArrayList(chosenToGain)); + } + + @Override + public PaymentDecision visit(CostMill cost) { + final String amount = cost.getAmount(); + Integer c = cost.convertAmount(); + + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(cost.getList().size()); + } else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + + if (!payer.getController().confirmPayment(cost, "Mill " + c + " card" + (c == 1 ? "" : "s") + " from your library?")) { + return null; + } + return PaymentDecision.card(payer.getCardsIn(ZoneType.Library, c)); + } + + @Override + public PaymentDecision visit(CostPayLife cost) { + final String amount = cost.getAmount(); + final int life = payer.getLife(); + + Integer c = cost.convertAmount(); + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.startsWith("XChoice")) { + int limit = life; + if (sVar.contains("LimitMax")) { + limit = AbilityUtils.calculateAmount(source, sVar.split("LimitMax.")[1], ability); + } + int maxLifePayment = limit < life ? limit : life; + c = chooseXValue(maxLifePayment); + } else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + + if (payer.canPayLife(c) && payer.getController().confirmPayment(cost, "Pay " + c + " Life?")) { + return PaymentDecision.number(c); + } + return null; + } + + @Override + public PaymentDecision visit(CostPartMana cost) { + // only interactive payment possible for now =( + return new PaymentDecision(0); + } + + @Override + public PaymentDecision visit(CostPutCardToLib cost) { + final String amount = cost.getAmount(); + Integer c = cost.convertAmount(); + + List list = cost.sameZone ? payer.getGame().getCardsIn(cost.getFrom()) : payer.getCardsIn(cost.getFrom()); + + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(cost.getList().size()); + } else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + + list = CardLists.getValidCards(list, cost.getType().split(";"), payer, source); + + if (cost.from == ZoneType.Hand) { + InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, list); + inp.setMessage("Put %d card(s) from your " + cost.from ); + inp.setCancelAllowed(true); + inp.showAndWait(); + return inp.hasCancelled() ? null : PaymentDecision.card(inp.getSelected()); + } + + if (cost.sameZone){ + List players = payer.getGame().getPlayers(); + List payableZone = new ArrayList(); + for (Player p : players) { + List enoughType = CardLists.filter(list, CardPredicates.isOwner(p)); + if (enoughType.size() < c) { + list.removeAll(enoughType); + } else { + payableZone.add(p); + } + } + return putFromSame(list, c.intValue(), payableZone, cost.from); + } else {//Graveyard + return putFromMiscZone(ability, c.intValue(), list, cost.from); + } + } + + + private PaymentDecision putFromMiscZone(SpellAbility sa, int nNeeded, List typeList, ZoneType fromZone) { + if(typeList.size() < nNeeded) + return null; + + List chosen = new ArrayList<>(); + for (int i = 0; i < nNeeded; i++) { + final Card c = GuiChoose.oneOrNone("Put from " + fromZone + " to library", typeList); + + if (c == null) + return null; + + typeList.remove(c); + chosen.add(c); + } + return PaymentDecision.card(chosen); + } + + private PaymentDecision putFromSame(List list, int nNeeded, List payableZone, ZoneType fromZone) { + if (nNeeded == 0) { + return PaymentDecision.number(0); + } + + final Player p = GuiChoose.oneOrNone(String.format("Put cards from whose %s?", fromZone), payableZone); + if (p == null) { + return null; + } + + List typeList = CardLists.filter(list, CardPredicates.isOwner(p)); + if(typeList.size() < nNeeded) + return null; + + List chosen = new ArrayList<>(); + for (int i = 0; i < nNeeded; i++) { + final Card c = GuiChoose.oneOrNone("Put cards from " + fromZone + " to Library", typeList); + if (c == null) + return null; + typeList.remove(c); + chosen.add(c); + } + return PaymentDecision.card(chosen); + } + + @Override + public PaymentDecision visit(CostPutCounter cost) { + Integer c = cost.getNumberOfCounters(ability); + + if (cost.payCostFromSource()) { + cost.setLastPaidAmount(c); + return PaymentDecision.number(c); + } + + // Cards to use this branch: Scarscale Ritual, Wandering Mage - each adds only one counter + List typeList = CardLists.getValidCards(payer.getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), payer, ability.getSourceCard()); + + InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, typeList); + inp.setMessage("Put " + Lang.nounWithAmount(c, cost.getCounter().getName() + " counter") + " on " +cost.getDescriptiveType()); + inp.setCancelAllowed(true); + inp.showAndWait(); + + if(inp.hasCancelled()) + return null; + + return PaymentDecision.card(inp.getSelected()); + } + + @Override + public PaymentDecision visit(CostReturn cost) { + final String amount = cost.getAmount(); + Integer c = cost.convertAmount(); + + final List list = payer.getCardsIn(ZoneType.Battlefield); + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(list.size()); + } else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + if (cost.payCostFromSource()) { + final Card card = ability.getSourceCard(); + if (card.getController() == payer && card.isInPlay()) { + return payer.getController().confirmPayment(cost, "Return " + card.getName() + " to hand?") ? PaymentDecision.card(card) : null; + } + } + else { + List validCards = CardLists.getValidCards(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); + + InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, validCards); + inp.setMessage("Return %d " + cost.getType() + " " + cost.getType() + " card(s) to hand"); + inp.showAndWait(); + if (inp.hasCancelled()) + return null; + + return PaymentDecision.card(inp.getSelected()); + } + return null; + + } + + @Override + public PaymentDecision visit(CostReveal cost) { + final String amount = cost.getAmount(); + + if (cost.payCostFromSource()) + return PaymentDecision.card(source); + + if (cost.getType().equals("Hand")) + return PaymentDecision.card(payer.getCardsIn(ZoneType.Hand)); + + InputSelectCardsFromList inp = null; + if (cost.getType().equals("SameColor")) { + Integer num = cost.convertAmount(); + List handList = payer.getCardsIn(ZoneType.Hand); + final List handList2 = handList; + handList = CardLists.filter(handList, new Predicate() { + @Override + public boolean apply(final Card c) { + for (Card card : handList2) { + if (!card.equals(c) && card.sharesColorWith(c)) { + return true; + } + } + return false; + } + }); + if (num == 0) + return PaymentDecision.number(0); + + inp = new InputSelectCardsFromList(num, handList) { + private static final long serialVersionUID = 8338626212893374798L; + + @Override + protected void onCardSelected(Card c, MouseEvent triggerEvent) { + Card firstCard = Iterables.getFirst(this.selected, null); + if(firstCard != null && !CardPredicates.sharesColorWith(firstCard).apply(c)) + return; + super.onCardSelected(c, triggerEvent); + } + }; + inp.setMessage("Select " + Lang.nounWithAmount(num, "card" ) + " of same color to reveal."); + + } else { + Integer num = cost.convertAmount(); + + List handList = payer.getCardsIn(ZoneType.Hand); + handList = CardLists.getValidCards(handList, cost.getType().split(";"), payer, ability.getSourceCard()); + + if (num == null) { + final String sVar = ability.getSVar(amount); + if (sVar.equals("XChoice")) { + num = chooseXValue(handList.size()); + } else { + num = AbilityUtils.calculateAmount(source, amount, ability); + } + } + if ( num == 0 ) + return PaymentDecision.number(0);; + + inp = new InputSelectCardsFromList(num, num, handList); + inp.setMessage("Select %d more " + cost.getDescriptiveType() + " card(s) to reveal."); + } + inp.setCancelAllowed(true); + inp.showAndWait(); + if (inp.hasCancelled()) + return null; + + return PaymentDecision.card(inp.getSelected()); + } + + @Override + public PaymentDecision visit(CostRemoveAnyCounter cost) { + Integer c = cost.convertAmount(); + final String type = cost.getType(); + + if (c == null) { + c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); + } + + List list = new ArrayList(payer.getCardsIn(ZoneType.Battlefield)); + list = CardLists.getValidCards(list, type.split(";"), payer, source); + + + list = CardLists.filter(list, new Predicate() { + @Override + public boolean apply(final Card card) { + return card.hasCounters(); + } + }); + InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, list); + inp.setMessage("Select " + cost.getDescriptiveType() + " to remove a counter"); + inp.setCancelAllowed(false); + inp.showAndWait(); + Card selected = inp.getFirstSelected(); + final Map tgtCounters = selected.getCounters(); + final ArrayList typeChoices = new ArrayList(); + for (CounterType key : tgtCounters.keySet()) { + if (tgtCounters.get(key) > 0) { + typeChoices.add(key); + } + } + + String prompt = "Select type counters to remove"; + cost.setCounterType(GuiChoose.one(prompt, typeChoices)); + + return PaymentDecision.card(selected, cost.getCounter()); + } + + public static final class InputSelectCardToRemoveCounter extends InputSelectManyBase { + private static final long serialVersionUID = 2685832214519141903L; + + private final Map cardsChosen; + private final CounterType counterType; + private final List validChoices; + + public InputSelectCardToRemoveCounter(int cntCounters, CounterType cType, List validCards) { + super(cntCounters, cntCounters); + this.validChoices = validCards; + counterType = cType; + cardsChosen = cntCounters > 0 ? new HashMap() : null; + } + + @Override + protected void onCardSelected(Card c, java.awt.event.MouseEvent triggerEvent) { + if (!isValidChoice(c) || c.getCounters(counterType) <= getTimesSelected(c)) { + return; + } + + int tc = getTimesSelected(c); + cardsChosen.put(c, tc+1); + + onSelectStateChanged(c, true); + refresh(); + }; + + @Override + protected boolean hasEnoughTargets() { + return hasAllTargets(); + } + + @Override + protected boolean hasAllTargets() { + int sum = getDistibutedCounters(); + return sum >= max; + } + + protected String getMessage() { + return max == Integer.MAX_VALUE + ? String.format(message, getDistibutedCounters()) + : String.format(message, max - getDistibutedCounters()); + } + + private int getDistibutedCounters() { + int sum = 0; + for(Entry kv : cardsChosen.entrySet()) { + sum += kv.getValue().intValue(); + } + return sum; + } + + protected final boolean isValidChoice(GameEntity choice) { + return validChoices.contains(choice); + } + + public int getTimesSelected(Card c) { + return cardsChosen.containsKey(c) ? cardsChosen.get(c).intValue() : 0; + } + + @Override + public Collection getSelected() { + return cardsChosen.keySet(); + } + } + + @Override + public PaymentDecision visit(CostRemoveCounter cost) { + + final String amount = cost.getAmount(); + Integer c = cost.convertAmount(); + final String type = cost.getType(); + + String sVarAmount = ability.getSVar(amount); + int cntRemoved = 1; + if (c != null) + cntRemoved = c.intValue(); + else if (!"XChoice".equals(sVarAmount)) { + cntRemoved = AbilityUtils.calculateAmount(source, amount, ability); + } + + if (cost.payCostFromSource()) { + int maxCounters = source.getCounters(cost.counter); + if (amount.equals("All")) + cntRemoved = maxCounters; + else if ( c == null && "XChoice".equals(sVarAmount)) { + cntRemoved = chooseXValue(maxCounters); + } + cost.setCntRemoved(cntRemoved); + + if (maxCounters < cntRemoved) + return null; + PaymentDecision res = PaymentDecision.card(source); + res.c = cntRemoved >= 0 ? cntRemoved : maxCounters; + return res; + } else if (type.equals("OriginalHost")) { + int maxCounters = ability.getOriginalHost().getCounters(cost.counter); + if (amount.equals("All")) { + cntRemoved = maxCounters; + } + if (maxCounters < cntRemoved) + return null; + + PaymentDecision res = PaymentDecision.card(ability.getOriginalHost()); + res.c = cntRemoved >= 0 ? cntRemoved : maxCounters; + cost.setCntRemoved(cntRemoved); + return res; + } + + List validCards = CardLists.getValidCards(payer.getCardsIn(cost.zone), type.split(";"), payer, source); + if (cost.zone.equals(ZoneType.Battlefield)) { + final InputSelectCardToRemoveCounter inp = new InputSelectCardToRemoveCounter(cntRemoved, cost.counter, validCards); + inp.setMessage("Remove %d " + cost.counter.getName() + " counters from " + cost.getDescriptiveType()); + inp.setCancelAllowed(true); + inp.showAndWait(); + if(inp.hasCancelled()) + return null; + + // Have to hack here: remove all counters minus one, without firing any triggers, + // triggers will fire when last is removed by executePayment. + // They don't care how many were removed anyway + // int sum = 0; + for(Card crd : inp.getSelected()) { + int removed = inp.getTimesSelected(crd); + // sum += removed; + if(removed < 2) continue; + int oldVal = crd.getCounters().get(cost.counter).intValue(); + crd.getCounters().put(cost.counter, Integer.valueOf(oldVal - removed + 1)); + } + cost.setCntRemoved(1); + return PaymentDecision.card(inp.getSelected()); + } + + // Rift Elemental only - always removes 1 counter, so there will be no code for N counters. + List suspended = new ArrayList(); + for(Card crd : validCards) + if(crd.getCounters( cost.counter) > 0 ) + suspended.add(crd); + + final Card card = GuiChoose.oneOrNone("Remove counter(s) from a card in " + cost.zone, suspended); + return null == card ? null : PaymentDecision.card(card); + } + + @Override + public PaymentDecision visit(CostSacrifice cost) { + final String amount = cost.getAmount(); + final String type = cost.getType(); + + List list = new ArrayList(payer.getCardsIn(ZoneType.Battlefield)); + list = CardLists.getValidCards(list, type.split(";"), payer, source); + if (payer.hasKeyword("You can't sacrifice creatures to cast spells or activate abilities.")) { + list = CardLists.getNotType(list, "Creature"); + } + + if (cost.payCostFromSource()) { + if (source.getController() == ability.getActivatingPlayer() && source.isInPlay()) { + return payer.getController().confirmPayment(cost, "Sacrifice " + source.getName() + "?") ? PaymentDecision.card(source) : null; + } else + return null; + } + + if (amount.equals("All")) + return PaymentDecision.card(list); + + + Integer c = cost.convertAmount(); + if (c == null) { + // Generalize this + if (ability.getSVar(amount).equals("XChoice")) { + c = chooseXValue(list.size()); + } else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + if (0 == c.intValue()) { + return PaymentDecision.number(0); + } + + InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, list); + inp.setMessage("Select a " + cost.getDescriptiveType() + " to sacrifice (%d left)"); + inp.setCancelAllowed(true); + inp.showAndWait(); + if ( inp.hasCancelled() ) + return null; + + return PaymentDecision.card(inp.getSelected()); + + } + + @Override + public PaymentDecision visit(CostTap cost) { + // if (!canPay(ability, source, ability.getActivatingPlayer(), + // payment.getCost())) + // return false; + return PaymentDecision.number(1); + } + + @Override + public PaymentDecision visit(CostTapType cost) { + List typeList = new ArrayList(payer.getCardsIn(ZoneType.Battlefield)); + String type = cost.getType(); + final String amount = cost.getAmount(); + Integer c = cost.convertAmount(); + + boolean sameType = false; + if (type.contains("sharesCreatureTypeWith")) { + sameType = true; + type = type.replace("sharesCreatureTypeWith", ""); + } + + boolean totalPower = false; + String totalP = ""; + if (type.contains("+withTotalPowerGE")) { + totalPower = true; + totalP = type.split("withTotalPowerGE")[1]; + type = type.replace("+withTotalPowerGE" + totalP, ""); + } + + typeList = CardLists.getValidCards(typeList, type.split(";"), payer, ability.getSourceCard()); + typeList = CardLists.filter(typeList, Presets.UNTAPPED); + if (c == null && !amount.equals("Any")) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(typeList.size()); + } else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + + if (sameType) { + final List List2 = typeList; + typeList = CardLists.filter(typeList, new Predicate() { + @Override + public boolean apply(final Card c) { + for (Card card : List2) { + if (!card.equals(c) && card.sharesCreatureTypeWith(c)) { + return true; + } + } + return false; + } + }); + if (c == 0) return PaymentDecision.number(0); + List tapped = new ArrayList(); + while (c > 0) { + InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, typeList); + inp.setMessage("Select one of the cards to tap. Already chosen: " + tapped); + inp.setCancelAllowed(true); + inp.showAndWait(); + if (inp.hasCancelled()) + return null; + final Card first = inp.getFirstSelected(); + tapped.add(first); + typeList = CardLists.filter(typeList, new Predicate() { + @Override + public boolean apply(final Card c) { + return c.sharesCreatureTypeWith(first); + } + }); + typeList.remove(first); + c--; + } + return PaymentDecision.card(tapped); + } + + if (totalPower) { + int i = Integer.parseInt(totalP); + InputSelectCardsFromList inp = new InputSelectCardsFromList(0, typeList.size(), typeList); + inp.setMessage("Select a card to tap."); + inp.setUnselectAllowed(true); + inp.setCancelAllowed(true); + inp.showAndWait(); + + if (inp.hasCancelled() || CardLists.getTotalPower(inp.getSelected()) < i) { + return null; + } else { + return PaymentDecision.card(inp.getSelected()); + } + } + + InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, typeList); + inp.setMessage("Select a " + cost.getDescriptiveType() + " to tap (%d left)"); + inp.showAndWait(); + if ( inp.hasCancelled() ) + return null; + + return PaymentDecision.card(inp.getSelected()); + } + + @Override + public PaymentDecision visit(CostUntapType cost) { + List typeList = CardLists.getValidCards(payer.getGame().getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), + payer, ability.getSourceCard()); + typeList = CardLists.filter(typeList, Presets.TAPPED); + if (!cost.canUntapSource) { + typeList.remove(source); + } + final String amount = cost.getAmount(); + Integer c = cost.convertAmount(); + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = chooseXValue(typeList.size()); + } else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + InputSelectCardsFromList inp = new InputSelectCardsFromList(c, c, typeList); + inp.setMessage("Select a " + cost.getDescriptiveType() + " to untap (%d left)"); + inp.showAndWait(); + if( inp.hasCancelled() || inp.getSelected().size() != c ) + return null; + return PaymentDecision.card(inp.getSelected()); + } + + @Override + public PaymentDecision visit(CostUntap cost) { + return PaymentDecision.number(1); + } + + @Override + public PaymentDecision visit(CostUnattach cost) { + final Card source = ability.getSourceCard(); + + Card cardToUnattach = cost.findCardToUnattach(source, payer, ability); + if (cardToUnattach != null && payer.getController().confirmPayment(cost, "Unattach " + cardToUnattach.getName() + "?")) { + return PaymentDecision.card(cardToUnattach); + } + return null; + } +} diff --git a/forge-gui/src/main/java/forge/gui/player/HumanPlay.java b/forge-gui/src/main/java/forge/gui/player/HumanPlay.java index d7985d6cef9..fa8836768f5 100644 --- a/forge-gui/src/main/java/forge/gui/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/gui/player/HumanPlay.java @@ -321,6 +321,8 @@ public class HumanPlay { } } + HumanCostDecision hcd = new HumanCostDecision(p, sourceAbility, source); + //the following costs do not need inputs for (CostPart part : parts) { boolean mayRemovePart = true; @@ -364,7 +366,7 @@ public class HumanPlay { } } else if (part instanceof CostGainLife) { - PaymentDecision pd = part.payHuman(sourceAbility, p); + PaymentDecision pd = part.accept(hcd); if (pd == null) return false; @@ -375,7 +377,7 @@ public class HumanPlay { if (!p.getController().confirmPayment(part, "Do you want to add " + ((CostAddMana) part).toString() + " to your mana pool?" + orString)) { return false; } - PaymentDecision pd = part.payHuman(sourceAbility, p); + PaymentDecision pd = part.accept(hcd); if (pd == null) return false; @@ -452,7 +454,7 @@ public class HumanPlay { } } else if (part instanceof CostRemoveCounter) { - CounterType counterType = ((CostRemoveCounter) part).getCounter(); + CounterType counterType = ((CostRemoveCounter) part).counter; int amount = getAmountFromPartX(part, source, sourceAbility); if (!part.canPay(sourceAbility)) {