diff --git a/forge-gui/src/main/java/forge/FThreads.java b/forge-gui/src/main/java/forge/FThreads.java index bb980772560..25e82f3c24a 100644 --- a/forge-gui/src/main/java/forge/FThreads.java +++ b/forge-gui/src/main/java/forge/FThreads.java @@ -12,8 +12,8 @@ import forge.util.ThreadUtil; * */ public class FThreads { - private FThreads() { } // no instances supposed + /** Checks if calling method uses event dispatch thread. * Exception thrown if method is on "wrong" thread. * A boolean is passed to indicate if the method must be EDT or not. @@ -22,11 +22,11 @@ public class FThreads { * @param mustBeEDT boolean: true = exception if not EDT, false = exception if EDT */ public static void assertExecutedByEdt(final boolean mustBeEDT) { - if (isGuiThread() != mustBeEDT ) { + if (isGuiThread() != mustBeEDT) { StackTraceElement[] trace = Thread.currentThread().getStackTrace(); - final String methodName = trace[2].getClassName() + "." + trace[2].getMethodName(); + final String methodName = trace[2].getClassName() + "." + trace[2].getMethodName(); String modalOperator = mustBeEDT ? " must be" : " may not be"; - throw new IllegalStateException( methodName + modalOperator + " accessed from the event dispatch thread."); + throw new IllegalStateException(methodName + modalOperator + " accessed from the event dispatch thread."); } } @@ -42,10 +42,12 @@ public class FThreads { * TODO: Write javadoc for this method. */ public static void invokeInEdtNowOrLater(Runnable proc) { - if( isGuiThread() ) + if (isGuiThread()) { proc.run(); - else + } + else { invokeInEdtLater(proc); + } } /** @@ -64,20 +66,20 @@ public class FThreads { if (SwingUtilities.isEventDispatchThread()) { // Just run in the current thread. proc.run(); - } else { + } + else { try { SwingUtilities.invokeAndWait(proc); - } catch (final InterruptedException exn) { + } + catch (final InterruptedException exn) { throw new RuntimeException(exn); - } catch (final InvocationTargetException exn) { + } + catch (final InvocationTargetException exn) { throw new RuntimeException(exn); } } } - - - /** * TODO: Write javadoc for this method. * @return @@ -86,16 +88,16 @@ public class FThreads { return SwingUtilities.isEventDispatchThread(); } - public static void delayInEDT(int milliseconds, final Runnable inputUpdater) { Runnable runInEdt = new Runnable() { - @Override public void run() { + @Override + public void run() { FThreads.invokeInEdtNowOrLater(inputUpdater); } }; ThreadUtil.delay(milliseconds, runInEdt); } - + public static String debugGetCurrThreadId() { return isGuiThread() ? "EDT" : Thread.currentThread().getName(); } @@ -103,28 +105,30 @@ public class FThreads { public static String prependThreadId(String message) { return debugGetCurrThreadId() + " > " + message; } - + public static void dumpStackTrace(PrintStream stream) { - StackTraceElement[] trace = Thread.currentThread().getStackTrace(); - stream.printf("%s > %s called from %s%n", debugGetCurrThreadId(), trace[2].getClassName()+"."+trace[2].getMethodName(), trace[3].toString()); - int i = 0; - for(StackTraceElement se : trace) { - if(i<2) i++; - else stream.println(se.toString()); - } + StackTraceElement[] trace = Thread.currentThread().getStackTrace(); + stream.printf("%s > %s called from %s%n", debugGetCurrThreadId(), + trace[2].getClassName() + "." + trace[2].getMethodName(), trace[3].toString()); + int i = 0; + for (StackTraceElement se : trace) { + if (i<2) { i++; } + else { stream.println(se.toString()); } + } } public static String debugGetStackTraceItem(int depth, boolean shorter) { StackTraceElement[] trace = Thread.currentThread().getStackTrace(); String lastItem = trace[depth].toString(); - if( shorter ) { + if (shorter) { int lastPeriod = lastItem.lastIndexOf('.'); lastPeriod = lastItem.lastIndexOf('.', lastPeriod-1); lastPeriod = lastItem.lastIndexOf('.', lastPeriod-1); lastItem = lastItem.substring(lastPeriod+1); return String.format("%s > from %s", debugGetCurrThreadId(), lastItem); } - return String.format("%s > %s called from %s", debugGetCurrThreadId(), trace[2].getClassName()+"."+trace[2].getMethodName(), lastItem); + return String.format("%s > %s called from %s", debugGetCurrThreadId(), + trace[2].getClassName() + "." + trace[2].getMethodName(), lastItem); } public static String debugGetStackTraceItem(int depth) { diff --git a/forge-gui/src/main/java/forge/ai/ComputerUtilMana.java b/forge-gui/src/main/java/forge/ai/ComputerUtilMana.java index 37aad1a5d8d..553d168a06c 100644 --- a/forge-gui/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-gui/src/main/java/forge/ai/ComputerUtilMana.java @@ -47,9 +47,17 @@ import forge.util.maps.MapOfLists; * */ public class ComputerUtilMana { - private final static boolean DEBUG_MANA_PAYMENT = false; + public static boolean canPayManaCost(ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) { + cost = new ManaCostBeingPaid(cost); //check copy of cost so it doesn't modify the exist cost being paid + return payManaCost(cost, sa, ai, true, 0, true); + } + + public static boolean payManaCost(ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) { + return payManaCost(cost, sa, ai, false, 0, true); + } + public static boolean canPayManaCost(final SpellAbility sa, final Player ai, final int extraMana) { return payManaCost(sa, ai, true, extraMana, true); } diff --git a/forge-gui/src/main/java/forge/game/mana/ManaCostBeingPaid.java b/forge-gui/src/main/java/forge/game/mana/ManaCostBeingPaid.java index 2c81dd4b843..e7e5b2ac6fa 100644 --- a/forge-gui/src/main/java/forge/game/mana/ManaCostBeingPaid.java +++ b/forge-gui/src/main/java/forge/game/mana/ManaCostBeingPaid.java @@ -50,7 +50,7 @@ import forge.util.maps.MapToAmount; /** *
- * ManaCost class. + * ManaCostBeingPaid class. *
* * @author Forge @@ -62,45 +62,49 @@ public class ManaCostBeingPaid { private ManaCostShard nextShard = null; private int remainingShards = 0; private boolean hasSentX = false; - - public ManaCostBeingPaidIterator() { + + public ManaCostBeingPaidIterator() { mch = unpaidShards.keySet().iterator(); } - + @Override public void remove() { throw new UnsupportedOperationException(); } - + @Override public ManaCostShard next() { - if (remainingShards == 0) + if (remainingShards == 0) { throw new UnsupportedOperationException("All shards were depleted, call hasNext()"); + } remainingShards--; return nextShard; } - + @Override public boolean hasNext() { - if ( remainingShards > 0 ) return true; - if ( !hasSentX ) { - if ( nextShard != ManaCostShard.X && cntX > 0) { + if (remainingShards > 0) { return true; } + if (!hasSentX) { + if (nextShard != ManaCostShard.X && cntX > 0) { nextShard = ManaCostShard.X; remainingShards = cntX; return true; - } else + } + else { hasSentX = true; + } } - if ( !mch.hasNext() ) return false; - + if (!mch.hasNext()) { return false; } + nextShard = mch.next(); - if ( nextShard == ManaCostShard.COLORLESS ) + if (nextShard == ManaCostShard.COLORLESS) { return this.hasNext(); // skip colorless + } remainingShards = unpaidShards.get(nextShard); - + return true; } - + @Override public int getTotalColorlessCost() { Integer c = unpaidShards.get(ManaCostShard.COLORLESS); @@ -110,15 +114,25 @@ public class ManaCostBeingPaid { // holds Mana_Part objects // ManaPartColor is stored before ManaPartColorless - private final MapToAmount
* Constructor for ManaCost.
@@ -134,9 +148,11 @@ public class ManaCostBeingPaid {
public ManaCostBeingPaid(ManaCost manaCost) {
this(manaCost, null);
}
+
public ManaCostBeingPaid(ManaCost manaCost, String srcRestriction) {
+ unpaidShards = new EnumMapToAmount
@@ -242,18 +260,20 @@ public class ManaCostBeingPaid {
List
* removeColorlessMana.
@@ -524,7 +548,6 @@ public class ManaCostBeingPaid {
final Card originalCard = sa.getSourceCard();
final SpellAbility spell = sa;
-
if (sa.isXCost() && !originalCard.isCopiedSpell()) {
originalCard.setXManaCostPaid(0);
}
@@ -544,7 +567,8 @@ public class ManaCostBeingPaid {
pc.getGame().getAction().exile(c);
}
}
- } else if (spell.getSourceCard().hasKeyword("Convoke")) {
+ }
+ else if (spell.getSourceCard().hasKeyword("Convoke")) {
adjustCostByConvoke(sa, spell);
}
} // isSpell
@@ -565,9 +589,11 @@ public class ManaCostBeingPaid {
for (final StaticAbility stAb : staticAbilities) {
if (stAb.getMapParams().get("Mode").equals("RaiseCost")) {
raiseAbilities.add(stAb);
- } else if (stAb.getMapParams().get("Mode").equals("ReduceCost")) {
+ }
+ else if (stAb.getMapParams().get("Mode").equals("ReduceCost")) {
reduceAbilities.add(stAb);
- } else if (stAb.getMapParams().get("Mode").equals("SetCost")) {
+ }
+ else if (stAb.getMapParams().get("Mode").equals("SetCost")) {
setAbilities.add(stAb);
}
}
@@ -601,27 +627,29 @@ public class ManaCostBeingPaid {
String chosenColor = null;
if (sa.getActivatingPlayer().isHuman()) {
workingCard = GuiChoose.oneOrNone("Tap for Convoke? " + toString(), untappedCreats);
- if( null == workingCard )
+ if (null == workingCard) {
break; // that means "I'm done"
+ }
List
* canMake. color is like "G", returns "Green".
@@ -272,16 +272,52 @@ public abstract class InputPayMana extends InputSyncronizedBase {
return bPaid;
}
+ protected boolean supportAutoPay() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onOk() {
+ if (supportAutoPay()) {
+ //use AI utility to automatically pay mana cost if possible
+ ComputerUtilMana.payManaCost(manaCost, saPaidFor, player);
+ this.showMessage();
+ }
+ }
+
+ protected void updateButtons() {
+ if (supportAutoPay()) {
+ ButtonUtil.setButtonText("Auto", "Cancel");
+ }
+ ButtonUtil.enableOnlyCancel();
+ }
+
+ protected final void updateMessage() {
+ if (supportAutoPay() && ComputerUtilMana.canPayManaCost(manaCost, saPaidFor, player)) {
+ ButtonUtil.enableAllFocusOk(); //enabled Auto button if mana cost can be paid
+ }
+ showMessage(getMessage());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected final void onStop() {
+ if (supportAutoPay()) {
+ ButtonUtil.reset();
+ }
+ }
+
/** {@inheritDoc} */
@Override
public void showMessage() {
if (isFinished()) { return; }
- ButtonUtil.enableOnlyCancel();
+ updateButtons();
onStateChanged();
}
protected void onStateChanged() {
- if(isAlreadyPaid()) {
+ if (isAlreadyPaid()) {
done();
stop();
}
@@ -297,11 +333,11 @@ public abstract class InputPayMana extends InputSyncronizedBase {
protected void onManaAbilityPaid() {} // some inputs overload it
protected abstract void done();
- protected abstract void updateMessage();
+ protected abstract String getMessage();
@Override
public String toString() {
- return String.format("PayManaBase %s left", manaCost.toString() );
+ return String.format("PayManaBase %s left", manaCost.toString());
}
public boolean isPaid() { return bPaid; }
diff --git a/forge-gui/src/main/java/forge/gui/input/InputPayManaExecuteCommands.java b/forge-gui/src/main/java/forge/gui/input/InputPayManaExecuteCommands.java
index 016bede8fcb..9465aeb9126 100644
--- a/forge-gui/src/main/java/forge/gui/input/InputPayManaExecuteCommands.java
+++ b/forge-gui/src/main/java/forge/gui/input/InputPayManaExecuteCommands.java
@@ -102,11 +102,9 @@ public class InputPayManaExecuteCommands extends InputPayMana {
this.stop();
}
- /* (non-Javadoc)
- * @see forge.control.input.InputPayManaBase#updateMessage()
- */
+ /** {@inheritDoc} */
@Override
- protected void updateMessage() {
+ protected String getMessage() {
final StringBuilder msg = new StringBuilder(this.message + "Pay Mana Cost: " + this.manaCost);
if (this.phyLifeToLose > 0) {
msg.append(" (");
@@ -117,6 +115,6 @@ public class InputPayManaExecuteCommands extends InputPayMana {
if (this.manaCost.containsPhyrexianMana()) {
msg.append("\n(Click on your life total to pay life for phyrexian mana.)");
}
- showMessage(msg.toString());
+ return msg.toString();
}
}
diff --git a/forge-gui/src/main/java/forge/gui/input/InputPayManaOfCostPayment.java b/forge-gui/src/main/java/forge/gui/input/InputPayManaOfCostPayment.java
index dcd7c6a6a0f..f844f34c21e 100644
--- a/forge-gui/src/main/java/forge/gui/input/InputPayManaOfCostPayment.java
+++ b/forge-gui/src/main/java/forge/gui/input/InputPayManaOfCostPayment.java
@@ -6,7 +6,6 @@ import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class InputPayManaOfCostPayment extends InputPayMana {
-
public InputPayManaOfCostPayment(ManaCostBeingPaid cost, SpellAbility spellAbility) {
super(spellAbility);
manaCost = cost;
@@ -39,7 +38,8 @@ public class InputPayManaOfCostPayment extends InputPayMana {
stop();
}
- protected void updateMessage() {
+ @Override
+ protected String getMessage() {
final String displayMana = manaCost.toString(false);
final StringBuilder msg = new StringBuilder("Pay Mana Cost: " + displayMana);
@@ -53,6 +53,6 @@ public class InputPayManaOfCostPayment extends InputPayMana {
msg.append("\n(Click on your life total to pay life for phyrexian mana.)");
}
- showMessage(msg.toString());
+ return msg.toString();
}
}
diff --git a/forge-gui/src/main/java/forge/gui/input/InputPayManaSimple.java b/forge-gui/src/main/java/forge/gui/input/InputPayManaSimple.java
index f834c653fa2..3c7523445a0 100644
--- a/forge-gui/src/main/java/forge/gui/input/InputPayManaSimple.java
+++ b/forge-gui/src/main/java/forge/gui/input/InputPayManaSimple.java
@@ -22,7 +22,6 @@ import forge.game.card.Card;
import forge.game.mana.ManaCostBeingPaid;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
-import forge.view.ButtonUtil;
//pays the cost of a card played from the player's hand
//the card is removed from the players hand if the cost is paid
@@ -102,7 +101,7 @@ public class InputPayManaSimple extends InputPayMana {
public final void showMessage() {
if (isFinished()) { return; }
- ButtonUtil.enableOnlyCancel();
+ updateButtons();
if (this.manaCost.isPaid() && !new ManaCostBeingPaid(this.originalManaCost).isPaid()) {
this.done();
@@ -117,7 +116,7 @@ public class InputPayManaSimple extends InputPayMana {
* @see forge.control.input.InputPayManaBase#updateMessage()
*/
@Override
- protected void updateMessage() {
+ protected String getMessage() {
final StringBuilder msg = new StringBuilder("Pay Mana Cost: " + this.manaCost.toString());
if (this.phyLifeToLose > 0) {
msg.append(" (");
@@ -130,6 +129,6 @@ public class InputPayManaSimple extends InputPayMana {
}
// has its own variant of checkIfPaid
- showMessage(msg.toString());
+ return msg.toString();
}
}
diff --git a/forge-gui/src/main/java/forge/gui/input/InputPayManaX.java b/forge-gui/src/main/java/forge/gui/input/InputPayManaX.java
index 31356d34871..ffb69262edd 100644
--- a/forge-gui/src/main/java/forge/gui/input/InputPayManaX.java
+++ b/forge-gui/src/main/java/forge/gui/input/InputPayManaX.java
@@ -55,6 +55,11 @@ public class InputPayManaX extends InputPayMana {
return !canceled && (xPaid > 0 || xCanBe0);
}
+ @Override
+ protected boolean supportAutoPay() {
+ return false;
+ }
+
@Override
public void showMessage() {
if (isFinished()) { return; }
@@ -63,7 +68,7 @@ public class InputPayManaX extends InputPayMana {
}
@Override
- protected void updateMessage() {
+ protected String getMessage() {
StringBuilder msg = new StringBuilder("Pay X Mana Cost for ");
msg.append(saPaidFor.getSourceCard().getName()).append("\n").append(this.xPaid);
msg.append(" Paid so far.");
@@ -79,7 +84,7 @@ public class InputPayManaX extends InputPayMana {
ButtonUtil.enableAllFocusOk();
}
- showMessage(msg.toString());
+ return msg.toString();
}
@Override
diff --git a/forge-gui/src/main/java/forge/gui/input/InputSyncronizedBase.java b/forge-gui/src/main/java/forge/gui/input/InputSyncronizedBase.java
index 660f197e380..101d2301805 100644
--- a/forge-gui/src/main/java/forge/gui/input/InputSyncronizedBase.java
+++ b/forge-gui/src/main/java/forge/gui/input/InputSyncronizedBase.java
@@ -6,14 +6,14 @@ import forge.FThreads;
import forge.Singletons;
import forge.error.BugReporter;
-public abstract class InputSyncronizedBase extends InputBase implements InputSynchronized {
+public abstract class InputSyncronizedBase extends InputBase implements InputSynchronized {
private static final long serialVersionUID = 8756177361251703052L;
private final CountDownLatch cdlDone;
public InputSyncronizedBase() {
cdlDone = new CountDownLatch(1);
}
-
+
public void awaitLatchRelease() {
FThreads.assertExecutedByEdt(false);
try{
@@ -22,17 +22,26 @@ public abstract class InputSyncronizedBase extends InputBase implements InputSyn
BugReporter.reportException(e);
}
}
-
+
public final void relaseLatchWhenGameIsOver() {
cdlDone.countDown();
}
-
- protected final void stop() {
- // ensure input won't accept any user actions.
- FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { setFinished(); } });
- // thread irrelevant
+ protected final void stop() {
+ onStop();
+
+ // ensure input won't accept any user actions.
+ FThreads.invokeInEdtNowOrLater(new Runnable() {
+ @Override
+ public void run() {
+ setFinished();
+ }
+ });
+
+ // thread irrelevant
Singletons.getControl().getInputQueue().removeInput(InputSyncronizedBase.this);
cdlDone.countDown();
}
+
+ protected void onStop() { }
}
\ No newline at end of file
diff --git a/forge-gui/src/main/java/forge/util/maps/EnumMapToAmount.java b/forge-gui/src/main/java/forge/util/maps/EnumMapToAmount.java
index a7ec052a49a..1505a2c00bc 100644
--- a/forge-gui/src/main/java/forge/util/maps/EnumMapToAmount.java
+++ b/forge-gui/src/main/java/forge/util/maps/EnumMapToAmount.java
@@ -4,7 +4,6 @@ import java.util.EnumMap;
import java.util.Map;
-
/**
* TODO: Write javadoc for this type.
*
@@ -12,7 +11,6 @@ import java.util.Map;
public class EnumMapToAmount