Don't prompt to declare blockers if no creatures can block

This commit is contained in:
drdev
2013-11-30 22:01:04 +00:00
parent 2ffc18cc89
commit 9ca9dc797e
6 changed files with 87 additions and 72 deletions

View File

@@ -35,21 +35,18 @@ import forge.gui.input.InputSynchronized;
* @version $Id$
*/
public class InputQueue extends Observable {
private final BlockingDeque<InputSynchronized> inputStack = new LinkedBlockingDeque<InputSynchronized>();
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

View File

@@ -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<Card> defendersArmy = defending.getCreaturesInPlay();
final List<Card> attackers = combat.getAttackers();
final List<Card> 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?
/**
* <p>
* canAttack.
* </p>
*
* @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<Card> creatures = p.getCreaturesInPlay();
if (creatures.isEmpty()) { return false; }
List<Card> 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?
/**
* <p>
@@ -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();

View File

@@ -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

View File

@@ -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<PaperCard> 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<Card, Integer> assignCombatDamage(Card attacker, List<Card> 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<Card, Integer> map;
if (defender != null && assignDamageAsIfNotBlocked(attacker)) {
map = new HashMap<Card, Integer>();
@@ -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<Card> choosePermanentsToSacrifice(SpellAbility sa, int min, int max, List<Card> 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<Card> choosePermanentsToDestroy(SpellAbility sa, int min, int max, List<Card> 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<Card> choosePermanentsTo(int min, int max, List<Card> valid, String outerMessage) {
@@ -322,17 +320,17 @@ public class PlayerControllerHuman extends PlayerController {
@Override
public List<Card> chooseCardsForEffect(List<Card> 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<Card> 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<Card> 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<Card>, List<Card>> arrangeForScry(List<Card> topN) {
List<Card> toBottom = null;
List<Card> 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<SpellAbilityStackInstance, GameObject> chooseTarget(SpellAbility saSpellskite, List<Pair<SpellAbilityStackInstance, GameObject>> allTargets) {
if( allTargets.size() < 2)
return Iterables.getFirst(allTargets, null);
final Function<Pair<SpellAbilityStackInstance, GameObject>, String> fnToString = new Function<Pair<SpellAbilityStackInstance, GameObject>, String>() {
@Override
public String apply(Pair<SpellAbilityStackInstance, GameObject> targ) {
return targ.getRight().toString() + " - " + targ.getLeft().getStackDescription();
}
};
List<Pair<SpellAbilityStackInstance, GameObject>> 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();
}

View File

@@ -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");

View File

@@ -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();
}