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 e35b22b89b..afaf19627e 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form
+++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form
@@ -82,7 +82,7 @@
-
+
@@ -159,7 +159,7 @@
-
+
@@ -173,7 +173,7 @@
-
+
@@ -333,16 +333,23 @@
-
+
-
+
+
+
+
-
+
+
+
+
+
@@ -357,6 +364,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -512,7 +529,7 @@
-
+
@@ -1155,7 +1172,7 @@
-
+
@@ -1606,7 +1623,7 @@
-
+
@@ -1819,7 +1836,7 @@
-
+
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 bd764f48ce..ec880ce8f4 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java
@@ -91,6 +91,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_GAME_ASK_MOVE_TO_GRAVE_ORDER = "gameAskMoveToGraveORder";
public static final String KEY_GAME_LOG_AUTO_SAVE = "gameLogAutoSave";
+ public static final String KEY_DRAFT_LOG_AUTO_SAVE = "draftLogAutoSave";
public static final String KEY_CARD_IMAGES_USE_DEFAULT = "cardImagesUseDefault";
public static final String KEY_CARD_IMAGES_PATH = "cardImagesPath";
@@ -362,6 +363,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbAskMoveToGraveOrder = new javax.swing.JCheckBox();
main_gamelog = new javax.swing.JPanel();
cbGameLogAutoSave = new javax.swing.JCheckBox();
+ cbDraftLogAutoSave = new javax.swing.JCheckBox();
tabPhases = new javax.swing.JPanel();
jLabelHeadLine = new javax.swing.JLabel();
jLabelYourTurn = new javax.swing.JLabel();
@@ -643,18 +645,32 @@ public class PreferencesDialog extends javax.swing.JDialog {
}
});
+ 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(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cbDraftLogAutoSaveActionPerformed(evt);
+ }
+ });
+
javax.swing.GroupLayout main_gamelogLayout = new javax.swing.GroupLayout(main_gamelog);
main_gamelog.setLayout(main_gamelogLayout);
main_gamelogLayout.setHorizontalGroup(
main_gamelogLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(main_gamelogLayout.createSequentialGroup()
.addContainerGap()
- .addComponent(cbGameLogAutoSave, javax.swing.GroupLayout.PREFERRED_SIZE, 528, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(main_gamelogLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(cbGameLogAutoSave, javax.swing.GroupLayout.PREFERRED_SIZE, 528, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(cbDraftLogAutoSave, javax.swing.GroupLayout.PREFERRED_SIZE, 528, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
main_gamelogLayout.setVerticalGroup(
main_gamelogLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(cbGameLogAutoSave, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(main_gamelogLayout.createSequentialGroup()
+ .addComponent(cbGameLogAutoSave)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(cbDraftLogAutoSave))
);
javax.swing.GroupLayout tabMainLayout = new javax.swing.GroupLayout(tabMain);
@@ -675,7 +691,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.addContainerGap()
.addComponent(main_card, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(main_game, javax.swing.GroupLayout.PREFERRED_SIZE, 189, Short.MAX_VALUE)
+ .addComponent(main_game, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(main_gamelog, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
@@ -861,7 +877,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.addComponent(checkBoxEndTurnOthers))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(phases_stopSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addContainerGap(13, Short.MAX_VALUE))
+ .addContainerGap(45, Short.MAX_VALUE))
);
tabsPanel.addTab("Phases", tabPhases);
@@ -1435,7 +1451,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
);
tabAvatarsLayout.setVerticalGroup(
tabAvatarsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(avatarPane, javax.swing.GroupLayout.DEFAULT_SIZE, 359, Short.MAX_VALUE)
+ .addComponent(avatarPane, javax.swing.GroupLayout.DEFAULT_SIZE, 432, Short.MAX_VALUE)
);
tabsPanel.addTab("Avatars", tabAvatars);
@@ -1585,7 +1601,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.addGroup(connection_serversLayout.createSequentialGroup()
.addGroup(connection_serversLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(lblURLServerList, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addComponent(txtURLServerList, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addComponent(txtURLServerList, javax.swing.GroupLayout.DEFAULT_SIZE, 28, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel17))
);
@@ -1617,7 +1633,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.addComponent(cbProxyType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(18, 18, 18)
.addComponent(pnlProxySettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addContainerGap(54, Short.MAX_VALUE))
+ .addContainerGap(90, Short.MAX_VALUE))
);
pnlProxySettings.getAccessibleContext().setAccessibleDescription("");
@@ -1681,6 +1697,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
save(prefs, dialog.cbConfirmEmptyManaPool, KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.cbAskMoveToGraveOrder, KEY_GAME_ASK_MOVE_TO_GRAVE_ORDER, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.cbGameLogAutoSave, KEY_GAME_LOG_AUTO_SAVE, "true", "false", UPDATE_CACHE_POLICY);
+ save(prefs, dialog.cbDraftLogAutoSave, KEY_DRAFT_LOG_AUTO_SAVE, "true", "false", UPDATE_CACHE_POLICY);
// Phases
save(prefs, dialog.checkBoxUpkeepYou, UPKEEP_YOU);
@@ -2001,6 +2018,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
// TODO add your handling code here:
}//GEN-LAST:event_cbAskMoveToGraveOrderActionPerformed
+ private void cbDraftLogAutoSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbDraftLogAutoSaveActionPerformed
+ // TODO add your handling code here:
+ }//GEN-LAST:event_cbDraftLogAutoSaveActionPerformed
+
private void showProxySettings() {
if (cbProxyType.getSelectedItem() == Connection.ProxyType.SOCKS) {
this.pnlProxy.setVisible(true);
@@ -2079,6 +2100,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
load(prefs, dialog.cbAskMoveToGraveOrder, KEY_GAME_ASK_MOVE_TO_GRAVE_ORDER, "true");
load(prefs, dialog.cbGameLogAutoSave, KEY_GAME_LOG_AUTO_SAVE, "true");
+ load(prefs, dialog.cbDraftLogAutoSave, KEY_DRAFT_LOG_AUTO_SAVE, "true");
load(prefs, dialog.checkBoxUpkeepYou, UPKEEP_YOU, "on", "on");
load(prefs, dialog.checkBoxDrawYou, DRAW_YOU, "on", "on");
@@ -2449,6 +2471,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
private javax.swing.JCheckBox cbAskMoveToGraveOrder;
private javax.swing.JCheckBox cbCheckForNewImages;
private javax.swing.JCheckBox cbConfirmEmptyManaPool;
+ private javax.swing.JCheckBox cbDraftLogAutoSave;
private javax.swing.JCheckBox cbEnableBattlefieldBGM;
private javax.swing.JCheckBox cbEnableDraftSounds;
private javax.swing.JCheckBox cbEnableGameSounds;
diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java
index da4ea2b425..99645ecaed 100644
--- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java
+++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java
@@ -41,10 +41,21 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
@@ -53,9 +64,12 @@ import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.Timer;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
import mage.client.MageFrame;
import mage.client.components.tray.MageTray;
import mage.client.deckeditor.SortSettingDraft;
+import mage.client.dialog.PreferencesDialog;
import mage.client.plugins.impl.Plugins;
import mage.client.util.CardsViewUtil;
import mage.client.util.Event;
@@ -94,6 +108,19 @@ public class DraftPanel extends javax.swing.JPanel {
// id of card with popup menu
protected UUID cardIdPopupMenu;
+ // Filename for the draft log (only updated if writing the log).
+ private String logFilename;
+
+ // Number of the current booster (for draft log writing).
+ private int packNo;
+
+ // Number of the current card pick (for draft log writing).
+ private int pickNo;
+
+ // Cached booster data to be written into the log (see logLastPick).
+ private String currentBoosterHeader;
+ private String[] currentBooster;
+
private static final CardsView emptyView = new CardsView();
/** Creates new form DraftPanel */
@@ -149,6 +176,20 @@ public class DraftPanel extends javax.swing.JPanel {
if (!session.joinDraft(draftId)) {
hideDraft();
}
+
+ if (isLogging()) {
+ // If we are logging the draft create a file that will contain
+ // the log.
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
+ logFilename = "Draft_" + sdf.format(new Date()) + "_" + draftId + ".txt";
+ try {
+ Files.write(pathToDraftLog(), "".getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+ } catch (IOException ex) {
+ Logger.getLogger(DraftPanel.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ } else {
+ logFilename = null;
+ }
}
public void updateDraft(DraftView draftView) {
@@ -167,6 +208,9 @@ public class DraftPanel extends javax.swing.JPanel {
this.chkPack3.setSelected(draftView.getBoosterNum() > 2);
this.txtCardNo.setText(Integer.toString(draftView.getCardNum()));
+ packNo = draftView.getBoosterNum();
+ pickNo = draftView.getCardNum();
+
int right = draftView.getPlayers().size() / 2;
int left = draftView.getPlayers().size() - right;
int height = left * 18;
@@ -247,6 +291,7 @@ public class DraftPanel extends javax.swing.JPanel {
}
public void loadBooster(DraftPickView draftPickView) {
+ logLastPick(draftPickView);
// upper area that shows the picks
loadCardsToPickedCardsArea(draftPickView.getPicks());
@@ -413,6 +458,94 @@ public class DraftPanel extends javax.swing.JPanel {
draftPicks.loadCards(CardsViewUtil.convertSimple(pickedCardsShown), bigCard, null);
}
+ // Log the last card picked into the draft log together with booster
+ // contents.
+ // We don't get any event when the card is selected due to timeout
+ // that's why instead of proactively logging our pick we instead
+ // log *last* pick from the list of picks.
+ // To make this possible we cache the list of cards from the
+ // previous booster and it's sequence number (pack number / pick number)
+ // in fields currentBooster and currentBoosterHeader.
+ private void logLastPick(DraftPickView pickView) {
+ if (!isLogging()) {
+ return;
+ }
+ if (currentBooster != null) {
+ String lastPick = getCardName(getLastPick(pickView.getPicks().values()));
+ if (lastPick != null) {
+ logPick(lastPick);
+ }
+ currentBooster = null;
+ }
+ setCurrentBoosterForLog(pickView.getBooster());
+ if (currentBooster.length == 1) {
+ logPick(currentBooster[0]);
+ }
+ }
+
+ private static boolean isLogging() {
+ String autoSave = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_DRAFT_LOG_AUTO_SAVE, "true");
+ return autoSave.equals("true");
+ }
+
+ private void setCurrentBoosterForLog(SimpleCardsView booster) {
+ LinkedList cards = new LinkedList<>();
+ for (SimpleCardView simple: booster.values()) {
+ String cardName = getCardName(simple);
+ if (cardName != null) {
+ cards.add(cardName);
+ }
+ }
+
+ currentBoosterHeader = "Pack " + packNo + " pick " + pickNo + ":\n";
+ currentBooster = cards.toArray(new String[cards.size()]);
+ }
+
+ private void logPick(String pick) {
+ StringBuilder b = new StringBuilder();
+ b.append(currentBoosterHeader);
+ for (String name : currentBooster) {
+ b.append(pick.equals(name) ? "--> " : " ");
+ b.append(name);
+ b.append('\n');
+ }
+ b.append('\n');
+ appendToDraftLog(b.toString());
+ }
+
+ private Path pathToDraftLog() {
+ File saveDir = new File("gamelogs");
+ if(!saveDir.exists()) {
+ saveDir.mkdirs();
+ }
+ return new File(saveDir, logFilename).toPath();
+ }
+
+ private void appendToDraftLog(String data) {
+ try {
+ Files.write(pathToDraftLog(), data.getBytes(), StandardOpenOption.APPEND);
+ } catch (IOException ex) {
+ Logger.getLogger(DraftPanel.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ private static SimpleCardView getLastPick(Collection picks) {
+ SimpleCardView last = null;
+ for (SimpleCardView pick : picks) {
+ last = pick;
+ }
+ return last;
+ }
+
+ private static String getCardName(SimpleCardView card) {
+ if (card == null) {
+ return null;
+ }
+ CardInfo cardInfo = CardRepository.instance.findCard(card.getExpansionSetCode(), card.getCardNumber());
+ return cardInfo != null ? cardInfo.getName() : null;
+ }
+
+
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is