diff --git a/forge-gui/src/main/java/forge/control/InputQueue.java b/forge-gui/src/main/java/forge/control/InputQueue.java index a8ee4956094..4e697a3f4e1 100644 --- a/forge-gui/src/main/java/forge/control/InputQueue.java +++ b/forge-gui/src/main/java/forge/control/InputQueue.java @@ -35,21 +35,18 @@ import forge.gui.input.InputSynchronized; * @version $Id$ */ public class InputQueue extends Observable { - private final BlockingDeque inputStack = new LinkedBlockingDeque(); private final InputLockUI inputLock; - public InputQueue() { inputLock = new InputLockUI(this); } - public final void updateObservers() { this.setChanged(); this.notifyObservers(); } - + public final Input getInput() { return inputStack.isEmpty() ? null : this.inputStack.peek(); } @@ -81,25 +78,24 @@ public class InputQueue extends Observable { public String printInputStack() { return inputStack.toString(); } - + public void setInputAndWait(InputSynchronized input) { this.inputStack.push(input); syncPoint(); this.updateObservers(); - + input.awaitLatchRelease(); } - + void setInput(InputSynchronized input) { this.inputStack.push(input); syncPoint(); this.updateObservers(); } - - public void syncPoint() { + public void syncPoint() { synchronized (inputLock) { - // acquire and release lock, so that actions from Game thread happen before EDT reads their results + // acquire and release lock, so that actions from Game thread happen before EDT reads their results } } @@ -113,5 +109,4 @@ public class InputQueue extends Observable { break; } } - } // InputControl diff --git a/forge-gui/src/main/java/forge/game/combat/CombatUtil.java b/forge-gui/src/main/java/forge/game/combat/CombatUtil.java index dff6c9c644d..28fdb4cec04 100644 --- a/forge-gui/src/main/java/forge/game/combat/CombatUtil.java +++ b/forge-gui/src/main/java/forge/game/combat/CombatUtil.java @@ -76,7 +76,6 @@ public class CombatUtil { * @return a boolean. */ public static boolean canBlock(final Card blocker, final Combat combat) { - if (blocker == null) { return false; } @@ -131,7 +130,6 @@ public class CombatUtil { * @return a boolean. */ public static boolean canBlock(final Card blocker, final boolean nextTurn) { - if (blocker == null) { return false; } @@ -176,7 +174,6 @@ public class CombatUtil { * @return a boolean. */ public static boolean canBeBlocked(final Card attacker, final Combat combat, Player defendingPlayer) { - if (attacker == null) { return true; } @@ -206,7 +203,6 @@ public class CombatUtil { * @return a boolean. */ public static boolean canBeBlocked(final Card attacker, Player defender) { - if (attacker == null) { return true; } @@ -366,7 +362,6 @@ public class CombatUtil { * @return a boolean. */ public static String validateBlocks(final Combat combat, final Player defending) { - final List defendersArmy = defending.getCreaturesInPlay(); final List attackers = combat.getAttackers(); final List blockers = CardLists.filterControlledBy(combat.getAllBlockers(), defending); @@ -494,6 +489,35 @@ public class CombatUtil { return false; } + // can a player block with one or more creatures at the moment? + /** + *

+ * canAttack. + *

+ * + * @param p + * a {@link forge.game.player} object. + * @param combat + * a {@link forge.game.combat.Combat} object. + * @return a boolean. + */ + public static boolean canBlock(Player p, Combat combat) { + List creatures = p.getCreaturesInPlay(); + if (creatures.isEmpty()) { return false; } + + List attackers = combat.getAttackers(); + if (attackers.isEmpty()) { return false; } + + for (Card c : creatures) { + for (Card a : attackers) { + if (CombatUtil.canBlock(a, c, combat)) { + return true; + } + } + } + return false; + } + // can the blocker block the attacker given the combat state? /** *

@@ -509,7 +533,6 @@ public class CombatUtil { * @return a boolean. */ public static boolean canBlock(final Card attacker, final Card blocker, final Combat combat) { - if (attacker == null || blocker == null) { return false; } @@ -614,7 +637,6 @@ public class CombatUtil { return false; } - if (attacker.hasStartOfKeyword("CantBeBlockedBy ")) { final int keywordPosition = attacker.getKeywordPosition("CantBeBlockedBy "); final String parse = attacker.getKeyword().get(keywordPosition).toString(); diff --git a/forge-gui/src/main/java/forge/game/phase/PhaseHandler.java b/forge-gui/src/main/java/forge/game/phase/PhaseHandler.java index c59a746dc22..1056ce1b5f0 100644 --- a/forge-gui/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-gui/src/main/java/forge/game/phase/PhaseHandler.java @@ -555,13 +555,16 @@ public class PhaseHandler implements java.io.Serializable { if (game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.attackerChoosesBlockers)) { whoDeclaresBlockers = combat.getAttackingPlayer(); } - if ( combat.isPlayerAttacked(p) ) { - whoDeclaresBlockers.getController().declareBlockers(p, combat); - } else - continue; + if (combat.isPlayerAttacked(p)) { + if (CombatUtil.canBlock(p, combat)) { + whoDeclaresBlockers.getController().declareBlockers(p, combat); + } + } + else { continue; } - if ( game.isGameOver() ) // they just like to close window at any moment + if (game.isGameOver()) { // they just like to close window at any moment return; + } // Handles removing cards like Mogg Flunkies from combat if group block // didn't occur diff --git a/forge-gui/src/main/java/forge/game/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/game/player/PlayerControllerHuman.java index 22a6f01e8e1..9e2e198ce84 100644 --- a/forge-gui/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -86,7 +86,7 @@ public class PlayerControllerHuman extends PlayerController { public boolean isUiSetToSkipPhase(final Player turn, final PhaseType phase) { return !CMatchUI.SINGLETON_INSTANCE.stopAtPhase(turn, phase); } - + /** * Uses GUI to learn which spell the player (human in our case) would like to play */ @@ -145,7 +145,7 @@ public class PlayerControllerHuman extends PlayerController { }); menu.show(triggerEvent.getComponent(), triggerEvent.getX(), triggerEvent.getY()); } - + return null; //delay ability until choice made } @@ -197,7 +197,7 @@ public class PlayerControllerHuman extends PlayerController { CardPool newSb = new CardPool(); List newMain = null; - + if (sbSize == 0 && mainSize == deckMinSize) { // Skip sideboard loop if there are no sideboarding opportunities newMain = main.toFlatList(); @@ -223,7 +223,7 @@ public class PlayerControllerHuman extends PlayerController { for(PaperCard c : newMain) { newSb.remove(c); } - + Deck res = (Deck)deck.copyTo(deck.getName()); res.getMain().clear(); res.getMain().add(newMain); @@ -239,7 +239,7 @@ public class PlayerControllerHuman extends PlayerController { @Override public Map assignCombatDamage(Card attacker, List blockers, int damageDealt, GameEntity defender, boolean overrideOrder) { // Attacker is a poor name here, since the creature assigning damage - // could just as easily be the blocker. + // could just as easily be the blocker. Map map; if (defender != null && assignDamageAsIfNotBlocked(attacker)) { map = new HashMap(); @@ -254,7 +254,7 @@ public class PlayerControllerHuman extends PlayerController { } return map; } - + private final boolean assignDamageAsIfNotBlocked(Card attacker) { return attacker.hasKeyword("CARDNAME assigns its combat damage as though it weren't blocked.") || (attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.") @@ -270,8 +270,7 @@ public class PlayerControllerHuman extends PlayerController { for(int i = canChooseZero ? 0 : 1; i < 10; i++) options.add(Integer.valueOf(i)); options.add("Other amount"); - - + Object chosen = GuiChoose.oneOrNone("Choose " + announce + " for " + ability.getSourceCard().getName(), options); if (chosen instanceof Integer || chosen == null) return (Integer)chosen; @@ -280,27 +279,26 @@ public class PlayerControllerHuman extends PlayerController { while(true){ String str = JOptionPane.showInputDialog(JOptionPane.getRootFrame(), message, ability.getSourceCard().getName(), JOptionPane.QUESTION_MESSAGE); if (null == str) return null; // that is 'cancel' - + if(StringUtils.isNumeric(str)) { Integer val = Integer.valueOf(str); - if (val == 0 && canChooseZero || val > 0) + if (val == 0 && canChooseZero || val > 0) return val; } GuiDialog.message("You have to enter a valid number", "Announce value"); } } - @Override public List choosePermanentsToSacrifice(SpellAbility sa, int min, int max, List valid, String message) { String outerMessage = "Select %d " + message + "(s) to sacrifice"; - return choosePermanentsTo(min, max, valid, outerMessage); + return choosePermanentsTo(min, max, valid, outerMessage); } @Override public List choosePermanentsToDestroy(SpellAbility sa, int min, int max, List valid, String message) { String outerMessage = "Select %d " + message + "(s) to be destroyed"; - return choosePermanentsTo(min, max, valid, outerMessage); + return choosePermanentsTo(min, max, valid, outerMessage); } private List choosePermanentsTo(int min, int max, List valid, String outerMessage) { @@ -322,17 +320,17 @@ public class PlayerControllerHuman extends PlayerController { @Override public List chooseCardsForEffect(List sourceList, SpellAbility sa, String title, int amount, boolean isOptional) { - // If only one card to choose, use a dialog box. - // Otherwise, use the order dialog to be able to grab multiple cards in one shot + // If only one card to choose, use a dialog box. + // Otherwise, use the order dialog to be able to grab multiple cards in one shot if (amount == 1) { return Lists.newArrayList(chooseSingleCardForEffect(sourceList, sa, title, isOptional)); } - + GuiUtils.setPanelSelection(sa.getSourceCard()); int remaining = isOptional ? -1 : Math.max(sourceList.size() - amount, 0); return GuiChoose.order(title, "Chosen Cards", remaining, sourceList, null, sa.getSourceCard()); } - + @Override public Card chooseSingleCardForEffect(Collection options, SpellAbility sa, String title, boolean isOptional) { // Human is supposed to read the message and understand from it what to choose @@ -340,7 +338,7 @@ public class PlayerControllerHuman extends PlayerController { return null; if ( !isOptional && options.size() == 1 ) return Iterables.getFirst(options, null); - + boolean canUseSelectCardsInput = true; for(Card c : options) { Zone cz = c.getZone(); @@ -359,7 +357,7 @@ public class PlayerControllerHuman extends PlayerController { Singletons.getControl().getInputQueue().setInputAndWait(input); return Iterables.getFirst(input.getSelected(), null); } - + return isOptional ? GuiChoose.oneOrNone(title, options) : GuiChoose.one(title, options); } @@ -422,7 +420,7 @@ public class PlayerControllerHuman extends PlayerController { @Override public void reveal(String string, Collection cards, ZoneType zone, Player owner) { String message = string; - if ( StringUtils.isBlank(message) ) + if ( StringUtils.isBlank(message) ) message = String.format("Looking at %s's %s", owner, zone); GuiChoose.oneOrNone(message, cards); } @@ -431,13 +429,13 @@ public class PlayerControllerHuman extends PlayerController { public ImmutablePair, List> arrangeForScry(List topN) { List toBottom = null; List toTop = null; - + if (topN.size() == 1) { if (willPutCardOnTop(topN.get(0))) toTop = topN; - else + else toBottom = topN; - } else { + } else { toBottom = GuiChoose.order("Select cards to be put on the bottom of your library", "Cards to put on the bottom", -1, topN, null, null); topN.removeAll(toBottom); if ( topN.isEmpty() ) @@ -633,7 +631,7 @@ public class PlayerControllerHuman extends PlayerController { return; } else autoPassCancel(); // probably cancel, since something has happened - + SpellAbility chosenSa = null; do { if (chosenSa != null) { @@ -709,8 +707,8 @@ public class PlayerControllerHuman extends PlayerController { } // end of not related candidates for move. - - + + /* (non-Javadoc) * @see forge.game.player.PlayerController#chooseBinary(java.lang.String, boolean) */ @@ -760,21 +758,21 @@ public class PlayerControllerHuman extends PlayerController { public Pair chooseTarget(SpellAbility saSpellskite, List> allTargets) { if( allTargets.size() < 2) return Iterables.getFirst(allTargets, null); - + final Function, String> fnToString = new Function, String>() { @Override public String apply(Pair targ) { return targ.getRight().toString() + " - " + targ.getLeft().getStackDescription(); } }; - + List> chosen = GuiChoose.getChoices(saSpellskite.getSourceCard().getName(), 1, 1, allTargets, null, fnToString); return Iterables.getFirst(chosen, null); } @Override public void notifyOfValue(SpellAbility sa, GameObject realtedTarget, String value) { - String message = formatNotificationMessage(sa, realtedTarget, value); + String message = formatNotificationMessage(sa, realtedTarget, value); GuiDialog.message(message, sa.getSourceCard().getName()); } @@ -790,8 +788,8 @@ public class PlayerControllerHuman extends PlayerController { return String.format(random ? "Randomly chosen number for %s is %s" : "%s choses number: %s", mayBeYou(target, player), value); case FlipACoin: String flipper = StringUtils.capitalize(mayBeYou(target, player)); - return sa.hasParam("NoCall") - ? String.format("%s flip comes up %s", Lang.getPossesive(flipper), value) + return sa.hasParam("NoCall") + ? String.format("%s flip comes up %s", Lang.getPossesive(flipper), value) : String.format("%s %s the flip", flipper, Lang.joinVerb(flipper, value)); case Protection: String choser = StringUtils.capitalize(mayBeYou(target, player)); @@ -800,7 +798,7 @@ public class PlayerControllerHuman extends PlayerController { return String.format("%s effect's value for %s is %s", sa.getSourceCard().getName(), mayBeYou(target, player), value); } } - + private String mayBeYou(GameObject what, Player you) { return what == you ? "you" : what.toString(); } diff --git a/forge-gui/src/main/java/forge/gui/input/InputBase.java b/forge-gui/src/main/java/forge/gui/input/InputBase.java index f3addaadea8..8a8ed7f57f2 100644 --- a/forge-gui/src/main/java/forge/gui/input/InputBase.java +++ b/forge-gui/src/main/java/forge/gui/input/InputBase.java @@ -42,14 +42,14 @@ public abstract class InputBase implements java.io.Serializable, Input { private boolean finished = false; protected final boolean isFinished() { return finished; } protected final void setFinished() { finished = true; } - + // showMessage() is always the first method called @Override public final void showMessageInitial() { finished = false; showMessage(); } - + protected abstract void showMessage(); @Override @@ -58,7 +58,6 @@ public abstract class InputBase implements java.io.Serializable, Input { @Override public void selectAbility(SpellAbility ab) { } - @Override public final void selectButtonCancel() { if( isFinished() ) return; @@ -82,11 +81,10 @@ public abstract class InputBase implements java.io.Serializable, Input { protected void onOk() {} // to remove need for CMatchUI dependence - protected final void showMessage(String message) { + protected final void showMessage(String message) { CMatchUI.SINGLETON_INSTANCE.showMessage(message); } - protected final void flashIncorrectAction() { SDisplayUtil.remind(VPrompt.SINGLETON_INSTANCE); } @@ -94,7 +92,7 @@ public abstract class InputBase implements java.io.Serializable, Input { protected String getTurnPhasePriorityMessage(Game game) { final PhaseHandler ph = game.getPhaseHandler(); final StringBuilder sb = new StringBuilder(); - + sb.append("Priority: ").append(ph.getPriorityPlayer()).append("\n"); sb.append("Turn ").append(ph.getTurn()).append(" (").append(ph.getPlayerTurn()).append(")\n"); sb.append("Phase: ").append(ph.getPhase().nameForUi).append("\n"); diff --git a/forge-gui/src/main/java/forge/gui/input/InputBlock.java b/forge-gui/src/main/java/forge/gui/input/InputBlock.java index 7359e3a09fa..7f866434e87 100644 --- a/forge-gui/src/main/java/forge/gui/input/InputBlock.java +++ b/forge-gui/src/main/java/forge/gui/input/InputBlock.java @@ -46,7 +46,7 @@ public class InputBlock extends InputSyncronizedBase { private final Combat combat; private final Player defender; private final Player declarer; - + /** * TODO: Write javadoc for Constructor. * @param priority @@ -62,24 +62,24 @@ public class InputBlock extends InputSyncronizedBase { protected final void showMessage() { // could add "Reset Blockers" button ButtonUtil.enableOnlyOk(); - - String prompt = declarer == defender ? "declare blockers." : "declare blockers for " + defender.getName(); - + + String prompt = declarer == defender ? "declare blockers." : "declare blockers for " + defender.getName(); + final StringBuilder sb = new StringBuilder(declarer.getName()); sb.append(", ").append(prompt).append("\n\n"); if (this.currentAttacker == null) { sb.append("To Block, click on your opponent's attacker first, then your blocker(s).\n"); sb.append("To cancel a block right-click on your blocker"); - showMessage(sb.toString()); - } else { + } + else { final String attackerName = this.currentAttacker.isFaceDown() ? "Morph" : this.currentAttacker.getName(); sb.append("Select a creature to block ").append(attackerName).append(" ("); sb.append(this.currentAttacker.getUniqueNumber()).append("). "); sb.append("To cancel a block right-click on your blocker"); - showMessage(sb.toString()); } + showMessage(sb.toString()); CMatchUI.SINGLETON_INSTANCE.showCombat(combat); } @@ -100,14 +100,13 @@ public class InputBlock extends InputSyncronizedBase { /** {@inheritDoc} */ @Override public final void onCardSelected(final Card card, final MouseEvent triggerEvent) { - if (triggerEvent.getButton() == 3 && card.getController() == defender) { combat.removeFromCombat(card); CMatchUI.SINGLETON_INSTANCE.fireEvent(new UiEventBlockerAssigned(card, (Card)null)); - } else { + } else { // is attacking? boolean isCorrectAction = false; - + if (combat.isAttacking(card)) { setCurrentAttacker(card); isCorrectAction = true; @@ -121,7 +120,7 @@ public class InputBlock extends InputSyncronizedBase { } } } - + if (!isCorrectAction) { flashIncorrectAction(); }