From ff640a942e83c2f86747f8d896fb51a36883609e Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 29 Mar 2019 18:10:00 +0400 Subject: [PATCH] UI: improved skips and stops for declare blocker step: * added options to STOP skip on any or zero blockers available; * added auto-stop before declare blockers step (e.g. user can cast instants and crew abilities); --- .../mage/client/dialog/PreferencesDialog.form | 21 +++++-- .../mage/client/dialog/PreferencesDialog.java | 59 +++++++++++------ .../src/mage/player/human/HumanPlayer.java | 63 +++++++++++++++---- .../players/net/UserSkipPrioritySteps.java | 27 +++++--- 4 files changed, 126 insertions(+), 44 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form index 8da4c30a02..32d661081b 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -4071,7 +4071,7 @@ - + @@ -4170,7 +4170,7 @@ - + @@ -4184,14 +4184,25 @@ - + - + + - + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 2cc6ef3f9d..57a6fef9b0 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -121,7 +121,8 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String END_OF_TURN_OTHERS = "endOfTurnOthers"; public static final String KEY_STOP_ATTACK = "stopDeclareAttacksStep"; - public static final String KEY_STOP_BLOCK = "stopDeclareBlockersStep"; + public static final String KEY_STOP_BLOCK_WITH_ANY = "stopDeclareBlockersStepWithAny"; + public static final String KEY_STOP_BLOCK_WITH_ZERO = "stopDeclareBlockersStepWithZero"; public static final String KEY_STOP_ALL_MAIN_PHASES = "stopOnAllMainPhases"; public static final String KEY_STOP_ALL_END_PHASES = "stopOnAllEndPhases"; public static final String KEY_STOP_NEW_STACK_OBJECTS = "stopOnNewStackObjects"; @@ -454,7 +455,8 @@ public class PreferencesDialog extends javax.swing.JDialog { checkBoxEndTurnOthers = new javax.swing.JCheckBox(); phases_stopSettings = new javax.swing.JPanel(); cbStopAttack = new javax.swing.JCheckBox(); - cbStopBlock = new javax.swing.JCheckBox(); + cbStopBlockWithAny = new javax.swing.JCheckBox(); + cbStopBlockWithZero = new javax.swing.JCheckBox(); cbStopOnNewStackObjects = new javax.swing.JCheckBox(); cbStopOnAllMain = new javax.swing.JCheckBox(); cbStopOnAllEnd = new javax.swing.JCheckBox(); @@ -655,7 +657,7 @@ public class PreferencesDialog extends javax.swing.JDialog { showFullImagePath.setSelected(true); showFullImagePath.setToolTipText("Show the path Xmage is expecting for this card's image (only displays if missing)"); showFullImagePath.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - showFullImagePath.setText("Display image path for missing images"); + showFullImagePath.setLabel("Display image path for missing images"); showFullImagePath.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { showFullImagePathActionPerformed(evt); @@ -1321,7 +1323,7 @@ public class PreferencesDialog extends javax.swing.JDialog { jLabelEndOfTurn.setText("End of turn:"); phases_stopSettings.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "SKIP settings")); - phases_stopSettings.setLayout(new java.awt.GridLayout(8, 1)); + phases_stopSettings.setLayout(new java.awt.GridLayout(9, 1)); cbStopAttack.setSelected(true); cbStopAttack.setText("STOP skips on declare attackers if attackers are available"); @@ -1334,15 +1336,26 @@ public class PreferencesDialog extends javax.swing.JDialog { }); phases_stopSettings.add(cbStopAttack); - cbStopBlock.setText("STOP skips on declare blockers if blockers are available"); - cbStopBlock.setToolTipText(""); - cbStopBlock.setActionCommand(""); - cbStopBlock.addActionListener(new java.awt.event.ActionListener() { + cbStopBlockWithAny.setSelected(true); + cbStopBlockWithAny.setText("STOP skips on declare blockers if ANY blockers are available"); + cbStopBlockWithAny.setToolTipText(""); + cbStopBlockWithAny.setActionCommand(""); + cbStopBlockWithAny.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - cbStopBlockActionPerformed(evt); + cbStopBlockWithAnyActionPerformed(evt); } }); - phases_stopSettings.add(cbStopBlock); + phases_stopSettings.add(cbStopBlockWithAny); + + cbStopBlockWithZero.setText("STOP skips on declare blockers if ZERO blockers are available"); + cbStopBlockWithZero.setToolTipText(""); + cbStopBlockWithZero.setActionCommand(""); + cbStopBlockWithZero.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cbStopBlockWithZeroActionPerformed(evt); + } + }); + phases_stopSettings.add(cbStopBlockWithZero); cbStopOnNewStackObjects.setText("Skip to STACK resolved (F10): stop on new objects added (on) or stop until empty (off)"); cbStopOnNewStackObjects.setToolTipText(""); @@ -1520,7 +1533,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .add(checkBoxEndTurnOthers)) .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED) .add(phases_stopSettings, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addContainerGap(185, Short.MAX_VALUE)) + .addContainerGap(160, Short.MAX_VALUE)) ); tabsPanel.addTab("Phases & Priority", tabPhases); @@ -2700,7 +2713,7 @@ public class PreferencesDialog extends javax.swing.JDialog { tabsPanel.addTab("Controls", tabControls); - saveButton.setText("Save"); + saveButton.setLabel("Save"); saveButton.setMaximumSize(new java.awt.Dimension(100, 30)); saveButton.setMinimumSize(new java.awt.Dimension(100, 30)); saveButton.setPreferredSize(new java.awt.Dimension(100, 30)); @@ -2711,7 +2724,7 @@ public class PreferencesDialog extends javax.swing.JDialog { } }); - exitButton.setText("Exit"); + exitButton.setLabel("Exit"); exitButton.setMaximumSize(new java.awt.Dimension(100, 30)); exitButton.setMinimumSize(new java.awt.Dimension(100, 30)); exitButton.setPreferredSize(new java.awt.Dimension(100, 30)); @@ -2857,7 +2870,8 @@ public class PreferencesDialog extends javax.swing.JDialog { save(prefs, dialog.checkBoxEndTurnOthers, END_OF_TURN_OTHERS); save(prefs, dialog.cbStopAttack, KEY_STOP_ATTACK, "true", "false", UPDATE_CACHE_POLICY); - save(prefs, dialog.cbStopBlock, KEY_STOP_BLOCK, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.cbStopBlockWithAny, KEY_STOP_BLOCK_WITH_ANY, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.cbStopBlockWithZero, KEY_STOP_BLOCK_WITH_ZERO, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbStopOnAllMain, KEY_STOP_ALL_MAIN_PHASES, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbStopOnAllEnd, KEY_STOP_ALL_END_PHASES, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbStopOnNewStackObjects, KEY_STOP_NEW_STACK_OBJECTS, "true", "false", UPDATE_CACHE_POLICY); @@ -3113,9 +3127,9 @@ public class PreferencesDialog extends javax.swing.JDialog { // TODO add your handling code here: }//GEN-LAST:event_cbStopAttackActionPerformed - private void cbStopBlockActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbStopBlockActionPerformed + private void cbStopBlockWithAnyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbStopBlockWithAnyActionPerformed // TODO add your handling code here: - }//GEN-LAST:event_cbStopBlockActionPerformed + }//GEN-LAST:event_cbStopBlockWithAnyActionPerformed private void cbStopOnAllMainActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbStopOnAllMainActionPerformed // TODO add your handling code here: @@ -3232,6 +3246,10 @@ public class PreferencesDialog extends javax.swing.JDialog { // TODO add your handling code here: }//GEN-LAST:event_cbStopOnNewStackObjectsActionPerformed + private void cbStopBlockWithZeroActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbStopBlockWithZeroActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_cbStopBlockWithZeroActionPerformed + private void showProxySettings() { Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem(); switch (proxyType) { @@ -3376,7 +3394,8 @@ public class PreferencesDialog extends javax.swing.JDialog { load(prefs, dialog.checkBoxEndTurnOthers, END_OF_TURN_OTHERS, "on", "on"); load(prefs, dialog.cbStopAttack, KEY_STOP_ATTACK, "true", "true"); - load(prefs, dialog.cbStopBlock, KEY_STOP_BLOCK, "true", "true"); + load(prefs, dialog.cbStopBlockWithAny, KEY_STOP_BLOCK_WITH_ANY, "true", "true"); + load(prefs, dialog.cbStopBlockWithZero, KEY_STOP_BLOCK_WITH_ZERO, "true", "false"); load(prefs, dialog.cbStopOnAllMain, KEY_STOP_ALL_MAIN_PHASES, "true", "false"); load(prefs, dialog.cbStopOnAllEnd, KEY_STOP_ALL_END_PHASES, "true", "false"); load(prefs, dialog.cbStopOnNewStackObjects, KEY_STOP_NEW_STACK_OBJECTS, "true", "false"); @@ -3544,7 +3563,8 @@ public class PreferencesDialog extends javax.swing.JDialog { userSkipPrioritySteps.getOpponentTurn().setEndOfTurn(dialog.checkBoxEndTurnOthers.isSelected()); userSkipPrioritySteps.setStopOnDeclareAttackersDuringSkipActions(dialog.cbStopAttack.isSelected()); - userSkipPrioritySteps.setStopOnDeclareBlockerIfNoneAvailable(dialog.cbStopBlock.isSelected()); + userSkipPrioritySteps.setStopOnDeclareBlockersWithAnyPermanents(dialog.cbStopBlockWithAny.isSelected()); + userSkipPrioritySteps.setStopOnDeclareBlockersWithZeroPermanents(dialog.cbStopBlockWithZero.isSelected()); userSkipPrioritySteps.setStopOnAllEndPhases(dialog.cbStopOnAllEnd.isSelected()); userSkipPrioritySteps.setStopOnAllMainPhases(dialog.cbStopOnAllMain.isSelected()); userSkipPrioritySteps.setStopOnStackNewObjects(dialog.cbStopOnNewStackObjects.isSelected()); @@ -3914,7 +3934,8 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JCheckBox cbSaveToZipFiles; private javax.swing.JCheckBox cbShowStormCounter; private javax.swing.JCheckBox cbStopAttack; - private javax.swing.JCheckBox cbStopBlock; + private javax.swing.JCheckBox cbStopBlockWithAny; + private javax.swing.JCheckBox cbStopBlockWithZero; private javax.swing.JCheckBox cbStopOnAllEnd; private javax.swing.JCheckBox cbStopOnAllMain; private javax.swing.JCheckBox cbStopOnNewStackObjects; diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index f55aea830f..aa0f659962 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -755,8 +755,27 @@ public class HumanPlayer extends PlayerImpl { } } - // SKIP buttons - use the skip actions only if the player itself controls its turn + // STOP conditions (temporary stop without skip reset) + boolean quickStop = false; if (isGameUnderControl()) { + + // if was attacked - always stop BEFORE blocker step (to cast extra spells) + if (game.getTurn().getStepType() == PhaseStep.DECLARE_ATTACKERS + && game.getCombat().getPlayerDefenders(game).contains(playerId)) { + + FilterCreatureForCombatBlock filter = filterCreatureForCombatBlock.copy(); + filter.add(new ControllerIdPredicate(playerId)); + // stop skip on any/zero permanents available + int possibleBlockersCount = game.getBattlefield().count(filter, null, playerId, game); + boolean canStopOnAny = possibleBlockersCount != 0 && getControllingPlayersUserData(game).getUserSkipPrioritySteps().isStopOnDeclareBlockersWithAnyPermanents(); + boolean canStopOnZero = possibleBlockersCount == 0 && getControllingPlayersUserData(game).getUserSkipPrioritySteps().isStopOnDeclareBlockersWithZeroPermanents(); + quickStop = canStopOnAny || canStopOnZero; + } + } + + // SKIP - use the skip actions only if the player itself controls its turn + if (!quickStop && isGameUnderControl()) { + if (passedAllTurns || passedTurnSkipStack) { if (passWithManaPoolCheck(game)) { return false; @@ -1185,11 +1204,23 @@ public class HumanPlayer extends PlayerImpl { filter.add(new ControllerIdPredicate(attackingPlayerId)); while (!abort) { + + List possibleAttackers = new ArrayList<>(); + for (Permanent possibleAttacker : game.getBattlefield().getActivePermanents(filter, attackingPlayerId, game)) { + if (possibleAttacker.canAttack(null, game)) { + possibleAttackers.add(possibleAttacker.getId()); + } + } + + // skip declare attack step + // old version: + // - passedAllTurns, passedUntilEndStepBeforeMyTurn: always skipped + // - other: on disabled option skipped if (passedAllTurns || passedUntilEndStepBeforeMyTurn || (!getControllingPlayersUserData(game) .getUserSkipPrioritySteps() - .isStopOnDeclareAttackersDuringSkipAction() + .isStopOnDeclareAttackers() && (passedTurn || passedTurnSkipStack || passedUntilEndOfTurn @@ -1198,14 +1229,21 @@ public class HumanPlayer extends PlayerImpl { return; } } - Map options = new HashMap<>(); - List possibleAttackers = new ArrayList<>(); - for (Permanent possibleAttacker : game.getBattlefield().getActivePermanents(filter, attackingPlayerId, game)) { - if (possibleAttacker.canAttack(null, game)) { - possibleAttackers.add(possibleAttacker.getId()); + /* + // new version: + // - all: on disabled option skipped (if attackers selected) + if (!getControllingPlayersUserData(game) + .getUserSkipPrioritySteps() + .isStopOnDeclareAttackers() + && (possibleAttackers.size() > 0)) { + if (checkIfAttackersValid(game)) { + return; } } + */ + + Map options = new HashMap<>(); options.put(Constants.Option.POSSIBLE_ATTACKERS, (Serializable) possibleAttackers); if (!possibleAttackers.isEmpty()) { options.put(Constants.Option.SPECIAL_BUTTON, "All attack"); @@ -1424,12 +1462,15 @@ public class HumanPlayer extends PlayerImpl { updateGameStatePriority("selectBlockers", game); FilterCreatureForCombatBlock filter = filterCreatureForCombatBlock.copy(); filter.add(new ControllerIdPredicate(defendingPlayerId)); - if (game.getBattlefield().count(filter, null, playerId, game) == 0 - && !getControllingPlayersUserData(game) - .getUserSkipPrioritySteps() - .isStopOnDeclareBlockerIfNoneAvailable()) { + + // stop skip on any/zero permanents available + int possibleBlockersCount = game.getBattlefield().count(filter, null, playerId, game); + boolean canStopOnAny = possibleBlockersCount != 0 && getControllingPlayersUserData(game).getUserSkipPrioritySteps().isStopOnDeclareBlockersWithAnyPermanents(); + boolean canStopOnZero = possibleBlockersCount == 0 && getControllingPlayersUserData(game).getUserSkipPrioritySteps().isStopOnDeclareBlockersWithZeroPermanents(); + if (!canStopOnAny && !canStopOnZero) { return; } + while (!abort) { prepareForResponse(game); if (!isExecutingMacro()) { diff --git a/Mage/src/main/java/mage/players/net/UserSkipPrioritySteps.java b/Mage/src/main/java/mage/players/net/UserSkipPrioritySteps.java index 9c5a2fbd3d..dd077671d8 100644 --- a/Mage/src/main/java/mage/players/net/UserSkipPrioritySteps.java +++ b/Mage/src/main/java/mage/players/net/UserSkipPrioritySteps.java @@ -10,8 +10,9 @@ public class UserSkipPrioritySteps implements Serializable { final SkipPrioritySteps yourTurn; final SkipPrioritySteps opponentTurn; - boolean stopOnDeclareAttackersDuringSkipAction = true; - boolean stopOnDeclareBlockerIfNoneAvailable = true; + boolean stopOnDeclareAttackers = true; + boolean stopOnDeclareBlockersWithZeroPermanents = false; + boolean stopOnDeclareBlockersWithAnyPermanents = true; boolean stopOnAllMainPhases = true; boolean stopOnAllEndPhases = true; boolean stopOnStackNewObjects = true; @@ -29,20 +30,28 @@ public class UserSkipPrioritySteps implements Serializable { return opponentTurn; } - public boolean isStopOnDeclareBlockerIfNoneAvailable() { - return stopOnDeclareBlockerIfNoneAvailable; + public boolean isStopOnDeclareBlockersWithZeroPermanents() { + return stopOnDeclareBlockersWithZeroPermanents; } - public void setStopOnDeclareBlockerIfNoneAvailable(boolean stopOnDeclareBlockerIfNoneAvailable) { - this.stopOnDeclareBlockerIfNoneAvailable = stopOnDeclareBlockerIfNoneAvailable; + public void setStopOnDeclareBlockersWithZeroPermanents(boolean stopOnDeclareBlockersWithZeroPermanents) { + this.stopOnDeclareBlockersWithZeroPermanents = stopOnDeclareBlockersWithZeroPermanents; } - public boolean isStopOnDeclareAttackersDuringSkipAction() { - return stopOnDeclareAttackersDuringSkipAction; + public boolean isStopOnDeclareAttackers() { + return stopOnDeclareAttackers; } public void setStopOnDeclareAttackersDuringSkipActions(boolean stopOnDeclareAttackersDuringSkipActions) { - this.stopOnDeclareAttackersDuringSkipAction = stopOnDeclareAttackersDuringSkipActions; + this.stopOnDeclareAttackers = stopOnDeclareAttackersDuringSkipActions; + } + + public boolean isStopOnDeclareBlockersWithAnyPermanents() { + return stopOnDeclareBlockersWithAnyPermanents; + } + + public void setStopOnDeclareBlockersWithAnyPermanents(boolean stopOnDeclareBlockersWithAnyPermanents) { + this.stopOnDeclareBlockersWithAnyPermanents = stopOnDeclareBlockersWithAnyPermanents; } public boolean isStopOnAllMainPhases() {