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 a10b6a4a50..e5a149c549 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form
+++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form
@@ -150,7 +150,7 @@
-
+
@@ -5904,6 +5904,7 @@
+
@@ -5916,9 +5917,10 @@
+
-
+
@@ -5976,6 +5978,11 @@
+
+
+
+
+
@@ -6117,6 +6124,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
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..a12bd1237e 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";
@@ -576,6 +577,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
labelConfirm = new javax.swing.JLabel();
controlsDescriptionLabel = new javax.swing.JLabel();
bttnResetControls = new javax.swing.JButton();
+ labelToggleRecordMacro = new javax.swing.JLabel();
+ keyToggleRecordMacro = new KeyBindButton(this, KEY_CONTROL_TOGGLE_MACRO);
saveButton = new javax.swing.JButton();
exitButton = new javax.swing.JButton();
@@ -591,7 +594,11 @@ public class PreferencesDialog extends javax.swing.JDialog {
showCardName.setToolTipText("Write the card's name on the card to make the card name more recognizable.");
showCardName.setActionCommand("");
showCardName.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
- showCardName.addActionListener(evt -> showCardNameActionPerformed(evt));
+ showCardName.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ showCardNameActionPerformed(evt);
+ }
+ });
tooltipDelayLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
tooltipDelayLabel.setText("Delay in milliseconds for showing the card tooltip text");
@@ -613,7 +620,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(main_cardLayout.createSequentialGroup()
.add(6, 6, 6)
.add(main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
- .add(tooltipDelayLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 308, Short.MAX_VALUE)
+ .add(tooltipDelayLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 308, Short.MAX_VALUE)
.add(org.jdesktop.layout.GroupLayout.LEADING, showCardName)
.add(tooltipDelay, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
@@ -633,43 +640,71 @@ public class PreferencesDialog extends javax.swing.JDialog {
nonLandPermanentsInOnePile.setSelected(true);
nonLandPermanentsInOnePile.setText("Put non-land permanents in same row as creatures");
nonLandPermanentsInOnePile.setToolTipText("If activated, all non land permanents are shown in one row.
\nFirst creatures than other permanents. If not activated, creatures are
\nshown in a separate row.");
- nonLandPermanentsInOnePile.addActionListener(evt -> nonLandPermanentsInOnePileActionPerformed(evt));
+ nonLandPermanentsInOnePile.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ nonLandPermanentsInOnePileActionPerformed(evt);
+ }
+ });
showPlayerNamesPermanently.setSelected(true);
showPlayerNamesPermanently.setText("Show player names on avatar permanently");
showPlayerNamesPermanently.setToolTipText("Instead showing the names only if you hover over the avatar with the mouse, the name is shown all the time.");
showPlayerNamesPermanently.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
- showPlayerNamesPermanently.addActionListener(evt -> showPlayerNamesPermanentlyActionPerformed(evt));
+ showPlayerNamesPermanently.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ showPlayerNamesPermanentlyActionPerformed(evt);
+ }
+ });
showAbilityPickerForced.setSelected(true);
showAbilityPickerForced.setText("Show ability picker for abilities or spells without costs");
showAbilityPickerForced.setToolTipText("This prevents you from accidently activating abilities without other costs than tapping or casting spells with 0 mana costs.");
showAbilityPickerForced.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
- showAbilityPickerForced.addActionListener(evt -> showAbilityPickerForcedActionPerformed(evt));
+ showAbilityPickerForced.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ showAbilityPickerForcedActionPerformed(evt);
+ }
+ });
cbAllowRequestToShowHandCards.setSelected(true);
cbAllowRequestToShowHandCards.setText("Allow requests from players and spectators to show your hand cards");
cbAllowRequestToShowHandCards.setToolTipText("This is the default setting used for your matches. If activated other players or spectators
\nof your match can send a request so you can allow them to see your hand cards.");
cbAllowRequestToShowHandCards.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
- cbAllowRequestToShowHandCards.addActionListener(evt -> cbAllowRequestToShowHandCardsActionPerformed(evt));
+ cbAllowRequestToShowHandCards.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbAllowRequestToShowHandCardsActionPerformed(evt);
+ }
+ });
cbShowStormCounter.setSelected(true);
cbShowStormCounter.setText("Show the number of spell casts during the current turn");
cbShowStormCounter.setToolTipText("Adds a little box left to the short keys line with the number
\nof spells already cast during the current turn (storm counter).");
cbShowStormCounter.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
- cbShowStormCounter.addActionListener(evt -> cbShowStormCounterActionPerformed(evt));
+ cbShowStormCounter.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbShowStormCounterActionPerformed(evt);
+ }
+ });
cbConfirmEmptyManaPool.setSelected(true);
cbConfirmEmptyManaPool.setText("Confirm if you want to pass a phase/step but there is still mana in your mana pool");
cbConfirmEmptyManaPool.setToolTipText("If activated you get a confirm message if you pass priority while stack is empty
\n and you still have mana in your mana pool.");
cbConfirmEmptyManaPool.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
- cbConfirmEmptyManaPool.addActionListener(evt -> cbConfirmEmptyManaPoolActionPerformed(evt));
+ cbConfirmEmptyManaPool.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbConfirmEmptyManaPoolActionPerformed(evt);
+ }
+ });
cbAskMoveToGraveOrder.setSelected(true);
cbAskMoveToGraveOrder.setText("Ask player for setting order cards go to graveyard");
cbAskMoveToGraveOrder.setToolTipText("If activated and multiple cards go to the graveyard at the same time
\nthe player is asked to set the order of the cards.");
cbAskMoveToGraveOrder.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
- cbAskMoveToGraveOrder.addActionListener(evt -> cbAskMoveToGraveOrderActionPerformed(evt));
+ cbAskMoveToGraveOrder.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbAskMoveToGraveOrderActionPerformed(evt);
+ }
+ });
org.jdesktop.layout.GroupLayout main_gameLayout = new org.jdesktop.layout.GroupLayout(main_game);
main_game.setLayout(main_gameLayout);
@@ -713,12 +748,20 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbGameLogAutoSave.setSelected(true);
cbGameLogAutoSave.setText("Auto save game logs (to \"../Mage.Client/gamelogs/\" directory)");
cbGameLogAutoSave.setToolTipText("The logs of all your games will be saved to the mentioned folder if this option is switched on.");
- cbGameLogAutoSave.addActionListener(evt -> cbGameLogAutoSaveActionPerformed(evt));
+ cbGameLogAutoSave.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbGameLogAutoSaveActionPerformed(evt);
+ }
+ });
cbDraftLogAutoSave.setSelected(true);
cbDraftLogAutoSave.setText("Auto save draft logs (to \"../Mage.Client/gamelogs/\" directory)");
cbDraftLogAutoSave.setToolTipText("The logs of all your games will be saved to the mentioned folder if this option is switched on.");
- cbDraftLogAutoSave.addActionListener(evt -> cbDraftLogAutoSaveActionPerformed(evt));
+ cbDraftLogAutoSave.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbDraftLogAutoSaveActionPerformed(evt);
+ }
+ });
org.jdesktop.layout.GroupLayout main_gamelogLayout = new org.jdesktop.layout.GroupLayout(main_gamelog);
main_gamelog.setLayout(main_gamelogLayout);
@@ -1219,47 +1262,75 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbStopAttack.setText("Stop on declare attackers step if you skip steps (F4/F5/F7) and attackers are available");
cbStopAttack.setToolTipText("If you use F4, F5 or F7 to skip steps, you stop on declare attackers step if attackers are available. If this option is not activated, you also skip the declare attackers step with this actions. F9 does always skip the declare attackers step.");
cbStopAttack.setActionCommand("");
- cbStopAttack.addActionListener(evt -> cbStopAttackActionPerformed(evt));
+ cbStopAttack.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbStopAttackActionPerformed(evt);
+ }
+ });
phases_stopSettings.add(cbStopAttack);
cbStopBlock.setText("Stop on your declare blockers step also if no blockers available");
cbStopBlock.setToolTipText("Also if you have no blockers to declare, the game stops at the declare blockers step.");
cbStopBlock.setActionCommand("");
- cbStopBlock.addActionListener(evt -> cbStopBlockActionPerformed(evt));
+ cbStopBlock.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbStopBlockActionPerformed(evt);
+ }
+ });
phases_stopSettings.add(cbStopBlock);
cbStopOnAllMain.setText("Skip with F7 to next main phase (if not activated skip always to your next main phase)");
cbStopOnAllMain.setToolTipText("If activated F7 skips to next main phases (regardless of the active players).");
cbStopOnAllMain.setActionCommand("");
- cbStopOnAllMain.addActionListener(evt -> cbStopOnAllMainActionPerformed(evt));
+ cbStopOnAllMain.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbStopOnAllMainActionPerformed(evt);
+ }
+ });
phases_stopSettings.add(cbStopOnAllMain);
cbStopOnAllEnd.setText("Skip with F5 to next end step (if not activated only to end steps of opponents)");
cbStopOnAllEnd.setToolTipText("If activated - F5 skips to the next end step (regardless of the current player)");
cbStopOnAllEnd.setActionCommand("");
cbStopOnAllEnd.setPreferredSize(new java.awt.Dimension(300, 25));
- cbStopOnAllEnd.addActionListener(evt -> cbStopOnAllEndActionPerformed(evt));
+ cbStopOnAllEnd.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbStopOnAllEndActionPerformed(evt);
+ }
+ });
phases_stopSettings.add(cbStopOnAllEnd);
cbPassPriorityCast.setText("Pass priority automatically after you have put a spell on the stack");
cbPassPriorityCast.setToolTipText("If activated the system passes priority automatically for you if you have put a spell on the stack.");
cbPassPriorityCast.setActionCommand("");
cbPassPriorityCast.setPreferredSize(new java.awt.Dimension(300, 25));
- cbPassPriorityCast.addActionListener(evt -> cbPassPriorityCastActionPerformed(evt));
+ cbPassPriorityCast.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbPassPriorityCastActionPerformed(evt);
+ }
+ });
phases_stopSettings.add(cbPassPriorityCast);
cbPassPriorityActivation.setText("Pass priority automatically after you have put an activated ability on the stack");
cbPassPriorityActivation.setToolTipText("If activated the system passes priority for you automatically after you have put an activated ability on the stack.");
cbPassPriorityActivation.setActionCommand("");
cbPassPriorityActivation.setPreferredSize(new java.awt.Dimension(300, 25));
- cbPassPriorityActivation.addActionListener(evt -> cbPassPriorityActivationActionPerformed(evt));
+ cbPassPriorityActivation.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbPassPriorityActivationActionPerformed(evt);
+ }
+ });
phases_stopSettings.add(cbPassPriorityActivation);
cbAutoOrderTrigger.setText("Set order for your triggers automatically if all have the same text");
cbAutoOrderTrigger.setToolTipText("If activated the order to put on the stack your triggers that trigger at the same time
\nis set automatically if all have the same text.");
cbAutoOrderTrigger.setActionCommand("");
cbAutoOrderTrigger.setPreferredSize(new java.awt.Dimension(300, 25));
- cbAutoOrderTrigger.addActionListener(evt -> cbAutoOrderTriggerActionPerformed(evt));
+ cbAutoOrderTrigger.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbAutoOrderTriggerActionPerformed(evt);
+ }
+ });
phases_stopSettings.add(cbAutoOrderTrigger);
org.jdesktop.layout.GroupLayout tabPhasesLayout = new org.jdesktop.layout.GroupLayout(tabPhases);
@@ -1381,18 +1452,34 @@ public class PreferencesDialog extends javax.swing.JDialog {
panelCardImages.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Card images:"));
cbUseDefaultImageFolder.setText("Use default location to save images");
- cbUseDefaultImageFolder.addActionListener(evt -> cbUseDefaultImageFolderActionPerformed(evt));
+ cbUseDefaultImageFolder.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbUseDefaultImageFolderActionPerformed(evt);
+ }
+ });
txtImageFolderPath.setToolTipText("The selected image will be used as background picture. You have to restart MAGE to view a changed background image.");
btnBrowseImageLocation.setText("Browse...");
- btnBrowseImageLocation.addActionListener(evt -> btnBrowseImageLocationActionPerformed(evt));
+ btnBrowseImageLocation.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnBrowseImageLocationActionPerformed(evt);
+ }
+ });
cbCheckForNewImages.setText("Check for new images on startup");
- cbCheckForNewImages.addActionListener(evt -> cbCheckForNewImagesActionPerformed(evt));
+ cbCheckForNewImages.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbCheckForNewImagesActionPerformed(evt);
+ }
+ });
cbSaveToZipFiles.setText("Store images in zip files");
- cbSaveToZipFiles.addActionListener(evt -> cbSaveToZipFilesActionPerformed(evt));
+ cbSaveToZipFiles.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbSaveToZipFilesActionPerformed(evt);
+ }
+ });
cbPreferedImageLanguage.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
@@ -1463,23 +1550,51 @@ public class PreferencesDialog extends javax.swing.JDialog {
panelBackgroundImages.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Background images setting:"));
cbUseDefaultBackground.setText("Use default image");
- cbUseDefaultBackground.addActionListener(evt -> cbUseDefaultBackgroundActionPerformed(evt));
+ cbUseDefaultBackground.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbUseDefaultBackgroundActionPerformed(evt);
+ }
+ });
- txtBackgroundImagePath.addActionListener(evt -> txtBackgroundImagePathActionPerformed(evt));
+ txtBackgroundImagePath.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ txtBackgroundImagePathActionPerformed(evt);
+ }
+ });
btnBrowseBackgroundImage.setText("Browse...");
- btnBrowseBackgroundImage.addActionListener(evt -> btnBrowseBackgroundImageActionPerformed(evt));
+ btnBrowseBackgroundImage.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnBrowseBackgroundImageActionPerformed(evt);
+ }
+ });
- txtBattlefieldImagePath.addActionListener(evt -> txtBattlefieldImagePathActionPerformed(evt));
+ txtBattlefieldImagePath.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ txtBattlefieldImagePathActionPerformed(evt);
+ }
+ });
btnBrowseBattlefieldImage.setText("Browse...");
- btnBrowseBattlefieldImage.addActionListener(evt -> btnBrowseBattlefieldImageActionPerformed(evt));
+ btnBrowseBattlefieldImage.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnBrowseBattlefieldImageActionPerformed(evt);
+ }
+ });
cbUseDefaultBattleImage.setText("Use default battlefield image");
- cbUseDefaultBattleImage.addActionListener(evt -> cbUseDefaultBattleImageActionPerformed(evt));
+ cbUseDefaultBattleImage.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbUseDefaultBattleImageActionPerformed(evt);
+ }
+ });
cbUseRandomBattleImage.setText("Select random battlefield image");
- cbUseRandomBattleImage.addActionListener(evt -> cbUseRandomBattleImageActionPerformed(evt));
+ cbUseRandomBattleImage.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbUseRandomBattleImageActionPerformed(evt);
+ }
+ });
jLabel14.setText("Background:");
@@ -1539,13 +1654,25 @@ public class PreferencesDialog extends javax.swing.JDialog {
jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Card rendering:"));
cbCardRenderImageFallback.setText("Fall back to plain image based rendering");
- cbCardRenderImageFallback.addActionListener(evt -> cbCardRenderImageFallbackActionPerformed(evt));
+ cbCardRenderImageFallback.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbCardRenderImageFallbackActionPerformed(evt);
+ }
+ });
cbCardRenderShowReminderText.setText("Show reminder text in rendered card textboxes");
- cbCardRenderShowReminderText.addActionListener(evt -> cbCardRenderShowReminderTextActionPerformed(evt));
+ cbCardRenderShowReminderText.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbCardRenderShowReminderTextActionPerformed(evt);
+ }
+ });
cbCardRenderHideSetSymbol.setText("Hide set symbols on cards (more space on the type line for card types)");
- cbCardRenderHideSetSymbol.addActionListener(evt -> cbCardRenderHideSetSymbolActionPerformed(evt));
+ cbCardRenderHideSetSymbol.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbCardRenderHideSetSymbolActionPerformed(evt);
+ }
+ });
org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
@@ -1600,22 +1727,38 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbEnableGameSounds.setText("Enable game sounds");
cbEnableGameSounds.setToolTipText("Sounds that will be played for certain actions (e.g. play land, attack, etc.) during the game.");
- cbEnableGameSounds.addActionListener(evt -> cbEnableGameSoundsActionPerformed(evt));
+ cbEnableGameSounds.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbEnableGameSoundsActionPerformed(evt);
+ }
+ });
sounds_clips.add(cbEnableGameSounds);
cbEnableDraftSounds.setText("Enable draft sounds");
cbEnableDraftSounds.setToolTipText("Sounds that will be played during drafting for card picking or warining if time runs out.");
- cbEnableDraftSounds.addActionListener(evt -> cbEnableDraftSoundsActionPerformed(evt));
+ cbEnableDraftSounds.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbEnableDraftSoundsActionPerformed(evt);
+ }
+ });
sounds_clips.add(cbEnableDraftSounds);
cbEnableSkipButtonsSounds.setText("Enable skip button sounds");
cbEnableSkipButtonsSounds.setToolTipText("Sounds that will be played if a priority skip action (F4/F5/F7/F9) or cancel skip action (F3) is used.");
- cbEnableSkipButtonsSounds.addActionListener(evt -> cbEnableSkipButtonsSoundsActionPerformed(evt));
+ cbEnableSkipButtonsSounds.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbEnableSkipButtonsSoundsActionPerformed(evt);
+ }
+ });
sounds_clips.add(cbEnableSkipButtonsSounds);
cbEnableOtherSounds.setText("Enable other sounds");
cbEnableOtherSounds.setToolTipText("Sounds that will be played for actions outside of games (e.g. whisper, player joins your game, player submits a deck ...).");
- cbEnableOtherSounds.addActionListener(evt -> cbEnableOtherSoundsActionPerformed(evt));
+ cbEnableOtherSounds.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbEnableOtherSoundsActionPerformed(evt);
+ }
+ });
sounds_clips.add(cbEnableOtherSounds);
sounds_backgroundMusic.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Music"));
@@ -1623,15 +1766,27 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbEnableBattlefieldBGM.setText("Play music during match");
cbEnableBattlefieldBGM.setToolTipText("During your matches music will be played from the seleced folder.");
cbEnableBattlefieldBGM.setActionCommand("Play automatically during matches");
- cbEnableBattlefieldBGM.addActionListener(evt -> cbEnableBattlefieldBGMActionPerformed(evt));
+ cbEnableBattlefieldBGM.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbEnableBattlefieldBGMActionPerformed(evt);
+ }
+ });
jLabel16.setText("Playing from folder:");
jLabel16.setToolTipText("");
- txtBattlefieldIBGMPath.addActionListener(evt -> txtBattlefieldIBGMPathActionPerformed(evt));
+ txtBattlefieldIBGMPath.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ txtBattlefieldIBGMPathActionPerformed(evt);
+ }
+ });
btnBattlefieldBGMBrowse.setText("Browse...");
- btnBattlefieldBGMBrowse.addActionListener(evt -> btnBattlefieldBGMBrowseActionPerformed(evt));
+ btnBattlefieldBGMBrowse.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnBattlefieldBGMBrowseActionPerformed(evt);
+ }
+ });
org.jdesktop.layout.GroupLayout sounds_backgroundMusicLayout = new org.jdesktop.layout.GroupLayout(sounds_backgroundMusic);
sounds_backgroundMusic.setLayout(sounds_backgroundMusicLayout);
@@ -2186,7 +2341,11 @@ public class PreferencesDialog extends javax.swing.JDialog {
lblProxyType.setText("Proxy:");
- cbProxyType.addActionListener(evt -> cbProxyTypeActionPerformed(evt));
+ cbProxyType.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbProxyTypeActionPerformed(evt);
+ }
+ });
pnlProxySettings.setBorder(javax.swing.BorderFactory.createEtchedBorder());
@@ -2204,10 +2363,18 @@ public class PreferencesDialog extends javax.swing.JDialog {
lblProxyPassword.setText("Password:");
- txtPasswordField.addActionListener(evt -> txtPasswordFieldActionPerformed(evt));
+ txtPasswordField.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ txtPasswordFieldActionPerformed(evt);
+ }
+ });
rememberPswd.setText("Remember Password");
- rememberPswd.addActionListener(evt -> rememberPswdActionPerformed(evt));
+ rememberPswd.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ rememberPswdActionPerformed(evt);
+ }
+ });
jLabel11.setFont(new java.awt.Font("Tahoma", 2, 10)); // NOI18N
jLabel11.setText("Note: password won't be encrypted!");
@@ -2354,7 +2521,15 @@ public class PreferencesDialog extends javax.swing.JDialog {
controlsDescriptionLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP);
bttnResetControls.setText("Reset to default");
- bttnResetControls.addActionListener(evt -> bttnResetControlsActionPerformed(evt));
+ bttnResetControls.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ bttnResetControlsActionPerformed(evt);
+ }
+ });
+
+ labelToggleRecordMacro.setText("Toggle Record Macro");
+
+ keyToggleRecordMacro.setText("keyBindButton1");
org.jdesktop.layout.GroupLayout tabControlsLayout = new org.jdesktop.layout.GroupLayout(tabControls);
tabControls.setLayout(tabControlsLayout);
@@ -2376,7 +2551,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(lebelSkip)
.add(labelPriorEnd)
.add(labelSkipStep)
- .add(labelConfirm))
+ .add(labelConfirm)
+ .add(labelToggleRecordMacro))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.add(tabControlsLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(keyConfirm, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
@@ -2387,9 +2563,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(keyMainStep, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(keyPriorEnd, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(keySkipStep, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
- .add(keyEndStep, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
+ .add(keyEndStep, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
+ .add(keyToggleRecordMacro, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
- .add(controlsDescriptionLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 498, Short.MAX_VALUE)))
+ .add(controlsDescriptionLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 441, Short.MAX_VALUE)))
.addContainerGap())
);
tabControlsLayout.setVerticalGroup(
@@ -2433,7 +2610,11 @@ public class PreferencesDialog extends javax.swing.JDialog {
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.add(tabControlsLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(labelPriorEnd)
- .add(keyPriorEnd, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))))
+ .add(keyPriorEnd, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
+ .add(tabControlsLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+ .add(labelToggleRecordMacro)
+ .add(keyToggleRecordMacro, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.add(bttnResetControls)
.addContainerGap())
@@ -2441,19 +2622,27 @@ 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));
saveButton.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM);
- saveButton.addActionListener(evt -> saveButtonActionPerformed(evt));
+ saveButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ saveButtonActionPerformed(evt);
+ }
+ });
- 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));
exitButton.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM);
- exitButton.addActionListener(evt -> exitButtonActionPerformed(evt));
+ exitButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ exitButtonActionPerformed(evt);
+ }
+ });
org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
@@ -2631,6 +2820,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
save(prefs, dialog.keyYourTurn);
save(prefs, dialog.keySkipStack);
save(prefs, dialog.keyPriorEnd);
+ save(prefs, dialog.keyToggleRecordMacro);
// Avatar
if (selectedAvatarId < MIN_AVATAR_ID || selectedAvatarId > MAX_AVATAR_ID) {
@@ -3183,6 +3373,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
load(prefs, dialog.keyYourTurn);
load(prefs, dialog.keySkipStack);
load(prefs, dialog.keyPriorEnd);
+ load(prefs, dialog.keyToggleRecordMacro);
}
private static void loadSelectedAvatar(Preferences prefs) {
@@ -3409,6 +3600,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
return KeyEvent.VK_F10;
case KEY_CONTROL_PRIOR_END:
return KeyEvent.VK_F11;
+ case KEY_CONTROL_TOGGLE_MACRO:
+ return KeyEvent.VK_F8;
default:
return 0;
}
@@ -3549,7 +3742,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
keyPriorEnd,
keySkipStack,
keySkipStep,
- keyYourTurn
+ keyYourTurn,
+ keyToggleRecordMacro
);
}
@@ -3660,6 +3854,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
private mage.client.components.KeyBindButton keyPriorEnd;
private mage.client.components.KeyBindButton keySkipStack;
private mage.client.components.KeyBindButton keySkipStep;
+ private mage.client.components.KeyBindButton keyToggleRecordMacro;
private mage.client.components.KeyBindButton keyYourTurn;
private javax.swing.JLabel labelCancel;
private javax.swing.JLabel labelCardSizeHand;
@@ -3680,6 +3875,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
private javax.swing.JLabel labelPriorEnd;
private javax.swing.JLabel labelSkipStep;
private javax.swing.JLabel labelStackWidth;
+ private javax.swing.JLabel labelToggleRecordMacro;
private javax.swing.JLabel labelTooltipSize;
private javax.swing.JLabel labelYourTurn;
private javax.swing.JLabel lblProxyPassword;
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..4dcfba7142 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,31 @@ 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.getToggleRecordMacroButtonImage()));
+ btnToggleMacro.setToolTipText("Toggle Record Macro ("
+ + getCachedKeyText(KEY_CONTROL_TOGGLE_MACRO) + ").");
+ btnToggleMacro.setFocusable(false);
+ btnToggleMacro.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent evt) {
+ if (evt.getButton() == MouseEvent.BUTTON1) {
+ btnToggleMacroActionPerformed(null);
+ }
+ }
+ });
+
+ KeyStroke kst = getCachedKeystroke(KEY_CONTROL_TOGGLE_MACRO);
+ 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 +1837,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 +1872,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 +2019,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 +2103,17 @@ 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);
+ if (btnToggleMacro.getBorder() instanceof EmptyBorder) {
+ btnToggleMacro.setBorder(new LineBorder(Color.orange, BORDER_SIZE));
+ } else {
+ btnToggleMacro.setBorder(new EmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE));
+ }
+ }
private void btnEndTurnActionPerformed(java.awt.event.ActionEvent evt) {
SessionHandler.sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_NEXT_TURN, gameId, null);
@@ -2360,6 +2404,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.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java
index 639cf37792..6e1db68012 100644
--- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java
+++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java
@@ -420,6 +420,9 @@ public class CallbackClientImpl implements CallbackClient {
.append("
")
.append(KeyEvent.getKeyText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CONTROL_CANCEL_SKIP, 114)))
.append(" - Undo F4/F5/F7/F9/F11")
+ .append("
")
+ .append(KeyEvent.getKeyText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CONTROL_TOGGLE_MACRO, 119)))
+ .append(" - Toggle recording a sequence of actions to repeat. Will not pause if interrupted and can fail if a selected card changes such as when scrying top card to bottom.")
.append("
").append(System.getProperty("os.name").contains("Mac OS X") ? "Cmd" : "Ctrl").append(" + click - Hold priority while casting a spell or activating an ability").toString(),
null, MessageType.USER_INFO, ChatMessage.MessageColor.BLUE);
break;
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/ImageManager.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/ImageManager.java
index 8da639e6d8..ec5c4c485a 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/ImageManager.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/ImageManager.java
@@ -43,6 +43,7 @@ public interface ImageManager {
Image getSkipStackButtonImage();
Image getSkipEndStepBeforeYourTurnButtonImage();
Image getSkipYourNextTurnButtonImage();
+ Image getToggleRecordMacroButtonImage();
Image getPhaseImage(String phase);
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java
index 9b08854b3f..ad7a161edc 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java
@@ -339,6 +339,14 @@ public enum ImageManagerImpl implements ImageManager {
}
return imageSkipYourNextTurnButton;
}
+
+ @Override
+ public Image getToggleRecordMacroButtonImage() {
+ if(imageToggleRecordMacroButton == null) {
+ imageToggleRecordMacroButton = getBufferedImageFromResource("/buttons/toggle_macro.png");
+ }
+ return imageToggleRecordMacroButton;
+ }
protected static Image getImageFromResourceTransparent(String path, Color mask, Rectangle rec) {
BufferedImage image;
@@ -433,6 +441,7 @@ public enum ImageManagerImpl implements ImageManager {
private static BufferedImage imageSkipStackButton;
private static BufferedImage imageSkipUntilEndStepBeforeYourTurnButton;
private static BufferedImage imageSkipYourNextTurnButton;
+ private static BufferedImage imageToggleRecordMacroButton;
private static Map phasesImages;
}
diff --git a/Mage.Client/src/main/resources/buttons/toggle_macro.png b/Mage.Client/src/main/resources/buttons/toggle_macro.png
new file mode 100644
index 0000000000..f615b4a43e
Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/toggle_macro.png differ
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..7e8a4b3e10 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,13 @@ 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;
+ protected boolean macroTriggeredSelectionFlag;
+ protected boolean activatingMacro = false;
public HumanPlayer(String name, RangeOfInfluence range, int skill) {
super(name, range);
@@ -116,8 +123,41 @@ public class HumanPlayer extends PlayerImpl {
this.currentlyUnpaidMana = player.currentlyUnpaidMana;
this.replacementEffectChoice = player.replacementEffectChoice;
}
+
+ protected boolean isExecutingMacro() {
+ return !recordingMacro
+ && (!actionQueue.isEmpty()
+ || (actionIterations > 0 && !actionQueueSaved.isEmpty()));
+ }
+
+ protected boolean pullResponseFromQueue(Game game) {
+ if (actionQueue.isEmpty() && actionIterations > 0 && !actionQueueSaved.isEmpty()) {
+ actionQueue = new LinkedList(actionQueueSaved);
+ actionIterations--;
+ }
+ PlayerResponse action = actionQueue.poll();
+ if (action != null) {
+ if (action.getString() != null
+ && action.getString().equals("resolveStack")) {
+ action = actionQueue.poll();
+ if (action == null) return false;
+ sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_STACK_RESOLVED, game, null);
+ }
+ synchronized (response) {
+ response.copy(action);
+ response.notifyAll();
+ macroTriggeredSelectionFlag = false;
+ return true;
+ }
+ }
+ return false;
+ }
protected void waitForResponse(Game game) {
+ if (isExecutingMacro()) {
+ pullResponseFromQueue(game);
+ return;
+ }
response.clear();
logger.debug("Waiting response from player: " + getId());
game.resumeTimer(getTurnControlledBy());
@@ -131,6 +171,10 @@ public class HumanPlayer extends PlayerImpl {
game.pauseTimer(getTurnControlledBy());
}
}
+ if (recordingMacro && !macroTriggeredSelectionFlag) {
+ logger.debug("Adding an action " + response);
+ actionQueueSaved.add(new PlayerResponse(response));
+ }
}
@Override
@@ -144,7 +188,7 @@ public class HumanPlayer extends PlayerImpl {
Map options = new HashMap<>();
options.put("UI.left.btn.text", "Mulligan");
options.put("UI.right.btn.text", "Keep");
- game.fireAskPlayerEvent(playerId, new MessageToClient(message), null, options);
+ if(!isExecutingMacro()) game.fireAskPlayerEvent(playerId, new MessageToClient(message), null, options);
waitForResponse(game);
} while (response.getBoolean() == null && !abort);
if (!abort) {
@@ -184,7 +228,7 @@ public class HumanPlayer extends PlayerImpl {
if (messageToClient.getSecondMessage() == null) {
messageToClient.setSecondMessage(getRelatedObjectName(source, game));
}
- game.fireAskPlayerEvent(playerId, messageToClient, source, options);
+ if(!isExecutingMacro()) game.fireAskPlayerEvent(playerId, messageToClient, source, options);
waitForResponse(game);
} while (response.getBoolean() == null && !abort);
if (!abort) {
@@ -243,7 +287,7 @@ public class HumanPlayer extends PlayerImpl {
replacementEffectChoice.setKeyChoices(rEffects);
while (!abort) {
- game.fireChooseChoiceEvent(playerId, replacementEffectChoice);
+ if(!isExecutingMacro()) game.fireChooseChoiceEvent(playerId, replacementEffectChoice);
updateGameStatePriority("chooseEffect", game);
waitForResponse(game);
logger.debug("Choose effect: " + response.getString());
@@ -278,7 +322,7 @@ public class HumanPlayer extends PlayerImpl {
}
updateGameStatePriority("choose(3)", game);
while (!abort) {
- game.fireChooseChoiceEvent(playerId, choice);
+ if(!isExecutingMacro()) game.fireChooseChoiceEvent(playerId, choice);
waitForResponse(game);
if (response.getString() != null) {
choice.setChoice(response.getString());
@@ -320,7 +364,7 @@ public class HumanPlayer extends PlayerImpl {
List chosen = target.getTargets();
options.put("chosen", (Serializable) chosen);
- game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(), getRelatedObjectName(sourceId, game)), targetIds, required, getOptions(target, options));
+ if(!isExecutingMacro()) game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(), getRelatedObjectName(sourceId, game)), targetIds, required, getOptions(target, options));
waitForResponse(game);
if (response.getUUID() != null) {
if (!targetIds.contains(response.getUUID())) {
@@ -385,7 +429,7 @@ public class HumanPlayer extends PlayerImpl {
required = false;
}
- game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), possibleTargets, required, getOptions(target, null));
+ if(!isExecutingMacro()) game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), possibleTargets, required, getOptions(target, null));
waitForResponse(game);
if (response.getUUID() != null) {
if (target.getTargets().contains(response.getUUID())) {
@@ -453,7 +497,7 @@ public class HumanPlayer extends PlayerImpl {
options.put("choosable", (Serializable) choosable);
}
- game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage()), cards, required, options);
+ if(!isExecutingMacro()) game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage()), cards, required, options);
waitForResponse(game);
if (response.getUUID() != null) {
if (target.canTarget(response.getUUID(), cards, game)) {
@@ -512,7 +556,7 @@ public class HumanPlayer extends PlayerImpl {
if (!choosable.isEmpty()) {
options.put("choosable", (Serializable) choosable);
}
- game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), cards, required, options);
+ if(!isExecutingMacro()) game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), cards, required, options);
waitForResponse(game);
if (response.getUUID() != null) {
if (target.getTargets().contains(response.getUUID())) { // if already included remove it
@@ -539,7 +583,7 @@ public class HumanPlayer extends PlayerImpl {
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
updateGameStatePriority("chooseTargetAmount", game);
while (!abort) {
- game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage() + "\n Amount remaining:" + target.getAmountRemaining(), getRelatedObjectName(source, game)),
+ if(!isExecutingMacro()) game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage() + "\n Amount remaining:" + target.getAmountRemaining(), getRelatedObjectName(source, game)),
target.possibleTargets(source == null ? null : source.getSourceId(), playerId, game),
target.isRequired(source),
getOptions(target, null));
@@ -677,16 +721,21 @@ public class HumanPlayer extends PlayerImpl {
while (canRespond()) {
updateGameStatePriority("priority", game);
holdingPriority = false;
- game.firePriorityEvent(playerId);
+ if(!isExecutingMacro()) game.firePriorityEvent(playerId);
waitForResponse(game);
if (game.executingRollback()) {
return true;
}
if (response.getBoolean() != null
|| response.getInteger() != null) {
- if (passWithManaPoolCheck(game)) {
+ if (passWithManaPoolCheck(game) && !activatingMacro) {
return false;
} else {
+ if(activatingMacro){
+ synchronized(actionQueue) {
+ actionQueue.notifyAll();
+ }
+ }
continue;
}
}
@@ -789,17 +838,28 @@ public class HumanPlayer extends PlayerImpl {
|| autoOrderUse) {
return abilitiesWithNoOrderSet.iterator().next();
}
+ macroTriggeredSelectionFlag = true;
updateGameStatePriority("chooseTriggeredAbility", game);
- game.fireSelectTargetTriggeredAbilityEvent(playerId, "Pick triggered ability (goes to the stack first)", abilitiesWithNoOrderSet);
+ if(!isExecutingMacro()) game.fireSelectTargetTriggeredAbilityEvent(playerId, "Pick triggered ability (goes to the stack first)", abilitiesWithNoOrderSet);
waitForResponse(game);
if (response.getUUID() != null) {
for (TriggeredAbility ability : abilitiesWithNoOrderSet) {
- if (ability.getId().equals(response.getUUID())) {
+ if (ability.getId().equals(response.getUUID())
+ || (!macroTriggeredSelectionFlag
+ && ability.getSourceId().equals(response.getUUID()))) {
+ if (recordingMacro) {
+ PlayerResponse tResponse = new PlayerResponse();
+ tResponse.setUUID(ability.getSourceId());
+ actionQueueSaved.add(tResponse);
+ logger.debug("Adding Triggered Ability Source: " + tResponse);
+ }
+ macroTriggeredSelectionFlag = false;
return ability;
}
}
}
}
+ macroTriggeredSelectionFlag = false;
return null;
}
@@ -814,7 +874,7 @@ public class HumanPlayer extends PlayerImpl {
protected boolean playManaHandling(Ability abilityToCast, ManaCost unpaid, String promptText, Game game) {
updateGameStatePriority("playMana", game);
Map options = new HashMap<>();
- game.firePlayManaEvent(playerId, "Pay " + promptText, options);
+ if(!isExecutingMacro()) game.firePlayManaEvent(playerId, "Pay " + promptText, options);
waitForResponse(game);
if (!this.canRespond()) {
return false;
@@ -837,6 +897,27 @@ public class HumanPlayer extends PlayerImpl {
}
return true;
}
+
+ /**
+ * Gets the number of times the user wants to repeat their macro
+ *
+ * @param game
+ * @return
+ */
+ public int announceRepetitions(Game game) {
+ int xValue = 0;
+ updateGameStatePriority("announceRepetitions", game);
+ do {
+ game.fireGetAmountEvent(playerId, "How many times do you want to repeat your shortcut?", 0, 999);
+ waitForResponse(game);
+ } while (response.getInteger() == null
+ && !abort);
+ if (response != null
+ && response.getInteger() != null) {
+ xValue = response.getInteger();
+ }
+ return xValue;
+ }
/**
* Gets the amount of mana the player want to spent for a x spell
@@ -853,7 +934,7 @@ public class HumanPlayer extends PlayerImpl {
int xValue = 0;
updateGameStatePriority("announceXMana", game);
do {
- game.fireGetAmountEvent(playerId, message, min, max);
+ if(!isExecutingMacro()) game.fireGetAmountEvent(playerId, message, min, max);
waitForResponse(game);
} while (response.getInteger() == null
&& !abort);
@@ -869,7 +950,7 @@ public class HumanPlayer extends PlayerImpl {
int xValue = 0;
updateGameStatePriority("announceXCost", game);
do {
- game.fireGetAmountEvent(playerId, message, min, max);
+ if(!isExecutingMacro()) game.fireGetAmountEvent(playerId, message, min, max);
waitForResponse(game);
} while (response.getInteger() == null
&& !abort);
@@ -933,7 +1014,7 @@ public class HumanPlayer extends PlayerImpl {
options.put(Constants.Option.SPECIAL_BUTTON, (Serializable) "All attack");
}
- game.fireSelectEvent(playerId, "Select attackers", options);
+ if(!isExecutingMacro()) game.fireSelectEvent(playerId, "Select attackers", options);
waitForResponse(game);
if (response.getString() != null
&& response.getString().equals("special")) { // All attack
@@ -1093,7 +1174,7 @@ public class HumanPlayer extends PlayerImpl {
return;
}
while (!abort) {
- game.fireSelectEvent(playerId, "Select blockers");
+ if(!isExecutingMacro()) game.fireSelectEvent(playerId, "Select blockers");
waitForResponse(game);
if (response.getBoolean() != null) {
return;
@@ -1123,7 +1204,7 @@ public class HumanPlayer extends PlayerImpl {
public UUID chooseAttackerOrder(List attackers, Game game) {
updateGameStatePriority("chooseAttackerOrder", game);
while (!abort) {
- game.fireSelectTargetEvent(playerId, "Pick attacker", attackers, true);
+ if(!isExecutingMacro()) game.fireSelectTargetEvent(playerId, "Pick attacker", attackers, true);
waitForResponse(game);
if (response.getUUID() != null) {
for (Permanent perm : attackers) {
@@ -1140,7 +1221,7 @@ public class HumanPlayer extends PlayerImpl {
public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) {
updateGameStatePriority("chooseBlockerOrder", game);
while (!abort) {
- game.fireSelectTargetEvent(playerId, "Pick blocker", blockers, true);
+ if(!isExecutingMacro()) game.fireSelectTargetEvent(playerId, "Pick blocker", blockers, true);
waitForResponse(game);
if (response.getUUID() != null) {
for (Permanent perm : blockers) {
@@ -1156,7 +1237,7 @@ public class HumanPlayer extends PlayerImpl {
protected void selectCombatGroup(UUID defenderId, UUID blockerId, Game game) {
updateGameStatePriority("selectCombatGroup", game);
TargetAttackingCreature target = new TargetAttackingCreature();
- game.fireSelectTargetEvent(playerId, new MessageToClient("Select attacker to block", getRelatedObjectName(blockerId, game)),
+ if(!isExecutingMacro()) game.fireSelectTargetEvent(playerId, new MessageToClient("Select attacker to block", getRelatedObjectName(blockerId, game)),
target.possibleTargets(null, playerId, game), false, getOptions(target, null));
waitForResponse(game);
if (response.getBoolean() != null) {
@@ -1206,7 +1287,7 @@ public class HumanPlayer extends PlayerImpl {
public int getAmount(int min, int max, String message, Game game) {
updateGameStatePriority("getAmount", game);
do {
- game.fireGetAmountEvent(playerId, message, min, max);
+ if(!isExecutingMacro()) game.fireGetAmountEvent(playerId, message, min, max);
waitForResponse(game);
} while (response.getInteger() == null && !abort);
if (response != null && response.getInteger() != null) {
@@ -1235,7 +1316,7 @@ public class HumanPlayer extends PlayerImpl {
LinkedHashMap specialActions = game.getState().getSpecialActions().getControlledBy(playerId, false);
if (!specialActions.isEmpty()) {
updateGameStatePriority("specialAction", game);
- game.fireGetChoiceEvent(playerId, name, null, new ArrayList<>(specialActions.values()));
+ if(!isExecutingMacro()) game.fireGetChoiceEvent(playerId, name, null, new ArrayList<>(specialActions.values()));
waitForResponse(game);
if (response.getUUID() != null) {
if (specialActions.containsKey(response.getUUID())) {
@@ -1249,7 +1330,7 @@ public class HumanPlayer extends PlayerImpl {
LinkedHashMap specialActions = game.getState().getSpecialActions().getControlledBy(playerId, true);
if (!specialActions.isEmpty()) {
updateGameStatePriority("specialAction", game);
- game.fireGetChoiceEvent(playerId, name, null, new ArrayList<>(specialActions.values()));
+ if(!isExecutingMacro()) game.fireGetChoiceEvent(playerId, name, null, new ArrayList<>(specialActions.values()));
waitForResponse(game);
if (response.getUUID() != null) {
if (specialActions.containsKey(response.getUUID())) {
@@ -1292,7 +1373,7 @@ public class HumanPlayer extends PlayerImpl {
}
}
- game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(abilities.values()));
+ if(!isExecutingMacro()) game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(abilities.values()));
waitForResponse(game);
if (response.getUUID() != null && isInGame()) {
@@ -1330,7 +1411,7 @@ public class HumanPlayer extends PlayerImpl {
return (SpellAbility) useableAbilities.values().iterator().next();
} else if (useableAbilities != null
&& !useableAbilities.isEmpty()) {
- game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(useableAbilities.values()));
+ if(!isExecutingMacro()) game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(useableAbilities.values()));
waitForResponse(game);
if (response.getUUID() != null) {
if (useableAbilities.containsKey(response.getUUID())) {
@@ -1380,7 +1461,7 @@ public class HumanPlayer extends PlayerImpl {
if (!modeMap.isEmpty()) {
boolean done = false;
while (!done) {
- game.fireGetModeEvent(playerId, "Choose Mode", modeMap);
+ if(!isExecutingMacro()) game.fireGetModeEvent(playerId, "Choose Mode", modeMap);
waitForResponse(game);
if (response.getUUID() != null) {
for (Mode mode : modes.getAvailableModes(source, game)) {
@@ -1407,7 +1488,7 @@ public class HumanPlayer extends PlayerImpl {
public boolean choosePile(Outcome outcome, String message, List extends Card> pile1, List extends Card> pile2, Game game) {
updateGameStatePriority("choosePile", game);
do {
- game.fireChoosePileEvent(playerId, message, pile1, pile2);
+ if(!isExecutingMacro()) game.fireChoosePileEvent(playerId, message, pile1, pile2);
waitForResponse(game);
} while (response.getBoolean() == null && !abort);
if (!abort) {
@@ -1523,6 +1604,36 @@ public class HumanPlayer extends PlayerImpl {
case UNHOLD_PRIORITY:
holdingPriority = false;
break;
+ case TOGGLE_RECORD_MACRO:
+ if(recordingMacro) {
+ logger.debug("Finished Recording Macro");
+ activatingMacro = true;
+ recordingMacro = false;
+ actionIterations = announceRepetitions(game);
+ try {
+ synchronized(actionQueue) {
+ actionQueue.wait();
+ }
+ } catch (InterruptedException ex){
+ } finally {
+ activatingMacro = false;
+ }
+ } else {
+ logger.debug("Starting Recording Macro");
+ resetPlayerPassedActions();
+ recordingMacro = true;
+ actionIterations = 0;
+ actionQueueSaved.clear();
+ actionQueue.clear();
+ }
+ break;
+ case PASS_PRIORITY_UNTIL_STACK_RESOLVED:
+ if (recordingMacro) {
+ logger.debug("Adding a resolveStack");
+ 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..601ccd3d6a 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,37 @@ public class PlayerResponse implements Serializable {
private Integer responseInteger;
private ManaType responseManaType;
private UUID responseManaTypePlayerId;
+
+ public PlayerResponse() {
+ clear();
+ }
+
+ public String toString() {
+ return new StringBuilder(responseString)
+ .append(',')
+ .append(responseUUID)
+ .append(',')
+ .append(responseBoolean)
+ .append(',')
+ .append(responseInteger)
+ .append(',')
+ .append(responseManaType)
+ .append(',')
+ .append(responseManaTypePlayerId).toString();
+ }
+
+ 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
}