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.");
}