diff --git a/src/main/java/forge/FThreads.java b/src/main/java/forge/FThreads.java index 7130594025b..4bbc6d32824 100644 --- a/src/main/java/forge/FThreads.java +++ b/src/main/java/forge/FThreads.java @@ -1,11 +1,15 @@ package forge; import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.swing.SwingUtilities; +import forge.control.input.Input; +import forge.error.BugReporter; + /** * TODO: Write javadoc for this type. * @@ -81,8 +85,8 @@ public class FThreads { } - public static void invokeInNewThread(Runnable proc) { - invokeInNewThread(proc, false); + public static void invokeInNewThread(Runnable toRun) { + getCachedPool().execute(toRun); } public static void invokeInNewThread(final Runnable proc, boolean lockUI) { @@ -94,13 +98,20 @@ public class FThreads { @Override public void run() { proc.run(); - // may try special unlock method here Singletons.getModel().getMatch().getInput().unlock(); } }; } - - getCachedPool().execute(toRun); + invokeInNewThread(toRun); + } + + public static final void setInputAndWait(Input inp, CountDownLatch cdl) { + Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); + try { + cdl.await(); + } catch (InterruptedException e) { + BugReporter.reportException(e); + } } } diff --git a/src/main/java/forge/card/cost/CostDiscard.java b/src/main/java/forge/card/cost/CostDiscard.java index 9d99e7af8e8..de50be20d48 100644 --- a/src/main/java/forge/card/cost/CostDiscard.java +++ b/src/main/java/forge/card/cost/CostDiscard.java @@ -26,6 +26,7 @@ import com.google.common.base.Predicate; import forge.Card; import forge.CardLists; import forge.CardPredicates; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; @@ -339,7 +340,7 @@ public class CostDiscard extends CostPartWithList { CountDownLatch cdl = new CountDownLatch(1); final Input inp = new InputPayCostDiscard(cdl, ability, handList, this, payment, c, discardType); - setInputAndWait(inp, cdl); + FThreads.setInputAndWait(inp, cdl); } } if ( !payment.isCanceled()) diff --git a/src/main/java/forge/card/cost/CostExile.java b/src/main/java/forge/card/cost/CostExile.java index 232ab9177dc..8d453b2ae4b 100644 --- a/src/main/java/forge/card/cost/CostExile.java +++ b/src/main/java/forge/card/cost/CostExile.java @@ -25,6 +25,7 @@ import java.util.concurrent.CountDownLatch; import forge.Card; import forge.CardLists; import forge.CardPredicates; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; @@ -602,7 +603,7 @@ public class CostExile extends CostPartWithList { } else { target = new InputExileFrom(cdl,ability, this.getType(), c, payment, this); } - setInputAndWait(target, cdl); + FThreads.setInputAndWait(target, cdl); if(!payment.isCanceled()) addListToHash(ability, "Exiled"); } diff --git a/src/main/java/forge/card/cost/CostPart.java b/src/main/java/forge/card/cost/CostPart.java index 8a4f60659fc..6861cc7c554 100644 --- a/src/main/java/forge/card/cost/CostPart.java +++ b/src/main/java/forge/card/cost/CostPart.java @@ -17,13 +17,9 @@ */ package forge.card.cost; -import java.util.concurrent.CountDownLatch; import forge.Card; -import forge.Singletons; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; -import forge.error.BugReporter; import forge.game.GameState; import forge.game.player.AIPlayer; import forge.game.player.Player; @@ -146,15 +142,6 @@ public abstract class CostPart { return i; } - public final void setInputAndWait(Input inp, CountDownLatch cdl) { - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); - try { - cdl.await(); - } catch (InterruptedException e) { - BugReporter.reportException(e); - } - } - /** * Can pay. * diff --git a/src/main/java/forge/card/cost/CostPartMana.java b/src/main/java/forge/card/cost/CostPartMana.java index 121abdf068e..7bb1ccb2228 100644 --- a/src/main/java/forge/card/cost/CostPartMana.java +++ b/src/main/java/forge/card/cost/CostPartMana.java @@ -22,6 +22,7 @@ import java.util.concurrent.CountDownLatch; import com.google.common.base.Strings; import forge.Card; +import forge.FThreads; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; import forge.control.input.Input; @@ -112,8 +113,8 @@ public class CostPartMana extends CostPart { /** * @return the xCantBe0 */ - public boolean isxCantBe0() { - return xCantBe0; + public boolean canXbe0() { + return !xCantBe0; } /** @@ -227,7 +228,7 @@ public class CostPartMana extends CostPart { } else inp = null; if ( null != inp) { - setInputAndWait(inp, cdl); + FThreads.setInputAndWait(inp, cdl); } } diff --git a/src/main/java/forge/card/cost/CostPayment.java b/src/main/java/forge/card/cost/CostPayment.java index 71090206939..1499b480454 100644 --- a/src/main/java/forge/card/cost/CostPayment.java +++ b/src/main/java/forge/card/cost/CostPayment.java @@ -205,12 +205,7 @@ public class CostPayment { * * @return a boolean. */ - public final boolean payCost() { - // Nothing actually ever checks this return value, is it needed? - if (this.bCancel) { - return false; - } - + public void payCost() { for (final CostPart part : this.cost.getCostParts()) { // This portion of the cost is already paid for, keep moving if (this.paidCostParts.contains(part)) { @@ -218,13 +213,11 @@ public class CostPayment { } part.payHuman(this.ability, this.card, this, game); - if ( isCanceled() ) break; + if ( isCanceled() ) return; setPaidPart(part); } - - this.resetUndoList(); // ?? - return true; + this.resetUndoList(); } /** diff --git a/src/main/java/forge/card/cost/CostPutCounter.java b/src/main/java/forge/card/cost/CostPutCounter.java index 7e782640a69..862b87406ce 100644 --- a/src/main/java/forge/card/cost/CostPutCounter.java +++ b/src/main/java/forge/card/cost/CostPutCounter.java @@ -23,6 +23,7 @@ import java.util.concurrent.CountDownLatch; import forge.Card; import forge.CardLists; import forge.CounterType; +import forge.FThreads; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; import forge.game.GameState; @@ -255,7 +256,7 @@ public class CostPutCounter extends CostPartWithList { this.addToList(source); } else { CountDownLatch cdl = new CountDownLatch(1); - setInputAndWait(new InputPayCostPutCounter(cdl, this.getType(), this, c, payment, ability), cdl); + FThreads.setInputAndWait(new InputPayCostPutCounter(cdl, this.getType(), this, c, payment, ability), cdl); } if ( !payment.isCanceled()) addListToHash(ability, "CounterPut"); diff --git a/src/main/java/forge/card/cost/CostRemoveCounter.java b/src/main/java/forge/card/cost/CostRemoveCounter.java index e016050a109..a5cd18243c1 100644 --- a/src/main/java/forge/card/cost/CostRemoveCounter.java +++ b/src/main/java/forge/card/cost/CostRemoveCounter.java @@ -24,6 +24,7 @@ import java.util.concurrent.CountDownLatch; import forge.Card; import forge.CardLists; import forge.CounterType; +import forge.FThreads; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; import forge.control.input.Input; @@ -386,7 +387,7 @@ public class CostRemoveCounter extends CostPartWithList { } else { inp = new InputPayCostRemoveCounterFrom(cdl, this, this.getType(), ability, c, payment); } - setInputAndWait(inp, cdl); + FThreads.setInputAndWait(inp, cdl); if ( !payment.isCanceled() ) addListToHash(ability, "CounterRemove"); diff --git a/src/main/java/forge/card/cost/CostReturn.java b/src/main/java/forge/card/cost/CostReturn.java index c8c0f554379..3e3c39988d0 100644 --- a/src/main/java/forge/card/cost/CostReturn.java +++ b/src/main/java/forge/card/cost/CostReturn.java @@ -23,6 +23,7 @@ import java.util.concurrent.CountDownLatch; import forge.Card; import forge.CardLists; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; @@ -254,7 +255,7 @@ public class CostReturn extends CostPartWithList { CountDownLatch cdl = new CountDownLatch(1); final Input target = new InputPayReturnType(cdl, ability, this, c, this.getType(), payment); final Input inp = target; - setInputAndWait(inp, cdl); + FThreads.setInputAndWait(inp, cdl); } if (!payment.isCanceled()) diff --git a/src/main/java/forge/card/cost/CostReveal.java b/src/main/java/forge/card/cost/CostReveal.java index d4021fef02c..b21bfb3aa17 100644 --- a/src/main/java/forge/card/cost/CostReveal.java +++ b/src/main/java/forge/card/cost/CostReveal.java @@ -23,6 +23,7 @@ import java.util.concurrent.CountDownLatch; import forge.Card; import forge.CardLists; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; @@ -279,7 +280,7 @@ public class CostReveal extends CostPartWithList { if (num > 0) { final CountDownLatch cdl = new CountDownLatch(1); final Input inp = new InputPayReveal(cdl, this, this.getType(), handList, ability, payment, num);; - setInputAndWait(inp, cdl); + FThreads.setInputAndWait(inp, cdl); } } if ( !payment.isCanceled()) diff --git a/src/main/java/forge/card/cost/CostSacrifice.java b/src/main/java/forge/card/cost/CostSacrifice.java index 462e9dc267e..943fc9e0e4f 100644 --- a/src/main/java/forge/card/cost/CostSacrifice.java +++ b/src/main/java/forge/card/cost/CostSacrifice.java @@ -23,6 +23,7 @@ import java.util.concurrent.CountDownLatch; import forge.Card; import forge.CardLists; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; @@ -252,7 +253,7 @@ public class CostSacrifice extends CostPartWithList { return; } final CountDownLatch cdl = new CountDownLatch(1); - setInputAndWait(new InputPayCostSacrificeFromList(cdl, this, ability, c, payment, list), cdl); + FThreads.setInputAndWait(new InputPayCostSacrificeFromList(cdl, this, ability, c, payment, list), cdl); } if( !payment.isCanceled()) addListToHash(ability, "Sacrificed"); diff --git a/src/main/java/forge/card/cost/CostTapType.java b/src/main/java/forge/card/cost/CostTapType.java index b39df05fd9f..ba8247d5e31 100644 --- a/src/main/java/forge/card/cost/CostTapType.java +++ b/src/main/java/forge/card/cost/CostTapType.java @@ -24,6 +24,7 @@ import java.util.concurrent.CountDownLatch; import forge.Card; import forge.CardLists; import forge.CardPredicates.Presets; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; @@ -248,7 +249,7 @@ public class CostTapType extends CostPartWithList { } } CountDownLatch cdl = new CountDownLatch(1); - setInputAndWait(new InputPayCostTapType(cdl, this, c, typeList, payment), cdl); + FThreads.setInputAndWait(new InputPayCostTapType(cdl, this, c, typeList, payment), cdl); if( !payment.isCanceled()) addListToHash(ability, "Tapped"); } diff --git a/src/main/java/forge/card/cost/CostUntapType.java b/src/main/java/forge/card/cost/CostUntapType.java index fb26df53138..20d4bec0cf0 100644 --- a/src/main/java/forge/card/cost/CostUntapType.java +++ b/src/main/java/forge/card/cost/CostUntapType.java @@ -23,6 +23,7 @@ import java.util.concurrent.CountDownLatch; import forge.Card; import forge.CardLists; import forge.CardPredicates.Presets; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; @@ -249,7 +250,7 @@ public class CostUntapType extends CostPartWithList { } } CountDownLatch cdl = new CountDownLatch(1); - setInputAndWait(new InputPayCostUntapY(cdl, c, typeList, this, payment), cdl); + FThreads.setInputAndWait(new InputPayCostUntapY(cdl, c, typeList, this, payment), cdl); if ( !payment.isCanceled() ) addListToHash(ability, "Untapped"); diff --git a/src/main/java/forge/card/spellability/SpellAbility.java b/src/main/java/forge/card/spellability/SpellAbility.java index c21d1ef742c..9988f964f09 100644 --- a/src/main/java/forge/card/spellability/SpellAbility.java +++ b/src/main/java/forge/card/spellability/SpellAbility.java @@ -23,8 +23,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.commons.lang.StringUtils; - import forge.Card; import forge.GameEntity; import forge.Singletons; @@ -1682,25 +1680,4 @@ public abstract class SpellAbility implements ISpellAbility { public void setCopied(boolean isCopied0) { this.isCopied = isCopied0; } - - public boolean announceRequirements() { - // Announcing Requirements like Choosing X or Multikicker - // SA Params as comma delimited list - String announce = this.getParam("Announce"); - if (announce != null) { - String[] announceVars = announce.split(","); - for(String aVar : announceVars) { - String value = this.getActivatingPlayer().getController().announceRequirements(this, aVar); - if (value == null || !StringUtils.isNumeric(value)) { - return false; - } else if (this.getPayCosts().getCostMana() != null && this.getPayCosts().getCostMana().isxCantBe0() - && Integer.parseInt(value) == 0) { - return false; - } - this.setSVar(aVar, "Number$" + value); - this.getSourceCard().setSVar(aVar, "Number$" + value); - } - } - return true; - } } diff --git a/src/main/java/forge/card/spellability/SpellAbilityRequirements.java b/src/main/java/forge/card/spellability/SpellAbilityRequirements.java index 7d3ee7c6b4e..15f4c28b9ea 100644 --- a/src/main/java/forge/card/spellability/SpellAbilityRequirements.java +++ b/src/main/java/forge/card/spellability/SpellAbilityRequirements.java @@ -19,6 +19,8 @@ package forge.card.spellability; import java.util.ArrayList; +import org.apache.commons.lang3.StringUtils; + import forge.Card; import forge.CardCharacteristicName; import forge.Singletons; @@ -44,67 +46,26 @@ public class SpellAbilityRequirements { private Zone fromZone = null; private Integer zonePosition = null; - /** - *

- * Setter for the field skipStack. - *

- * - * @param bSkip - * a boolean. - */ + public final void setSkipStack(final boolean bSkip) { this.skipStack = bSkip; } - - /** - *

- * setFree. - *

- * - * @param bFree - * a boolean. - */ + public final void setFree(final boolean bFree) { this.isFree = bFree; } - - /** - *

- * Constructor for SpellAbility_Requirements. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param ts - * a {@link forge.card.spellability.TargetSelection} object. - * @param cp - * a {@link forge.card.cost.CostPayment} object. - */ public SpellAbilityRequirements(final SpellAbility sa, final TargetSelection ts, final CostPayment cp) { this.ability = sa; this.select = ts; this.payment = cp; } - /** - *

- * fillRequirements. - *

- */ public final void fillRequirements() { this.fillRequirements(false); } - /** - *

- * fillRequirements. - *

- * - * @param skipTargeting - * a boolean. - */ public final void fillRequirements(final boolean skipTargeting) { if ((this.ability instanceof Spell) && !this.bCasting) { // remove from hand @@ -118,14 +79,13 @@ public class SpellAbilityRequirements { } } - // freeze Stack. No abilities should go onto the stack while I'm filling - // requirements. + // freeze Stack. No abilities should go onto the stack while I'm filling requirements. Singletons.getModel().getGame().getStack().freezeStack(); // Announce things like how many times you want to Multikick or the value of X - if (!this.ability.announceRequirements()) { + if (!this.announceRequirements()) { this.select.setCancel(true); - this.finishedTargeting(); + rollbackAbility(); return; } @@ -134,110 +94,104 @@ public class SpellAbilityRequirements { // (or trigger case where its already targeted) if (!skipTargeting && (this.select.doesTarget() || (this.ability.getSubAbility() != null))) { this.select.setRequirements(this); - this.select.resetTargets(); + this.select.clearTargets(); this.select.chooseTargets(); - } else { - this.needPayment(); - } - } - - /** - *

- * finishedTargeting. - *

- */ - public final void finishedTargeting() { - if (this.select.isCanceled()) { - // cancel ability during target choosing - final Card c = this.ability.getSourceCard(); - - // split cards transform back to full form if targeting is canceled - if (c.isSplitCard()) { - c.setState(CardCharacteristicName.Original); + if (this.select.isCanceled()) { + rollbackAbility(); + return; } - - if (this.bCasting && !c.isCopiedSpell()) { // and not a copy - // add back to where it came from - Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition); - } - - this.select.resetTargets(); - Singletons.getModel().getGame().getStack().removeFromFrozenStack(this.ability); - return; - } else { - this.needPayment(); } - } - - /** - *

- * needPayment. - *

- */ - public final void needPayment() { + + // Payment if (!this.isFree) { this.payment.setRequirements(this); this.payment.changeCost(); this.payment.payCost(); } - + if (this.payment.isCanceled()) { - final Card c = this.ability.getSourceCard(); - - // split cards transform back to full form if mana cost is not paid - if (c.isSplitCard()) { - c.setState(CardCharacteristicName.Original); - } - - if (this.bCasting && !c.isCopiedSpell()) { // and not a copy - // add back to Previous Zone - Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition); - } - - if (this.select != null) { - this.select.resetTargets(); - } - - this.ability.resetOnceResolved(); - this.payment.cancelPayment(); - Singletons.getModel().getGame().getStack().clearFrozen(); + rollbackAbility(); + return; } + else if (this.isFree || this.payment.isAllPaid()) { if (this.skipStack) { AbilityUtils.resolve(this.ability, false); } else { - this.addAbilityToStack(); - } - this.select.resetTargets(); + this.enusureAbilityHasDescription(this.ability); + this.ability.getActivatingPlayer().getManaPool().clearManaPaid(this.ability, false); + Singletons.getModel().getGame().getStack().addAndUnfreeze(this.ability); + } + + // Warning about this - resolution may come in another thread, and it would still need its targets + this.select.clearTargets(); Singletons.getModel().getGame().getAction().checkStateEffects(); } } - /** - *

- * addAbilityToStack. - *

- */ - public final void addAbilityToStack() { - // For older abilities that don't setStackDescription set it here - if (this.ability.getStackDescription().equals("")) { - final StringBuilder sb = new StringBuilder(); - sb.append(this.ability.getSourceCard().getName()); - if (this.ability.getTarget() != null) { - final ArrayList targets = this.ability.getTarget().getTargets(); - if (targets.size() > 0) { - sb.append(" - Targeting "); - for (final Object o : targets) { - sb.append(o.toString()).append(" "); - } - } - } + private void rollbackAbility() { + // cancel ability during target choosing + final Card c = this.ability.getSourceCard(); - this.ability.setStackDescription(sb.toString()); + // split cards transform back to full form if targeting is canceled + if (c.isSplitCard()) { + c.setState(CardCharacteristicName.Original); } - this.ability.getActivatingPlayer().getManaPool().clearManaPaid(this.ability, false); - Singletons.getModel().getGame().getStack().addAndUnfreeze(this.ability); + if (this.bCasting && !c.isCopiedSpell()) { // and not a copy + // add back to where it came from + Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition); + } + + if (this.select != null) { + this.select.clearTargets(); + } + + this.ability.resetOnceResolved(); + this.payment.cancelPayment(); + Singletons.getModel().getGame().getStack().clearFrozen(); + // Singletons.getModel().getGame().getStack().removeFromFrozenStack(this.ability); + } + + + public boolean announceRequirements() { + // Announcing Requirements like Choosing X or Multikicker + // SA Params as comma delimited list + String announce = ability.getParam("Announce"); + if (announce != null) { + for(String aVar : announce.split(",")) { + String value = ability.getActivatingPlayer().getController().announceRequirements(ability, aVar); + if (value == null || !StringUtils.isNumeric(value)) { + return false; + } else if (ability.getPayCosts().getCostMana() != null && !ability.getPayCosts().getCostMana().canXbe0() + && Integer.parseInt(value) == 0) { + return false; + } + ability.setSVar(aVar, "Number$" + value); + ability.getSourceCard().setSVar(aVar, "Number$" + value); + } + } + return true; + } + + private void enusureAbilityHasDescription(SpellAbility ability) { + if (!StringUtils.isBlank(ability.getStackDescription())) + return; + + // For older abilities that don't setStackDescription set it here + final StringBuilder sb = new StringBuilder(); + sb.append(ability.getSourceCard().getName()); + if (ability.getTarget() != null) { + final ArrayList targets = ability.getTarget().getTargets(); + if (targets.size() > 0) { + sb.append(" - Targeting "); + for (final Object o : targets) { + sb.append(o.toString()).append(" "); + } + } + } + + ability.setStackDescription(sb.toString()); } } diff --git a/src/main/java/forge/card/spellability/TargetSelection.java b/src/main/java/forge/card/spellability/TargetSelection.java index 03286649df0..adee9c1ae81 100644 --- a/src/main/java/forge/card/spellability/TargetSelection.java +++ b/src/main/java/forge/card/spellability/TargetSelection.java @@ -20,11 +20,13 @@ package forge.card.spellability; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.concurrent.CountDownLatch; import com.google.common.base.Predicate; import forge.Card; import forge.CardLists; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; @@ -44,6 +46,177 @@ import forge.view.ButtonUtil; * @version $Id$ */ public class TargetSelection { + /** + * TODO: Write javadoc for this type. + * + */ + public final class InputSelectTargets extends Input { + private final TargetSelection select; + private final List choices; + private final ArrayList alreadyTargeted; + private final boolean targeted; + private final Target tgt; + private final SpellAbility sa; + private final boolean mandatory; + private final CountDownLatch cdlDone; + private static final long serialVersionUID = -1091595663541356356L; + + /** + * TODO: Write javadoc for Constructor. + * @param select + * @param choices + * @param req + * @param alreadyTargeted + * @param targeted + * @param tgt + * @param sa + * @param mandatory + */ + public InputSelectTargets(CountDownLatch cdl, TargetSelection select, List choices, ArrayList alreadyTargeted, boolean targeted, Target tgt, SpellAbility sa, boolean mandatory) { + cdlDone = cdl; + this.select = select; + this.choices = choices; + this.alreadyTargeted = alreadyTargeted; + this.targeted = targeted; + this.tgt = tgt; + this.sa = sa; + this.mandatory = mandatory; + } + + @Override + public void showMessage() { + final StringBuilder sb = new StringBuilder(); + sb.append("Targeted: "); + for (final Object o : alreadyTargeted) { + sb.append(o).append(" "); + } + sb.append(tgt.getTargetedString()); + sb.append("\n"); + sb.append(tgt.getVTSelection()); + + CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString()); + + // If reached Minimum targets, enable OK button + if (!tgt.isMinTargetsChosen(sa.getSourceCard(), sa) || tgt.isDividedAsYouChoose()) { + if (mandatory && tgt.hasCandidates(sa, true)) { + // Player has to click on a target + ButtonUtil.disableAll(); + } else { + ButtonUtil.enableOnlyCancel(); + } + } else { + if (mandatory && tgt.hasCandidates(sa, true)) { + // Player has to click on a target or ok + ButtonUtil.enableOnlyOk(); + } else { + ButtonUtil.enableAllFocusOk(); + } + } + } + + @Override + public void selectButtonCancel() { + select.setCancel(true); + this.done(); + } + + @Override + public void selectButtonOK() { + this.done(); + } + + @Override + public void selectCard(final Card card) { + // leave this in temporarily, there some seriously wrong things + // going on here + if (targeted && !card.canBeTargetedBy(sa)) { + CMatchUI.SINGLETON_INSTANCE.showMessage("Cannot target this card (Shroud? Protection? Restrictions?)."); + } else if (choices.contains(card)) { + if (tgt.isDividedAsYouChoose()) { + final int stillToDivide = tgt.getStillToDivide(); + int allocatedPortion = 0; + // allow allocation only if the max targets isn't reached and there are more candidates + if ((tgt.getNumTargeted() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) + && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { + final Integer[] choices = new Integer[stillToDivide]; + for (int i = 1; i <= stillToDivide; i++) { + choices[i - 1] = i; + } + String apiBasedMessage = "Distribute how much to "; + if (sa.getApi() == ApiType.DealDamage) { + apiBasedMessage = "Select how much damage to deal to "; + } else if (sa.getApi() == ApiType.PreventDamage) { + apiBasedMessage = "Select how much damage to prevent to "; + } else if (sa.getApi() == ApiType.PutCounter) { + apiBasedMessage = "Select how many counters to distribute to "; + } + final StringBuilder sb = new StringBuilder(); + sb.append(apiBasedMessage); + sb.append(card.toString()); + Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); + if (null == chosen) { + return; + } + allocatedPortion = chosen; + } else { // otherwise assign the rest of the damage/protection + allocatedPortion = stillToDivide; + } + tgt.setStillToDivide(stillToDivide - allocatedPortion); + tgt.addDividedAllocation(card, allocatedPortion); + } + tgt.addTarget(card); + this.done(); + } + } // selectCard() + + @Override + public void selectPlayer(final Player player) { + if (alreadyTargeted.contains(player)) { + return; + } + + if (sa.canTarget(player)) { + if (tgt.isDividedAsYouChoose()) { + final int stillToDivide = tgt.getStillToDivide(); + int allocatedPortion = 0; + // allow allocation only if the max targets isn't reached and there are more candidates + if ((alreadyTargeted.size() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) + && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { + final Integer[] choices = new Integer[stillToDivide]; + for (int i = 1; i <= stillToDivide; i++) { + choices[i - 1] = i; + } + String apiBasedMessage = "Distribute how much to "; + if (sa.getApi() == ApiType.DealDamage) { + apiBasedMessage = "Select how much damage to deal to "; + } else if (sa.getApi() == ApiType.PreventDamage) { + apiBasedMessage = "Select how much damage to prevent to "; + } + final StringBuilder sb = new StringBuilder(); + sb.append(apiBasedMessage); + sb.append(player.getName()); + Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); + if (null == chosen) { + return; + } + allocatedPortion = chosen; + } else { // otherwise assign the rest of the damage/protection + allocatedPortion = stillToDivide; + } + tgt.setStillToDivide(stillToDivide - allocatedPortion); + tgt.addDividedAllocation(player, allocatedPortion); + } + tgt.addTarget(player); + this.done(); + } + } + + void done() { + this.stop(); + cdlDone.countDown(); + } + } + private Target target = null; private SpellAbility ability = null; private Card card = null; @@ -98,6 +271,7 @@ public class TargetSelection { } private boolean bCancel = false; + private boolean bTargetingDone = false; /** *

@@ -130,20 +304,6 @@ public class TargetSelection { return this.subSelection.isCanceled(); } - private boolean bDoneTarget = false; - - /** - *

- * setDoneTarget. - *

- * - * @param done - * a boolean. - */ - public final void setDoneTarget(final boolean done) { - this.bDoneTarget = done; - } - /** *

* Constructor for Target_Selection. @@ -179,7 +339,7 @@ public class TargetSelection { * resetTargets. *

*/ - public final void resetTargets() { + public final void clearTargets() { if (this.target != null) { this.target.resetTargets(); this.target.calculateStillToDivide(this.ability.getParam("DividedAsYouChoose"), this.getCard(), this.ability); @@ -195,24 +355,25 @@ public class TargetSelection { */ public final boolean chooseTargets() { // if not enough targets chosen, reset and cancel Ability - if (this.bCancel || (this.bDoneTarget && !this.target.isMinTargetsChosen(this.card, this.ability))) { + if (this.bCancel || (this.bTargetingDone && !this.target.isMinTargetsChosen(this.card, this.ability))) { this.bCancel = true; - this.req.finishedTargeting(); return false; - } else if (!this.doesTarget() || (this.bDoneTarget && this.target.isMinTargetsChosen(this.card, this.ability)) + } + + if (!this.doesTarget() + || this.bTargetingDone && this.target.isMinTargetsChosen(this.card, this.ability) || this.target.isMaxTargetsChosen(this.card, this.ability) - || (this.target.isDividedAsYouChoose() && this.target.getStillToDivide() == 0)) { + || this.target.isDividedAsYouChoose() && this.target.getStillToDivide() == 0) { final AbilitySub abSub = this.ability.getSubAbility(); if (abSub == null) { // if no more SubAbilities finish targeting - this.req.finishedTargeting(); return true; } else { // Has Sub Ability this.subSelection = new TargetSelection(abSub.getTarget(), abSub); this.subSelection.setRequirements(this.req); - this.subSelection.resetTargets(); + this.subSelection.clearTargets(); return this.subSelection.chooseTargets(); } } @@ -220,11 +381,12 @@ public class TargetSelection { if (!this.target.hasCandidates(this.ability, true) && !this.target.isMinTargetsChosen(this.card, this.ability)) { // Cancel ability if there aren't any valid Candidates this.bCancel = true; - this.req.finishedTargeting(); return false; } this.chooseValidInput(); + if ( !bCancel ) + return chooseTargets(); return false; } @@ -362,178 +524,15 @@ public class TargetSelection { } if (zone.contains(ZoneType.Battlefield) && zone.size() == 1) { - Singletons.getModel().getMatch().getInput().setInput(this.inputTargetSpecific(choices, true, mandatory, objects)); + CountDownLatch cdl = new CountDownLatch(1); + Input inp = new InputSelectTargets(cdl, this, choices, objects, true, this.target, this.ability, mandatory); + FThreads.setInputAndWait(inp, cdl); + bTargetingDone = !bCancel; } else { this.chooseCardFromList(choices, true, mandatory); } } // input_targetValid - // List choices are the only cards the user can successful select - /** - *

- * input_targetSpecific. - *

- * - * @param choices - * a {@link forge.CardList} object. - * @param targeted - * a boolean. - * @param mandatory - * a boolean. - * @param alreadyTargeted - * the already targeted - * @return a {@link forge.control.input.Input} object. - */ - public final Input inputTargetSpecific(final List choices, final boolean targeted, final boolean mandatory, - final ArrayList alreadyTargeted) { - final SpellAbility sa = this.ability; - final TargetSelection select = this; - final Target tgt = this.target; - final SpellAbilityRequirements req = this.req; - - final Input target = new Input() { - private static final long serialVersionUID = -1091595663541356356L; - - @Override - public void showMessage() { - final StringBuilder sb = new StringBuilder(); - sb.append("Targeted: "); - for (final Object o : alreadyTargeted) { - sb.append(o).append(" "); - } - sb.append(tgt.getTargetedString()); - sb.append("\n"); - sb.append(tgt.getVTSelection()); - - CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString()); - - // If reached Minimum targets, enable OK button - if (!tgt.isMinTargetsChosen(sa.getSourceCard(), sa) || tgt.isDividedAsYouChoose()) { - if (mandatory && tgt.hasCandidates(sa, true)) { - // Player has to click on a target - ButtonUtil.disableAll(); - } else { - ButtonUtil.enableOnlyCancel(); - } - } else { - if (mandatory && tgt.hasCandidates(sa, true)) { - // Player has to click on a target or ok - ButtonUtil.enableOnlyOk(); - } else { - ButtonUtil.enableAllFocusOk(); - } - } - } - - @Override - public void selectButtonCancel() { - select.setCancel(true); - this.stop(); - req.finishedTargeting(); - } - - @Override - public void selectButtonOK() { - select.setDoneTarget(true); - this.done(); - } - - @Override - public void selectCard(final Card card) { - // leave this in temporarily, there some seriously wrong things - // going on here - if (targeted && !card.canBeTargetedBy(sa)) { - CMatchUI.SINGLETON_INSTANCE.showMessage("Cannot target this card (Shroud? Protection? Restrictions?)."); - } else if (choices.contains(card)) { - if (tgt.isDividedAsYouChoose()) { - final int stillToDivide = tgt.getStillToDivide(); - int allocatedPortion = 0; - // allow allocation only if the max targets isn't reached and there are more candidates - if ((tgt.getNumTargeted() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) - && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { - final Integer[] choices = new Integer[stillToDivide]; - for (int i = 1; i <= stillToDivide; i++) { - choices[i - 1] = i; - } - String apiBasedMessage = "Distribute how much to "; - if (sa.getApi() == ApiType.DealDamage) { - apiBasedMessage = "Select how much damage to deal to "; - } else if (sa.getApi() == ApiType.PreventDamage) { - apiBasedMessage = "Select how much damage to prevent to "; - } else if (sa.getApi() == ApiType.PutCounter) { - apiBasedMessage = "Select how many counters to distribute to "; - } - final StringBuilder sb = new StringBuilder(); - sb.append(apiBasedMessage); - sb.append(card.toString()); - Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); - if (null == chosen) { - return; - } - allocatedPortion = chosen; - } else { // otherwise assign the rest of the damage/protection - allocatedPortion = stillToDivide; - } - tgt.setStillToDivide(stillToDivide - allocatedPortion); - tgt.addDividedAllocation(card, allocatedPortion); - } - tgt.addTarget(card); - this.done(); - } - } // selectCard() - - @Override - public void selectPlayer(final Player player) { - if (alreadyTargeted.contains(player)) { - return; - } - - if (sa.canTarget(player)) { - if (tgt.isDividedAsYouChoose()) { - final int stillToDivide = tgt.getStillToDivide(); - int allocatedPortion = 0; - // allow allocation only if the max targets isn't reached and there are more candidates - if ((alreadyTargeted.size() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) - && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { - final Integer[] choices = new Integer[stillToDivide]; - for (int i = 1; i <= stillToDivide; i++) { - choices[i - 1] = i; - } - String apiBasedMessage = "Distribute how much to "; - if (sa.getApi() == ApiType.DealDamage) { - apiBasedMessage = "Select how much damage to deal to "; - } else if (sa.getApi() == ApiType.PreventDamage) { - apiBasedMessage = "Select how much damage to prevent to "; - } - final StringBuilder sb = new StringBuilder(); - sb.append(apiBasedMessage); - sb.append(player.getName()); - Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); - if (null == chosen) { - return; - } - allocatedPortion = chosen; - } else { // otherwise assign the rest of the damage/protection - allocatedPortion = stillToDivide; - } - tgt.setStillToDivide(stillToDivide - allocatedPortion); - tgt.addDividedAllocation(player, allocatedPortion); - } - tgt.addTarget(player); - this.done(); - } - } - - void done() { - this.stop(); - - select.chooseTargets(); - } - }; - - return target; - } // input_targetSpecific() - /** *

* chooseCardFromList. @@ -624,7 +623,7 @@ public class TargetSelection { if (!c.equals(divBattlefield) && !c.equals(divExile) && !c.equals(divGrave) && !c.equals(divLibrary) && !c.equals(divStack)) { if (c.equals(dummy)) { - this.setDoneTarget(true); + bTargetingDone = true; } else { tgt.addTarget(c); } @@ -632,8 +631,6 @@ public class TargetSelection { } else { this.setCancel(true); } - - this.chooseTargets(); } /** @@ -675,7 +672,7 @@ public class TargetSelection { if (madeChoice != null) { if (madeChoice.equals(doneDummy)) { - this.setDoneTarget(true); + bTargetingDone = true; } else { tgt.addTarget(map.get(madeChoice)); } @@ -683,8 +680,6 @@ public class TargetSelection { select.setCancel(true); } } - - select.chooseTargets(); } // TODO The following three functions are Utility functions for diff --git a/src/main/java/forge/control/input/InputPayManaBase.java b/src/main/java/forge/control/input/InputPayManaBase.java index 81188b89340..6d666a232b2 100644 --- a/src/main/java/forge/control/input/InputPayManaBase.java +++ b/src/main/java/forge/control/input/InputPayManaBase.java @@ -327,5 +327,16 @@ public abstract class InputPayManaBase extends Input { public String toString() { return "PayManaBase (" + manaCost.toString() + ")"; } + + protected void handleConvokedCards(boolean isCancelled) { + if (saPaidFor.getTappedForConvoke() != null) { + for (final Card c : saPaidFor.getTappedForConvoke()) { + c.setTapped(false); + if (!isCancelled) + c.tap(); + } + saPaidFor.clearTappedForConvoke(); + } + } } diff --git a/src/main/java/forge/control/input/InputPayManaOfCostPayment.java b/src/main/java/forge/control/input/InputPayManaOfCostPayment.java index 208eec9cfa9..5f00dd90163 100644 --- a/src/main/java/forge/control/input/InputPayManaOfCostPayment.java +++ b/src/main/java/forge/control/input/InputPayManaOfCostPayment.java @@ -82,19 +82,9 @@ public class InputPayManaOfCostPayment extends InputPayManaBase { // any mana tapabilities can't be used in payment as well as being tapped for convoke) handleConvokedCards(false); + cdlFinished.countDown(); } - protected void handleConvokedCards(boolean isCancelled) { - if (saPaidFor.getTappedForConvoke() != null) { - for (final Card c : saPaidFor.getTappedForConvoke()) { - c.setTapped(false); - if (!isCancelled) - c.tap(); - } - saPaidFor.clearTappedForConvoke(); - } - } - @Override public void selectButtonCancel() { handleConvokedCards(true); @@ -102,6 +92,7 @@ public class InputPayManaOfCostPayment extends InputPayManaBase { this.stop(); this.resetManaCost(); payment.cancelCost(); + cdlFinished.countDown(); } @Override diff --git a/src/main/java/forge/control/input/InputPayManaSimple.java b/src/main/java/forge/control/input/InputPayManaSimple.java index 8c571d78294..599a93b355e 100644 --- a/src/main/java/forge/control/input/InputPayManaSimple.java +++ b/src/main/java/forge/control/input/InputPayManaSimple.java @@ -103,21 +103,7 @@ public class InputPayManaSimple extends InputPayManaBase { this.saPaidFor.setSourceCard(game.getAction().moveToStack(this.originalCard)); } - // If this is a spell with convoke, re-tap all creatures used for - // it. - // This is done to make sure Taps triggers go off at the right time - // (i.e. AFTER cost payment, they are tapped previously as well so - // that - // any mana tapabilities can't be used in payment as well as being - // tapped for convoke) - - if (this.saPaidFor.getTappedForConvoke() != null) { - for (final Card c : this.saPaidFor.getTappedForConvoke()) { - c.setTapped(false); - c.tap(); - } - this.saPaidFor.clearTappedForConvoke(); - } + handleConvokedCards(false); } Singletons.getModel().getMatch().getInput().resetInput(); @@ -127,14 +113,7 @@ public class InputPayManaSimple extends InputPayManaBase { /** {@inheritDoc} */ @Override public final void selectButtonCancel() { - // If this is a spell with convoke, untap all creatures used for it. - if (this.saPaidFor.getTappedForConvoke() != null) { - for (final Card c : this.saPaidFor.getTappedForConvoke()) { - c.setTapped(false); - } - this.saPaidFor.clearTappedForConvoke(); - } - + handleConvokedCards(true); this.resetManaCost(); whoPays.getManaPool().refundManaPaid(this.saPaidFor, true); diff --git a/src/main/java/forge/control/input/InputPayManaX.java b/src/main/java/forge/control/input/InputPayManaX.java index 5b21bb31cb8..7056ca3b6f0 100644 --- a/src/main/java/forge/control/input/InputPayManaX.java +++ b/src/main/java/forge/control/input/InputPayManaX.java @@ -38,8 +38,7 @@ public class InputPayManaX extends InputPayManaBase { @Override public void showMessage() { - if ((xPaid == 0 && costMana.isxCantBe0()) || (this.colorX.equals("") - && !this.manaCost.toString().equals(strX))) { + if (xPaid == 0 && !costMana.canXbe0() || this.colorX.equals("") && !this.manaCost.toString().equals(strX)) { ButtonUtil.enableOnlyCancel(); // only cancel if partially paid an X value // or X is 0, and x can't be 0 @@ -50,7 +49,7 @@ public class InputPayManaX extends InputPayManaBase { StringBuilder msg = new StringBuilder("Pay X Mana Cost for "); msg.append(saPaidFor.getSourceCard().getName()).append("\n").append(this.xPaid); msg.append(" Paid so far."); - if (costMana.isxCantBe0()) { + if (!costMana.canXbe0()) { msg.append(" X Can't be 0."); }