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 61b83ebd8e..407d135223 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -299,6 +299,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_CONNECTION_URL_SERVER_LIST = "connectionURLServerList"; // controls + public static final String KEY_CONTROL_TOGGLE_MACRO = "controlToggleMacro"; public static final String KEY_CONTROL_CONFIRM = "controlConfirm"; public static final String KEY_CONTROL_CANCEL_SKIP = "controlCancelSkip"; public static final String KEY_CONTROL_NEXT_TURN = "controlNextTurn"; diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 3c6d2f3bfe..0897a0b4c2 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -489,6 +489,7 @@ public final class GamePanel extends javax.swing.JPanel { this.btnStopWatching.setVisible(false); this.btnSwitchHands.setVisible(false); this.btnCancelSkip.setVisible(true); + this.btnToggleMacro.setVisible(true); this.btnSkipToNextTurn.setVisible(true); this.btnSkipToEndTurn.setVisible(true); @@ -522,6 +523,7 @@ public final class GamePanel extends javax.swing.JPanel { this.btnSwitchHands.setVisible(false); this.chosenHandKey = ""; this.btnCancelSkip.setVisible(false); + this.btnToggleMacro.setVisible(false); this.btnSkipToNextTurn.setVisible(false); this.btnSkipToEndTurn.setVisible(false); @@ -1346,6 +1348,7 @@ public final class GamePanel extends javax.swing.JPanel { txtHoldPriority.setToolTipText("Holding priority after the next spell cast or ability activation"); txtHoldPriority.setVisible(false); + btnToggleMacro = new KeyboundButton(KEY_CONTROL_TOGGLE_MACRO); btnCancelSkip = new KeyboundButton(KEY_CONTROL_CANCEL_SKIP); // F3 btnSkipToNextTurn = new KeyboundButton(KEY_CONTROL_NEXT_TURN); // F4 btnSkipToEndTurn = new KeyboundButton(KEY_CONTROL_END_STEP); // F5 @@ -1436,6 +1439,30 @@ public final class GamePanel extends javax.swing.JPanel { bigCard.setBorder(new LineBorder(Color.black, 1, true)); int c = JComponent.WHEN_IN_FOCUSED_WINDOW; + + + btnToggleMacro.setContentAreaFilled(false); + btnToggleMacro.setBorder(new EmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE)); + btnToggleMacro.setIcon(new ImageIcon(ImageManagerImpl.instance.getSkipNextTurnButtonImage())); + btnToggleMacro.setToolTipText("Toggle Record Macro (F8)."); + btnToggleMacro.setFocusable(false); + btnToggleMacro.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent evt) { + if (evt.getButton() == MouseEvent.BUTTON1) { + btnToggleMacroActionPerformed(null); + } + } + }); + + KeyStroke kst = KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0); + this.getInputMap(c).put(kst, "F8_PRESS"); + this.getActionMap().put("F8_PRESS", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + btnToggleMacroActionPerformed(actionEvent); + } + }); KeyStroke ks3 = getCachedKeystroke(KEY_CONTROL_CANCEL_SKIP); this.getInputMap(c).put(ks3, "F3_PRESS"); @@ -1809,6 +1836,7 @@ public final class GamePanel extends javax.swing.JPanel { .addComponent(btnSkipToEndStepBeforeYourTurn) ) .addGroup(gl_pnlShortCuts.createSequentialGroup() + .addComponent(btnToggleMacro) .addComponent(txtHoldPriority) .addComponent(txtSpellsCast) .addComponent(btnSwitchHands) @@ -1843,6 +1871,7 @@ public final class GamePanel extends javax.swing.JPanel { .addComponent(btnSkipToEndStepBeforeYourTurn) ) .addGroup(gl_pnlShortCuts.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(btnToggleMacro) .addComponent(txtHoldPriority) .addComponent(txtSpellsCast) .addComponent(btnSwitchHands) @@ -1989,6 +2018,9 @@ public final class GamePanel extends javax.swing.JPanel { for (MouseListener ml : this.getMouseListeners()) { this.removeMouseListener(ml); } + for (MouseListener ml : this.btnToggleMacro.getMouseListeners()) { + this.btnToggleMacro.removeMouseListener(ml); + } for (MouseListener ml : this.btnCancelSkip.getMouseListeners()) { this.btnCancelSkip.removeMouseListener(ml); } @@ -2070,6 +2102,12 @@ public final class GamePanel extends javax.swing.JPanel { message.setGameId(gameId); MageFrame.getInstance().showUserRequestDialog(message); } + + private void btnToggleMacroActionPerformed(java.awt.event.ActionEvent evt) { + SessionHandler.sendPlayerAction(PlayerAction.TOGGLE_RECORD_MACRO, gameId, null); + AudioManager.playOnSkipButton(); + updateSkipButtons(false, false, false, false, false, false); + } private void btnEndTurnActionPerformed(java.awt.event.ActionEvent evt) { SessionHandler.sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_NEXT_TURN, gameId, null); @@ -2360,6 +2398,7 @@ public final class GamePanel extends javax.swing.JPanel { private mage.client.cards.BigCard bigCard; // private JPanel cancelSkipPanel; + private KeyboundButton btnToggleMacro; private KeyboundButton btnCancelSkip; private KeyboundButton btnSkipToNextTurn; // F4 private KeyboundButton btnSkipToEndTurn; // F5 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 d3196c9384..d83b6ac2e6 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 @@ -102,6 +102,12 @@ public class HumanPlayer extends PlayerImpl { protected Map requestAutoAnswerText = new HashMap<>(); protected boolean holdingPriority; + + protected Queue actionQueue = new LinkedList<>(); + protected Queue actionQueueSaved = new LinkedList<>(); + protected int actionIterations = 0; + protected boolean recordingMacro = false; + public HumanPlayer(String name, RangeOfInfluence range, int skill) { super(name, range); @@ -116,8 +122,32 @@ public class HumanPlayer extends PlayerImpl { this.currentlyUnpaidMana = player.currentlyUnpaidMana; this.replacementEffectChoice = player.replacementEffectChoice; } + + protected boolean pullResponseFromQueue(Game game) { + if (actionQueue.isEmpty() && actionIterations > 0 && !actionQueueSaved.isEmpty()) { + actionQueue = new LinkedList(actionQueueSaved); + actionIterations--; + } + PlayerResponse action = actionQueue.poll(); + if (action != null) { + logger.info("Popping an action " + action); + synchronized (response) { + if (action.getString() != null && action.getString().equals("resolveStack")) { + logger.info("Skipping stack"); + sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_STACK_RESOLVED, game, null); + response.clear(); + } else { + response.copy(action); + } + response.notifyAll(); + return true; + } + } + return false; + } protected void waitForResponse(Game game) { + if(pullResponseFromQueue(game)) return; response.clear(); logger.debug("Waiting response from player: " + getId()); game.resumeTimer(getTurnControlledBy()); @@ -131,6 +161,10 @@ public class HumanPlayer extends PlayerImpl { game.pauseTimer(getTurnControlledBy()); } } + if (recordingMacro) { + logger.info("Adding an action " + response); + actionQueueSaved.add(new PlayerResponse(response)); + } } @Override @@ -679,6 +713,7 @@ public class HumanPlayer extends PlayerImpl { holdingPriority = false; game.firePriorityEvent(playerId); waitForResponse(game); + logger.info("Human Priority got response: " + response); if (game.executingRollback()) { return true; } @@ -1523,6 +1558,28 @@ public class HumanPlayer extends PlayerImpl { case UNHOLD_PRIORITY: holdingPriority = false; break; + case TOGGLE_RECORD_MACRO: + if(recordingMacro) { + logger.info("Finished Recording Macro"); + recordingMacro = false; + // TODO: Figure out how to make dialog + actionIterations = 3; + pullResponseFromQueue(game); + } else { + logger.info("Starting Recording Macro"); + resetPlayerPassedActions(); + recordingMacro = true; + actionIterations = 0; + actionQueueSaved.clear(); + actionQueue.clear(); + } + break; + case PASS_PRIORITY_UNTIL_STACK_RESOLVED: + if (recordingMacro) { + PlayerResponse tResponse = new PlayerResponse(); + tResponse.setString("resolveStack"); + actionQueueSaved.add(tResponse); + } default: super.sendPlayerAction(playerAction, game, data); } diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/PlayerResponse.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/PlayerResponse.java index d8769b7284..5d82f84374 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/PlayerResponse.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/PlayerResponse.java @@ -44,6 +44,34 @@ public class PlayerResponse implements Serializable { private Integer responseInteger; private ManaType responseManaType; private UUID responseManaTypePlayerId; + + public PlayerResponse() { + clear(); + } + + public String toString() { + String res = new String(); + res += responseString + ","; + res += responseUUID + ","; + res += responseBoolean + ","; + res += responseInteger + ","; + res += responseManaType + ","; + res += responseManaTypePlayerId; + return res; + } + + public PlayerResponse(PlayerResponse other) { + copy(other); + } + + public void copy(PlayerResponse other) { + responseString = other.responseString; + responseUUID = other.responseUUID; + responseBoolean = other.responseBoolean; + responseInteger = other.responseInteger; + responseManaType = other.responseManaType; + responseManaTypePlayerId = other.responseManaTypePlayerId; + } public void clear() { responseString = null; diff --git a/Mage/src/main/java/mage/constants/PlayerAction.java b/Mage/src/main/java/mage/constants/PlayerAction.java index cdee7edd3a..fa0bf2c05b 100644 --- a/Mage/src/main/java/mage/constants/PlayerAction.java +++ b/Mage/src/main/java/mage/constants/PlayerAction.java @@ -84,5 +84,6 @@ public enum PlayerAction { CLIENT_REPLAY_ACTION, HOLD_PRIORITY, UNHOLD_PRIORITY, - VIEW_LIMITED_DECK + VIEW_LIMITED_DECK, + TOGGLE_RECORD_MACRO }