mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Added locking UI input, added guards to playAbility, playLand and discard to disallow their execution from EDT
merged trunk up to 20537
This commit is contained in:
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -13696,6 +13696,7 @@ src/main/java/forge/Command.java svneol=native#text/plain
|
||||
src/main/java/forge/CommandList.java svneol=native#text/plain
|
||||
src/main/java/forge/Constant.java svneol=native#text/plain
|
||||
src/main/java/forge/CounterType.java svneol=native#text/plain
|
||||
src/main/java/forge/FThreads.java -text
|
||||
src/main/java/forge/GameEntity.java -text
|
||||
src/main/java/forge/GameLog.java -text
|
||||
src/main/java/forge/ImageCache.java svneol=native#text/plain
|
||||
@@ -14080,6 +14081,7 @@ src/main/java/forge/control/input/InputAttack.java svneol=native#text/plain
|
||||
src/main/java/forge/control/input/InputBlock.java svneol=native#text/plain
|
||||
src/main/java/forge/control/input/InputCleanup.java svneol=native#text/plain
|
||||
src/main/java/forge/control/input/InputControl.java svneol=native#text/plain
|
||||
src/main/java/forge/control/input/InputLockUI.java -text
|
||||
src/main/java/forge/control/input/InputMulligan.java svneol=native#text/plain
|
||||
src/main/java/forge/control/input/InputPassPriority.java svneol=native#text/plain
|
||||
src/main/java/forge/control/input/InputPayDiscardCost.java -text
|
||||
@@ -14520,7 +14522,6 @@ src/main/java/forge/util/MyRandom.java svneol=native#text/plain
|
||||
src/main/java/forge/util/PredicateString.java -text
|
||||
src/main/java/forge/util/ReflectionUtil.java -text
|
||||
src/main/java/forge/util/TextUtil.java -text
|
||||
src/main/java/forge/util/ThreadUtil.java svneol=native#text/plain
|
||||
src/main/java/forge/util/XmlUtil.java -text
|
||||
src/main/java/forge/util/package-info.java -text
|
||||
src/main/java/forge/util/storage/IStorage.java -text
|
||||
|
||||
94
src/main/java/forge/FThreads.java
Normal file
94
src/main/java/forge/FThreads.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package forge;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import forge.control.input.InputLockUI;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class FThreads {
|
||||
|
||||
private final static ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||
public static ExecutorService getCachedPool() {
|
||||
return threadPool;
|
||||
}
|
||||
|
||||
// This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5
|
||||
public final static ExecutorService getComputingPool(float loadFactor) {
|
||||
return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor)));
|
||||
}
|
||||
|
||||
public static boolean isMultiCoreSystem() {
|
||||
return Runtime.getRuntime().availableProcessors() > 1;
|
||||
}
|
||||
|
||||
/** 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.
|
||||
*
|
||||
* @param methodName   String, part of the custom exception message.
|
||||
* @param mustBeEDT   boolean: true = exception if not EDT, false = exception if EDT
|
||||
*/
|
||||
public static void checkEDT(final String methodName, final boolean mustBeEDT) {
|
||||
boolean isEDT = SwingUtilities.isEventDispatchThread();
|
||||
if ( isEDT != mustBeEDT ) {
|
||||
String modalOperator = mustBeEDT ? " must be" : " may not be";
|
||||
throw new IllegalStateException( methodName + modalOperator + " accessed from the event dispatch thread.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @param runnable
|
||||
*/
|
||||
public static void invokeInEDT(Runnable runnable) {
|
||||
SwingUtilities.invokeLater(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the given Runnable in an Event Dispatch Thread and wait for it to
|
||||
* finish; but <B>try to use SwingUtilities.invokeLater instead whenever
|
||||
* feasible.</B>
|
||||
*
|
||||
* Exceptions generated by SwingUtilities.invokeAndWait (if used), are
|
||||
* rethrown as RuntimeExceptions.
|
||||
*
|
||||
* @param proc
|
||||
* the Runnable to run
|
||||
* @see javax.swing.SwingUtilities#invokeLater(Runnable)
|
||||
*/
|
||||
public static void invokeInEDTAndWait(final Runnable proc) {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
// Just run in the current thread.
|
||||
proc.run();
|
||||
} else {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(proc);
|
||||
} catch (final InterruptedException exn) {
|
||||
throw new RuntimeException(exn);
|
||||
} catch (final InvocationTargetException exn) {
|
||||
throw new RuntimeException(exn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void invokeInNewThread(Runnable proc) {
|
||||
invokeInNewThread(proc, false);
|
||||
}
|
||||
|
||||
private final static InputLockUI inpuptLock = new InputLockUI();
|
||||
public static void invokeInNewThread(Runnable proc, boolean lockUI) {
|
||||
getCachedPool().execute(proc);
|
||||
if( lockUI ) {
|
||||
Singletons.getModel().getMatch().getInput().setInput(inpuptLock);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -153,13 +153,21 @@ public class StaticEffects {
|
||||
}
|
||||
|
||||
if (params.containsKey("AddColor")) {
|
||||
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(params.get("AddColor").split(
|
||||
" & "))));
|
||||
final String colors = params.get("AddColor");
|
||||
if (colors.equals("ChosenColor")) {
|
||||
addColors = CardUtil.getShortColorsString(se.getSource().getChosenColor());
|
||||
} else {
|
||||
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(" & "))));
|
||||
}
|
||||
}
|
||||
|
||||
if (params.containsKey("SetColor")) {
|
||||
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(params.get("SetColor").split(
|
||||
" & "))));
|
||||
final String colors = params.get("SetColor");
|
||||
if (colors.equals("ChosenColor")) {
|
||||
addColors = CardUtil.getShortColorsString(se.getSource().getChosenColor());
|
||||
} else {
|
||||
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(" & "))));
|
||||
}
|
||||
}
|
||||
|
||||
// modify players
|
||||
|
||||
@@ -927,7 +927,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
final Card source = sa.getSourceCard();
|
||||
final ZoneType origin = ZoneType.smartValueOf(sa.getParam("Origin"));
|
||||
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
|
||||
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
||||
final Target tgt = sa.getTarget();
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ public class DiscardEffect extends RevealEffectBase {
|
||||
} else if (mode.equals("RevealYouChoose") || mode.equals("RevealOppChoose") || mode.equals("TgtChoose")) {
|
||||
// Is Reveal you choose right? I think the wrong player is
|
||||
// being used?
|
||||
List<Card> dPHand = p.getCardsIn(ZoneType.Hand);
|
||||
List<Card> dPHand = new ArrayList<Card>(p.getCardsIn(ZoneType.Hand));
|
||||
if (dPHand.isEmpty())
|
||||
continue; // for loop over players
|
||||
|
||||
|
||||
@@ -5,18 +5,11 @@ import java.util.List;
|
||||
|
||||
|
||||
import forge.Card;
|
||||
import forge.Singletons;
|
||||
import forge.card.cost.Cost;
|
||||
import forge.card.spellability.AbilityActivated;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.control.input.Input;
|
||||
import forge.control.input.InputSelectManyCards;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.gui.match.CMatchUI;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
@@ -102,96 +95,5 @@ class CardFactoryArtifacts {
|
||||
ab1.setStackDescription(sb.toString());
|
||||
card.addSpellAbility(ab1);
|
||||
} // *************** END ************ END **************************
|
||||
|
||||
// *************** START *********** START **************************
|
||||
else if (cardName.equals("Scroll Rack")) {
|
||||
class AbilityScrollRack extends AbilityActivated {
|
||||
public AbilityScrollRack(final Card ca, final Cost co, final Target t) {
|
||||
super(ca, co, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbilityActivated getCopy() {
|
||||
AbilityActivated res = new AbilityScrollRack(getSourceCard(),
|
||||
getPayCosts(), getTarget() == null ? null : new Target(getTarget()));
|
||||
CardFactory.copySpellAbility(this, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -5588587187720068547L;
|
||||
|
||||
@Override
|
||||
public void resolve() {
|
||||
// not implemented for compy
|
||||
if (card.getController().isHuman()) {
|
||||
|
||||
InputSelectManyCards inp = new InputSelectManyCards(0, Integer.MAX_VALUE) {
|
||||
private static final long serialVersionUID = 806464726820739922L;
|
||||
|
||||
@Override
|
||||
protected boolean isValidChoice(Card c) {
|
||||
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
|
||||
return zone.is(ZoneType.Hand) && c.getController() == card.getController();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.control.input.InputSelectManyCards#onDone()
|
||||
*/
|
||||
@Override
|
||||
protected Input onDone() {
|
||||
for (final Card c : selected) {
|
||||
Singletons.getModel().getGame().getAction().exile(c);
|
||||
}
|
||||
|
||||
// Put that many cards from the top of your
|
||||
// library into your hand.
|
||||
// Ruling: This is not a draw...
|
||||
final PlayerZone lib = card.getController().getZone(ZoneType.Library);
|
||||
int numCards = 0;
|
||||
while ((lib.size() > 0) && (numCards < selected.size())) {
|
||||
Singletons.getModel().getGame().getAction().moveToHand(lib.get(0));
|
||||
numCards++;
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(card.getName()).append(" - Returning cards to top of library.");
|
||||
CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString());
|
||||
|
||||
// Then look at the exiled cards and put them on
|
||||
// top of your library in any order.
|
||||
while (selected.size() > 0) {
|
||||
final Card c1 = GuiChoose.one("Put a card on top of your library.", selected);
|
||||
Singletons.getModel().getGame().getAction().moveToLibrary(c1);
|
||||
selected.remove(c1);
|
||||
}
|
||||
return null; }
|
||||
};
|
||||
inp.setMessage(card.getName() + " - Exile cards from hand. Currently, %d selected. (Press OK when done.)");
|
||||
|
||||
Singletons.getModel().getMatch().getInput().setInput(inp);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlayAI() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
final Cost abCost = new Cost(card, "1 T", true);
|
||||
final AbilityActivated ability = new AbilityScrollRack(card, abCost, null);
|
||||
|
||||
final StringBuilder sbDesc = new StringBuilder();
|
||||
sbDesc.append(abCost);
|
||||
sbDesc.append("Exile any number of cards from your hand face down. Put that many cards ");
|
||||
sbDesc.append("from the top of your library into your hand. Then look at the exiled cards ");
|
||||
sbDesc.append("and put them on top of your library in any order.");
|
||||
ability.setDescription(sbDesc.toString());
|
||||
|
||||
final StringBuilder sbStack = new StringBuilder();
|
||||
sbStack.append(cardName).append(" - exile any number of cards from your hand.");
|
||||
ability.setStackDescription(sbStack.toString());
|
||||
card.addSpellAbility(ability);
|
||||
} // *************** END ************ END **************************
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ import javax.swing.SwingUtilities;
|
||||
|
||||
import org.apache.commons.lang.time.StopWatch;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardRulesReader;
|
||||
import forge.control.FControl;
|
||||
import forge.error.BugReporter;
|
||||
import forge.gui.toolbox.FProgressBar;
|
||||
import forge.util.FileUtil;
|
||||
@@ -62,7 +62,7 @@ public class CardStorageReader {
|
||||
/** Default charset when loading from files. */
|
||||
public static final String DEFAULT_CHARSET_NAME = "US-ASCII";
|
||||
|
||||
final private boolean useThreadPool = FControl.isMultiCoreSystem();
|
||||
final private boolean useThreadPool = FThreads.isMultiCoreSystem();
|
||||
final private int NUMBER_OF_PARTS = 25;
|
||||
|
||||
final private CountDownLatch cdl = new CountDownLatch(NUMBER_OF_PARTS);
|
||||
@@ -209,7 +209,7 @@ public class CardStorageReader {
|
||||
|
||||
try {
|
||||
if ( useThreadPool ) {
|
||||
final ExecutorService executor = FControl.getComputingPool(0.5f);
|
||||
final ExecutorService executor = FThreads.getComputingPool(0.5f);
|
||||
final List<Future<List<CardRules>>> parts = executor.invokeAll(tasks);
|
||||
executor.shutdown();
|
||||
cdl.await();
|
||||
|
||||
@@ -25,9 +25,6 @@ import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLayeredPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
@@ -85,8 +82,6 @@ public enum FControl {
|
||||
DRAFTING_PROCESS
|
||||
}
|
||||
|
||||
private final ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||
|
||||
private final SoundSystem soundSystem = new SoundSystem();
|
||||
|
||||
|
||||
@@ -317,21 +312,4 @@ public enum FControl {
|
||||
public SoundSystem getSoundSystem() {
|
||||
return soundSystem;
|
||||
}
|
||||
|
||||
public ExecutorService getThreadPool() {
|
||||
return threadPool;
|
||||
}
|
||||
|
||||
// This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5
|
||||
public final static ExecutorService getComputingPool(float loadFactor) {
|
||||
return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor)));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMultiCoreSystem() {
|
||||
return Runtime.getRuntime().availableProcessors() > 1;
|
||||
}
|
||||
}
|
||||
|
||||
18
src/main/java/forge/control/input/InputLockUI.java
Normal file
18
src/main/java/forge/control/input/InputLockUI.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package forge.control.input;
|
||||
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.view.ButtonUtil;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class InputLockUI extends Input {
|
||||
private static final long serialVersionUID = 5777143577098597374L;
|
||||
|
||||
public void showMessage() {
|
||||
ButtonUtil.disableAll();
|
||||
CMatchUI.SINGLETON_INSTANCE.showMessage("Waiting for actions...");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,10 +18,10 @@
|
||||
package forge.control.input;
|
||||
|
||||
import forge.Card;
|
||||
import forge.FThreads;
|
||||
import forge.Singletons;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.control.FControl;
|
||||
import forge.game.GameState;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.gui.GuiDisplayUtil;
|
||||
@@ -80,11 +80,17 @@ public class InputPassPriority extends Input {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void selectCard(final Card card) {
|
||||
Player player = Singletons.getControl().getPlayer();
|
||||
GameState game = Singletons.getModel().getGame();
|
||||
SpellAbility ab = player.getController().getAbilityToPlay(game.getAbilitesOfCard(card, player));
|
||||
final Player player = Singletons.getControl().getPlayer();
|
||||
final SpellAbility ab = player.getController().getAbilityToPlay(player.getGame().getAbilitesOfCard(card, player));
|
||||
if ( null != ab) {
|
||||
player.playSpellAbility(card, ab);
|
||||
Runnable execAbility = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
player.playSpellAbility(card, ab);
|
||||
}
|
||||
};
|
||||
|
||||
FThreads.invokeInNewThread(execAbility, true);
|
||||
}
|
||||
else {
|
||||
SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE);
|
||||
|
||||
@@ -10,6 +10,7 @@ import forge.CardCharacteristicName;
|
||||
import forge.CardColor;
|
||||
import forge.CardLists;
|
||||
import forge.CardPredicates;
|
||||
import forge.FThreads;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.ability.AbilityUtils;
|
||||
import forge.card.ability.ApiType;
|
||||
@@ -358,6 +359,7 @@ public class GameActionPlay {
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
*/
|
||||
public final void playSpellAbility(SpellAbility sa, Player activator) {
|
||||
FThreads.checkEDT("Player.playSpellAbility", false);
|
||||
sa.setActivatingPlayer(activator);
|
||||
|
||||
final Card source = sa.getSourceCard();
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -44,6 +45,7 @@ import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.SpellPermanent;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameState;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.AIPlayer;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -762,5 +764,97 @@ public class AiController {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void onPriorityRecieved() {
|
||||
final PhaseType phase = game.getPhaseHandler().getPhase();
|
||||
|
||||
if (game.getStack().size() > 0) {
|
||||
playSpellAbilities(game);
|
||||
} else {
|
||||
switch(phase) {
|
||||
case CLEANUP:
|
||||
if ( game.getPhaseHandler().getPlayerTurn() == player ) {
|
||||
final int size = player.getCardsIn(ZoneType.Hand).size();
|
||||
|
||||
if (!player.isUnlimitedHandSize()) {
|
||||
int max = Math.min(player.getZone(ZoneType.Hand).size(), size - player.getMaxHandSize());
|
||||
final List<Card> toDiscard = player.getAi().getCardsToDiscard(max, (String[])null, null);
|
||||
for (int i = 0; i < toDiscard.size(); i++) {
|
||||
player.discard(toDiscard.get(i), null);
|
||||
}
|
||||
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case COMBAT_DECLARE_ATTACKERS:
|
||||
declareAttackers();
|
||||
break;
|
||||
|
||||
case MAIN1:
|
||||
case MAIN2:
|
||||
Log.debug("Computer " + phase.toString());
|
||||
playLands();
|
||||
// fall through is intended
|
||||
default:
|
||||
playSpellAbilities(game);
|
||||
break;
|
||||
}
|
||||
}
|
||||
player.getController().passPriority();
|
||||
}
|
||||
|
||||
|
||||
private void declareAttackers() {
|
||||
// 12/2/10(sol) the decision making here has moved to getAttackers()
|
||||
game.setCombat(new AiAttackController(player, player.getOpponent()).getAttackers());
|
||||
|
||||
final List<Card> att = game.getCombat().getAttackers();
|
||||
if (!att.isEmpty()) {
|
||||
game.getPhaseHandler().setCombat(true);
|
||||
}
|
||||
|
||||
for (final Card element : att) {
|
||||
// tapping of attackers happens after Propaganda is paid for
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Computer just assigned ").append(element.getName()).append(" as an attacker.");
|
||||
Log.debug(sb.toString());
|
||||
}
|
||||
|
||||
player.getZone(ZoneType.Battlefield).updateObservers();
|
||||
|
||||
game.getPhaseHandler().setPlayersPriorityPermission(false);
|
||||
|
||||
// ai is about to attack, cancel all phase skipping
|
||||
for (Player p : game.getPlayers()) {
|
||||
p.getController().autoPassCancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void playLands() {
|
||||
final Player player = getPlayer();
|
||||
List<Card> landsWannaPlay = getLandsToPlay();
|
||||
|
||||
while(landsWannaPlay != null && !landsWannaPlay.isEmpty() && player.canPlayLand(null)) {
|
||||
Card land = chooseBestLandToPlay(landsWannaPlay);
|
||||
landsWannaPlay.remove(land);
|
||||
player.playLand(land);
|
||||
game.getPhaseHandler().setPriority(player);
|
||||
}
|
||||
}
|
||||
|
||||
private void playSpellAbilities(final GameState game)
|
||||
{
|
||||
SpellAbility sa;
|
||||
do {
|
||||
sa = getSpellAbilityToPlay();
|
||||
if ( sa == null ) break;
|
||||
//System.out.println("Playing sa: " + sa);
|
||||
if (!ComputerUtil.handlePlayingSpellAbility(player, sa, game)) {
|
||||
break;
|
||||
}
|
||||
} while ( sa != null );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,18 +17,8 @@
|
||||
*/
|
||||
package forge.game.ai;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.FThreads;
|
||||
import forge.control.input.Input;
|
||||
import forge.game.GameState;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.AIPlayer;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -43,8 +33,6 @@ public class AiInputCommon extends Input {
|
||||
private static final long serialVersionUID = -3091338639571662216L;
|
||||
|
||||
private final AiController computer;
|
||||
private final AIPlayer player;
|
||||
private final GameState game;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -56,15 +44,13 @@ public class AiInputCommon extends Input {
|
||||
*/
|
||||
public AiInputCommon(final AiController iComputer) {
|
||||
this.computer = iComputer;
|
||||
player = computer.getPlayer();
|
||||
this.game = computer.getGame();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void showMessage() {
|
||||
// should not think when the game is over
|
||||
if (game.isGameOver()) {
|
||||
if (computer.getGame().isGameOver()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,100 +62,19 @@ public class AiInputCommon extends Input {
|
||||
* \"Detailed Error Trace\" to the Forge forum.");
|
||||
*/
|
||||
|
||||
final PhaseType phase = game.getPhaseHandler().getPhase();
|
||||
FThreads.invokeInNewThread(aiActions, true);
|
||||
|
||||
if (game.getStack().size() > 0) {
|
||||
playSpellAbilities(game);
|
||||
} else {
|
||||
switch(phase) {
|
||||
case CLEANUP:
|
||||
if ( game.getPhaseHandler().getPlayerTurn() == player ) {
|
||||
final int size = player.getCardsIn(ZoneType.Hand).size();
|
||||
|
||||
if (!player.isUnlimitedHandSize()) {
|
||||
int max = Math.min(player.getZone(ZoneType.Hand).size(), size - player.getMaxHandSize());
|
||||
final List<Card> toDiscard = player.getAi().getCardsToDiscard(max, (String[])null, null);
|
||||
for (int i = 0; i < toDiscard.size(); i++) {
|
||||
player.discard(toDiscard.get(i), null);
|
||||
}
|
||||
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case COMBAT_DECLARE_ATTACKERS:
|
||||
declareAttackers();
|
||||
break;
|
||||
|
||||
case MAIN1:
|
||||
case MAIN2:
|
||||
Log.debug("Computer " + phase.toString());
|
||||
playLands();
|
||||
// fall through is intended
|
||||
default:
|
||||
playSpellAbilities(game);
|
||||
break;
|
||||
}
|
||||
}
|
||||
player.getController().passPriority();
|
||||
} // getMessage();
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
*/
|
||||
private void declareAttackers() {
|
||||
// 12/2/10(sol) the decision making here has moved to getAttackers()
|
||||
game.setCombat(new AiAttackController(player, player.getOpponent()).getAttackers());
|
||||
final Runnable aiActions = new Runnable() {
|
||||
|
||||
final List<Card> att = game.getCombat().getAttackers();
|
||||
if (!att.isEmpty()) {
|
||||
game.getPhaseHandler().setCombat(true);
|
||||
@Override
|
||||
public void run() {
|
||||
computer.onPriorityRecieved();
|
||||
}
|
||||
};
|
||||
|
||||
for (final Card element : att) {
|
||||
// tapping of attackers happens after Propaganda is paid for
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Computer just assigned ").append(element.getName()).append(" as an attacker.");
|
||||
Log.debug(sb.toString());
|
||||
}
|
||||
|
||||
player.getZone(ZoneType.Battlefield).updateObservers();
|
||||
|
||||
game.getPhaseHandler().setPlayersPriorityPermission(false);
|
||||
|
||||
// ai is about to attack, cancel all phase skipping
|
||||
for (Player p : game.getPlayers()) {
|
||||
p.getController().autoPassCancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
*/
|
||||
private void playLands() {
|
||||
final Player player = computer.getPlayer();
|
||||
List<Card> landsWannaPlay = computer.getLandsToPlay();
|
||||
|
||||
while(landsWannaPlay != null && !landsWannaPlay.isEmpty() && player.canPlayLand(null)) {
|
||||
Card land = computer.chooseBestLandToPlay(landsWannaPlay);
|
||||
landsWannaPlay.remove(land);
|
||||
player.playLand(land);
|
||||
game.getPhaseHandler().setPriority(player);
|
||||
}
|
||||
}
|
||||
|
||||
protected void playSpellAbilities(final GameState game)
|
||||
{
|
||||
SpellAbility sa;
|
||||
do {
|
||||
sa = computer.getSpellAbilityToPlay();
|
||||
if ( sa == null ) break;
|
||||
//System.out.println("Playing sa: " + sa);
|
||||
if (!ComputerUtil.handlePlayingSpellAbility(player, sa, game)) {
|
||||
break;
|
||||
}
|
||||
} while ( sa != null );
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -39,6 +39,7 @@ import forge.CardPredicates.Presets;
|
||||
import forge.CardUtil;
|
||||
import forge.Constant.Preferences;
|
||||
import forge.CounterType;
|
||||
import forge.FThreads;
|
||||
import forge.GameEntity;
|
||||
import forge.Singletons;
|
||||
import forge.card.ability.AbilityFactory;
|
||||
@@ -1623,6 +1624,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
*/
|
||||
protected final void doDiscard(final Card c, final SpellAbility sa) {
|
||||
FThreads.checkEDT("Player.doDiscard", false);
|
||||
// TODO: This line should be moved inside CostPayment somehow
|
||||
/*if (sa != null) {
|
||||
sa.addCostToHashList(c, "Discarded");
|
||||
@@ -1845,6 +1847,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
|
||||
* a {@link forge.Card} object.
|
||||
*/
|
||||
public final void playLand(final Card land) {
|
||||
FThreads.checkEDT("Player.playSpellAbility", false);
|
||||
if (this.canPlayLand(land)) {
|
||||
land.setController(this, 0);
|
||||
game.getAction().moveTo(this.getZone(ZoneType.Battlefield), land);
|
||||
|
||||
@@ -31,7 +31,6 @@ import java.util.List;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import forge.Card;
|
||||
import forge.gui.match.VMatchUI;
|
||||
@@ -89,26 +88,6 @@ public final class GuiUtils {
|
||||
return ttf;
|
||||
}
|
||||
|
||||
/** 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.
|
||||
*
|
||||
* @param methodName   String, part of the custom exception message.
|
||||
* @param mustBeEDT   boolean: true = exception if not EDT, false = exception if EDT
|
||||
*/
|
||||
public static void checkEDT(final String methodName, final boolean mustBeEDT) {
|
||||
boolean isEDT = SwingUtilities.isEventDispatchThread();
|
||||
|
||||
if (!isEDT && mustBeEDT) {
|
||||
throw new IllegalStateException(
|
||||
methodName + " must be accessed from the event dispatch thread.");
|
||||
}
|
||||
else if (isEDT && !mustBeEDT) {
|
||||
throw new IllegalStateException(
|
||||
methodName + " may not be accessed from the event dispatch thread.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all visually highlighted card panels on the battlefield.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
||||
import forge.Card;
|
||||
import forge.FThreads;
|
||||
import forge.Singletons;
|
||||
import forge.control.input.Input;
|
||||
import forge.game.player.Player;
|
||||
@@ -58,7 +59,17 @@ public class InputProxy implements Observer {
|
||||
public final synchronized void setInput(final Input in) {
|
||||
valid = true;
|
||||
this.input = in;
|
||||
this.input.showMessage(); // this call may invalidate the input by the time it returns
|
||||
|
||||
if ( null == input ) {
|
||||
throw new NullPointerException("input is null");
|
||||
}
|
||||
|
||||
FThreads.invokeInEDT(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
InputProxy.this.input.showMessage(); // this call may invalidate the input by the time it returns
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,7 +47,7 @@ public enum CPicture implements ICDoc {
|
||||
*/
|
||||
public void showCard(final Card c) {
|
||||
canFlip = c != null && (c.isDoubleFaced() || c.isFlipCard());
|
||||
this.currentCard = c;
|
||||
currentCard = c;
|
||||
flipped = canFlip && (c.getCurState() == CardCharacteristicName.Transformed ||
|
||||
c.getCurState() == CardCharacteristicName.Flipped);
|
||||
VPicture.SINGLETON_INSTANCE.getLblFlipcard().setVisible(canFlip);
|
||||
@@ -60,13 +60,15 @@ public enum CPicture implements ICDoc {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentCard = null;
|
||||
canFlip = false;
|
||||
flipped = false;
|
||||
currentCard = null;
|
||||
VPicture.SINGLETON_INSTANCE.getLblFlipcard().setVisible(false);
|
||||
VPicture.SINGLETON_INSTANCE.getPnlPicture().setCard(item);
|
||||
}
|
||||
|
||||
public Card getCurrentCard() {
|
||||
return this.currentCard;
|
||||
return currentCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,7 +102,8 @@ public enum CPicture implements ICDoc {
|
||||
} else if (currentCard.isFlipCard()) {
|
||||
newState = CardCharacteristicName.Flipped;
|
||||
} else {
|
||||
// if this is hit, then there is a misalignment between this method and the showCard method above
|
||||
// if this is hit, then then showCard has been modified to handle additional types, but
|
||||
// this function is missing an else if statement above
|
||||
throw new RuntimeException("unhandled flippable card");
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,7 @@ import java.util.Date;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import forge.gui.GuiUtils;
|
||||
import forge.FThreads;
|
||||
|
||||
/**
|
||||
* A simple progress bar component using the Forge skin.
|
||||
@@ -37,7 +37,7 @@ public class FProgressBar extends JProgressBar {
|
||||
* @param s0   A description to prepend before statistics.
|
||||
*/
|
||||
public void setDescription(final String s0) {
|
||||
GuiUtils.checkEDT("FProgressBar$setDescription", true);
|
||||
FThreads.checkEDT("FProgressBar$setDescription", true);
|
||||
this.desc = s0;
|
||||
this.setString(s0);
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public class FProgressBar extends JProgressBar {
|
||||
|
||||
/** Resets the various values required for this class. Must be called from EDT. */
|
||||
public void reset() {
|
||||
GuiUtils.checkEDT("FProgressBar$reset", true);
|
||||
FThreads.checkEDT("FProgressBar$reset", true);
|
||||
this.setIndeterminate(true);
|
||||
this.setValue(0);
|
||||
this.tempVal = 0;
|
||||
|
||||
@@ -37,6 +37,7 @@ import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.LineBorder;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.gui.GuiUtils;
|
||||
import forge.view.FView;
|
||||
|
||||
@@ -419,7 +420,7 @@ public enum FSkin {
|
||||
*/
|
||||
public static void loadLight(final String skinName) {
|
||||
// No need for this method to be loaded while on the EDT.
|
||||
GuiUtils.checkEDT("FSkin$constructor", false);
|
||||
FThreads.checkEDT("FSkin$constructor", false);
|
||||
|
||||
// Non-default (preferred) skin name and dir.
|
||||
FSkin.preferredName = skinName.toLowerCase().replace(' ', '_');
|
||||
@@ -479,7 +480,7 @@ public enum FSkin {
|
||||
*/
|
||||
public static void loadFull() {
|
||||
// No need for this method to be loaded while on the EDT.
|
||||
GuiUtils.checkEDT("FSkin$load", false);
|
||||
FThreads.checkEDT("FSkin$load", false);
|
||||
|
||||
// Preferred skin name must be called via loadLight() method,
|
||||
// which does some cleanup and init work.
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.List;
|
||||
|
||||
import forge.Constant;
|
||||
import forge.Constant.Preferences;
|
||||
import forge.FThreads;
|
||||
import forge.card.BoosterData;
|
||||
import forge.card.CardBlock;
|
||||
import forge.card.CardRulesReader;
|
||||
@@ -43,7 +44,6 @@ import forge.game.MatchController;
|
||||
import forge.game.limited.GauntletMini;
|
||||
import forge.game.player.LobbyPlayer;
|
||||
import forge.gauntlet.GauntletData;
|
||||
import forge.gui.GuiUtils;
|
||||
import forge.item.CardDb;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
@@ -161,7 +161,7 @@ public enum FModel {
|
||||
this.loadDynamicGamedata();
|
||||
|
||||
// Loads all cards (using progress bar).
|
||||
GuiUtils.checkEDT("CardFactory$constructor", false);
|
||||
FThreads.checkEDT("CardFactory$constructor", false);
|
||||
final CardStorageReader reader = new CardStorageReader(NewConstants.CARD_DATA_DIR, true);
|
||||
try {
|
||||
// this fills in our map of card names to Card instances.
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* The files in the directory "net/slightlymagic/braids" and in all subdirectories of it (the "Files") are
|
||||
* Copyright 2011 Braids Cabal-Conjurer. They are available under either Forge's
|
||||
* main license (the GNU Public License; see LICENSE.txt in Forge's top directory)
|
||||
* or under the Apache License, as explained below.
|
||||
*
|
||||
* The Files are additionally licensed under the Apache License, Version 2.0 (the
|
||||
* "Apache License"); you may not use the files in this directory except in
|
||||
* compliance with one of its two licenses. You may obtain a copy of the Apache
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Apache License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the Apache License for the specific language governing permissions and
|
||||
* limitations under the Apache License.
|
||||
*
|
||||
*/
|
||||
package forge.util;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
/**
|
||||
* Some general-purpose functions.
|
||||
*/
|
||||
public final class ThreadUtil {
|
||||
|
||||
/**
|
||||
* Invoke the given Runnable in an Event Dispatch Thread and wait for it to
|
||||
* finish; but <B>try to use SwingUtilities.invokeLater instead whenever
|
||||
* feasible.</B>
|
||||
*
|
||||
* Exceptions generated by SwingUtilities.invokeAndWait (if used), are
|
||||
* rethrown as RuntimeExceptions.
|
||||
*
|
||||
* @param proc
|
||||
* the Runnable to run
|
||||
* @see javax.swing.SwingUtilities#invokeLater(Runnable)
|
||||
*/
|
||||
public static void invokeInEventDispatchThreadAndWait(final Runnable proc) {
|
||||
// by
|
||||
// Braids
|
||||
// on
|
||||
// 8/18/11
|
||||
// 11:19
|
||||
// PM
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
// Just run in the current thread.
|
||||
proc.run();
|
||||
} else {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(proc);
|
||||
} catch (final InterruptedException exn) {
|
||||
throw new RuntimeException(exn);
|
||||
// 11:19 PM
|
||||
} catch (final InvocationTargetException exn) {
|
||||
throw new RuntimeException(exn);
|
||||
// 11:19 PM
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user