diff --git a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java index 50b06f8383..ea00608dda 100644 --- a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java @@ -43,7 +43,6 @@ import mage.client.util.gui.ArrowBuilder; import mage.remote.Session; import org.apache.log4j.Logger; -import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.io.Serializable; @@ -117,6 +116,7 @@ public class FeedbackPanel extends javax.swing.JPanel { this.btnRight.setVisible(true); this.btnRight.setText("Cancel"); this.helper.setState("", false, "Cancel", true); + this.helper.setUndoEnabled(false); break; case SELECT: this.btnLeft.setVisible(false); @@ -149,7 +149,7 @@ public class FeedbackPanel extends javax.swing.JPanel { this.revalidate(); this.repaint(); - this.helper.setLinks(btnLeft, btnRight, btnSpecial); + this.helper.setLinks(btnLeft, btnRight, btnSpecial, btnUndo); this.helper.setVisible(true); } @@ -205,7 +205,6 @@ public class FeedbackPanel extends javax.swing.JPanel { } public void clear() { -// stopModal(); this.btnLeft.setVisible(false); this.btnRight.setVisible(false); this.btnSpecial.setVisible(false); @@ -213,138 +212,14 @@ public class FeedbackPanel extends javax.swing.JPanel { logger.debug("feedback - clear"); } -// public void clear0() { -// stopModal(); -// } - -// private synchronized void startModal() { -// -// try { -// if (SwingUtilities.isEventDispatchThread()) { -// EventQueue theQueue = getToolkit().getSystemEventQueue(); -// while (!selected) { -// AWTEvent event = theQueue.getNextEvent(); -// Object source = event.getSource(); -// boolean dispatch = true; -// -// if (event instanceof MouseEvent) { -// MouseEvent e = (MouseEvent) event; -// MouseEvent m = SwingUtilities.convertMouseEvent((Component) e.getSource(), e, this); -// if (!this.contains(m.getPoint()) && e.getID() != MouseEvent.MOUSE_DRAGGED) { -// dispatch = false; -// } -// } -// -// if (dispatch) { -// if (event instanceof ActiveEvent) { -// ((ActiveEvent) event).dispatch(); -// } else if (source instanceof Component) { -// ((Component) source).dispatchEvent(event); -// } else if (source instanceof MenuComponent) { -// ((MenuComponent) source).dispatchEvent(event); -// } else { -// logger.warn("Unable to dispatch: " + event); -// } -// } -// } -// } else { -// while (!selected) { -// wait(); -// } -// } -// } catch (InterruptedException ignored) { -// } -// -// } -// -// private synchronized void stopModal() { -// notifyAll(); -// } - - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - btnRight = new javax.swing.JButton(); - btnLeft = new javax.swing.JButton(); - jScrollPane1 = new javax.swing.JScrollPane(); - //lblMessage = new javax.swing.JTextArea(); - lblMessage = new MageTextArea(); - btnSpecial = new javax.swing.JButton(); - - setBackground(new java.awt.Color(255,255,255,200)); - - btnRight.setText("Cancel"); - btnRight.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnRightActionPerformed(evt); - } - }); - - btnLeft.setText("OK"); - btnLeft.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnLeftActionPerformed(evt); - } - }); - - jScrollPane1.setBorder(javax.swing.BorderFactory.createEtchedBorder()); - jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - - //lblMessage.setBackground(new java.awt.Color(204, 204, 204)); - /*lblMessage.setColumns(20); - lblMessage.setEditable(false); - lblMessage.setLineWrap(true); - lblMessage.setRows(2); - lblMessage.setWrapStyleWord(true);*/ - - lblMessage.setBorder(null); - jScrollPane1.setViewportView(lblMessage); - jScrollPane1.setBorder(null); - - btnSpecial.setText("Special"); - btnSpecial.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnSpecialActionPerformed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addComponent(btnSpecial) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 31, Short.MAX_VALUE) - .addComponent(btnLeft) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnRight)) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 224, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 81, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(btnRight) - .addComponent(btnLeft) - .addComponent(btnSpecial))) - ); - }// //GEN-END:initComponents - public void customInitComponents() { btnRight = new javax.swing.JButton(); btnLeft = new javax.swing.JButton(); jScrollPane1 = new javax.swing.JScrollPane(); lblMessage = new MageTextArea(); btnSpecial = new javax.swing.JButton(); + btnUndo = new javax.swing.JButton(); + btnUndo.setVisible(true); setBackground(new java.awt.Color(0,0,0,80)); @@ -379,26 +254,18 @@ public class FeedbackPanel extends javax.swing.JPanel { } }); - JLabel jlabel = new JLabel(); - jlabel.setLayout(new BorderLayout()); - jlabel.add(jScrollPane1, BorderLayout.CENTER); + btnUndo.setText("Undo"); + btnUndo.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnUndoActionPerformed(evt); + } + }); - setLayout(new BorderLayout()); - - JLabel jlabel2 = new JLabel(); - jlabel2.setLayout(new FlowLayout()); - jlabel2.add(btnSpecial); - jlabel2.add(btnLeft); - jlabel2.add(btnRight); - jlabel2.setPreferredSize(new Dimension(0, 35)); - - add(jlabel, BorderLayout.CENTER); - add(jlabel2, BorderLayout.PAGE_END); } private void btnRightActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRightActionPerformed this.selected = true; -// clear0(); if (connectedDialog != null) { connectedDialog.hideDialog(); } @@ -414,7 +281,6 @@ public class FeedbackPanel extends javax.swing.JPanel { private void btnLeftActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLeftActionPerformed this.selected = true; -// clear0(); session.sendPlayerBoolean(gameId, true); AudioManager.playButtonCancel(); }//GEN-LAST:event_btnLeftActionPerformed @@ -423,6 +289,10 @@ public class FeedbackPanel extends javax.swing.JPanel { session.sendPlayerString(gameId, "special"); }//GEN-LAST:event_btnSpecialActionPerformed + private void btnUndoActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSpecialActionPerformed + session.undo(gameId); + } + public void setHelperPanel(HelperPanel helper) { this.helper = helper; } @@ -443,9 +313,18 @@ public class FeedbackPanel extends javax.swing.JPanel { } } + public void allowUndo(int bookmark) { + this.helper.setUndoEnabled(true); + } + + public void disableUndo() { + this.helper.setUndoEnabled(false); + } + private javax.swing.JButton btnLeft; private javax.swing.JButton btnRight; private javax.swing.JButton btnSpecial; + private javax.swing.JButton btnUndo; private javax.swing.JScrollPane jScrollPane1; //private javax.swing.JTextArea lblMessage; private MageTextArea lblMessage; 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 09c32a6cbb..a8df0075e0 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -490,6 +490,14 @@ public final class GamePanel extends javax.swing.JPanel { } else { CombatManager.getInstance().hideCombat(gameId); } + + System.out.println("Size: " + game.getStatesSavedSize()); + if (game.getStatesSavedSize() > 0) { + feedbackPanel.allowUndo(game.getStatesSavedSize()); + } else { + feedbackPanel.disableUndo(); + } + this.revalidate(); this.repaint(); } diff --git a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java index 9ec04c0020..8aa258a234 100644 --- a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java @@ -29,10 +29,11 @@ package mage.client.game; -import java.awt.*; -import javax.swing.*; import mage.client.components.MageTextArea; +import javax.swing.*; +import java.awt.*; + /** * Panel with buttons that copy the state of feedback panel. * @@ -43,6 +44,7 @@ public class HelperPanel extends JPanel { private javax.swing.JButton btnLeft; private javax.swing.JButton btnRight; private javax.swing.JButton btnSpecial; + private javax.swing.JButton btnUndo; //private javax.swing.JButton btnEndTurn; //private javax.swing.JButton btnStopTimer; @@ -52,6 +54,7 @@ public class HelperPanel extends JPanel { private javax.swing.JButton linkLeft; private javax.swing.JButton linkRight; private javax.swing.JButton linkSpecial; + private javax.swing.JButton linkUndo; public HelperPanel() { initComponents(); @@ -89,6 +92,9 @@ public class HelperPanel extends JPanel { btnRight = new JButton("Cancel"); btnRight.setVisible(false); container.add(btnRight); + btnUndo = new JButton("Undo"); + btnUndo.setVisible(false); + container.add(btnUndo); btnLeft.addActionListener(new java.awt.event.ActionListener() { @Override @@ -122,6 +128,15 @@ public class HelperPanel extends JPanel { }} } }); + + btnUndo.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + if (linkUndo != null) {{ + linkUndo.doClick(); + }} + } + }); } public void setState(String txtLeft, boolean leftVisible, String txtRight, boolean rightVisible) { @@ -140,6 +155,10 @@ public class HelperPanel extends JPanel { this.btnSpecial.setText(txtSpecial); } + public void setUndoEnabled(boolean enabled) { + this.btnUndo.setVisible(enabled); + } + public void setRight(String txtRight, boolean rightVisible) { this.btnRight.setVisible(rightVisible); if (!txtRight.isEmpty()) { @@ -147,10 +166,11 @@ public class HelperPanel extends JPanel { } } - public void setLinks(JButton left, JButton right, JButton special) { + public void setLinks(JButton left, JButton right, JButton special, JButton undo) { this.linkLeft = left; this.linkRight = right; this.linkSpecial = special; + this.linkUndo = undo; } public void setMessage(String message) { diff --git a/Mage.Common/src/mage/interfaces/MageServer.java b/Mage.Common/src/mage/interfaces/MageServer.java index b9806744be..3944a38167 100644 --- a/Mage.Common/src/mage/interfaces/MageServer.java +++ b/Mage.Common/src/mage/interfaces/MageServer.java @@ -100,6 +100,7 @@ public interface MageServer { void sendPlayerBoolean(UUID gameId, String sessionId, Boolean data) throws MageException; void sendPlayerInteger(UUID gameId, String sessionId, Integer data) throws MageException; void concedeGame(UUID gameId, String sessionId) throws MageException; + void undo(UUID gameId, String sessionId) throws MageException; GameView getGameView(UUID gameId, String sessionId, UUID playerId) throws MageException; //priority methods diff --git a/Mage.Common/src/mage/remote/SessionImpl.java b/Mage.Common/src/mage/remote/SessionImpl.java index 11cfce49e1..1af8d1faf5 100644 --- a/Mage.Common/src/mage/remote/SessionImpl.java +++ b/Mage.Common/src/mage/remote/SessionImpl.java @@ -929,6 +929,21 @@ public class SessionImpl implements Session { return false; } + @Override + public boolean undo(UUID gameId) { + try { + if (isConnected()) { + server.undo(gameId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + @Override public boolean passPriorityUntilNextYourTurn(UUID gameId) { try { diff --git a/Mage.Common/src/mage/remote/interfaces/GamePlay.java b/Mage.Common/src/mage/remote/interfaces/GamePlay.java index 0773c95283..3387d792e3 100644 --- a/Mage.Common/src/mage/remote/interfaces/GamePlay.java +++ b/Mage.Common/src/mage/remote/interfaces/GamePlay.java @@ -59,6 +59,8 @@ public interface GamePlay { DraftPickView sendCardPick(UUID draftId, UUID cardId); + boolean undo(UUID gameId); + /*** Separate methods for priority handling ***/ /** * magenoxx: diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index fb6438775e..a3b641dce8 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -74,10 +74,17 @@ public class GameView implements Serializable { private String priorityPlayerName = ""; private int turn; private boolean special = false; + private int statesSavedSize; public GameView(GameState state, Game game) { for (Player player: state.getPlayers().values()) { players.add(new PlayerView(player, state, game)); + if (player.getStoredBookmark() > 0) { + if (this.statesSavedSize > 0) { + throw new IllegalStateException("This shouldn't happen"); + } + this.statesSavedSize = player.getStoredBookmark(); + } } for (StackObject stackObject: state.getStack()) { if (stackObject instanceof StackAbility) { @@ -254,7 +261,7 @@ public class GameView implements Serializable { return special; } - /*public List getStackOrder() { - return stackOrder; - }*/ + public int getStatesSavedSize() { + return statesSavedSize; + } } diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java index 5fb8c89a1d..25be5b66c1 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java @@ -27,9 +27,6 @@ */ package mage.player.ai; -import java.io.File; -import java.util.*; -import java.util.concurrent.*; import mage.Constants.Outcome; import mage.Constants.PhaseStep; import mage.Constants.RangeOfInfluence; @@ -62,6 +59,10 @@ import mage.target.Target; import mage.target.TargetCard; import mage.target.Targets; +import java.io.File; +import java.util.*; +import java.util.concurrent.*; + /** * @@ -132,7 +133,7 @@ public class ComputerPlayer6 extends ComputerPlayer implements switch (game.getTurn().getStepType()) { case UPKEEP: case DRAW: - pass(); + pass(game); return false; case PRECOMBAT_MAIN: case POSTCOMBAT_MAIN: @@ -146,36 +147,36 @@ public class ComputerPlayer6 extends ComputerPlayer implements act(game); return true; } else { - pass(); + pass(game); } return false; case BEGIN_COMBAT: case FIRST_COMBAT_DAMAGE: case COMBAT_DAMAGE: case END_COMBAT: - pass(); + pass(game); return false; case DECLARE_ATTACKERS: if (game.getActivePlayerId().equals(playerId)) { //declareAttackers(game, playerId); - pass(); + pass(game); } else { - pass(); + pass(game); } return false; case DECLARE_BLOCKERS: if (!game.getActivePlayerId().equals(playerId)) { declareBlockers(game, playerId); - pass(); + pass(game); } else { - pass(); + pass(game); } return false; case END_TURN: - pass(); + pass(game); return false; case CLEANUP: - pass(); + pass(game); return false; } return false; @@ -222,7 +223,7 @@ public class ComputerPlayer6 extends ComputerPlayer implements protected void act(Game game) { if (actions == null || actions.size() == 0) { - pass(); + pass(game); } else { boolean usedStack = false; while (actions.peek() != null) { @@ -257,7 +258,7 @@ public class ComputerPlayer6 extends ComputerPlayer implements } } if (usedStack) { - pass(); + pass(game); } } } @@ -536,7 +537,7 @@ public class ComputerPlayer6 extends ComputerPlayer implements } if (!sim.isGameOver() && action.isUsesStack()) { // only pass if the last action uses the stack - sim.getPlayer(currentPlayer.getId()).pass(); + sim.getPlayer(currentPlayer.getId()).pass(game); sim.getPlayerList().getNext(); } SimulationNode2 newNode = new SimulationNode2(node, sim, action, depth, currentPlayer.getId()); diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java index 7b4c4eb8ac..aaf726cdc0 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java @@ -28,7 +28,6 @@ package mage.player.ai; -import java.util.*; import mage.Constants; import mage.Constants.RangeOfInfluence; import mage.abilities.Ability; @@ -40,6 +39,10 @@ import mage.game.turn.*; import mage.players.Player; import org.apache.log4j.Logger; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; + /** * @@ -77,7 +80,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 implements Player { switch (game.getTurn().getStepType()) { case UPKEEP: case DRAW: - pass(); + pass(game); return false; case PRECOMBAT_MAIN: if (game.getActivePlayerId().equals(playerId)) { @@ -89,11 +92,11 @@ public class ComputerPlayer7 extends ComputerPlayer6 implements Player { return true; } else { - pass(); + pass(game); } return false; case BEGIN_COMBAT: - pass(); + pass(game); return false; case DECLARE_ATTACKERS: if (!game.getActivePlayerId().equals(playerId)) { @@ -105,14 +108,14 @@ public class ComputerPlayer7 extends ComputerPlayer6 implements Player { return true; } else { - pass(); + pass(game); } return false; case DECLARE_BLOCKERS: case FIRST_COMBAT_DAMAGE: case COMBAT_DAMAGE: case END_COMBAT: - pass(); + pass(game); return false; case POSTCOMBAT_MAIN: // if (game.getActivePlayerId().equals(playerId)) { @@ -124,13 +127,13 @@ public class ComputerPlayer7 extends ComputerPlayer6 implements Player { return true; // } // else { -// pass(); +// pass(game); // } // return false; case END_TURN: case CLEANUP: actionCache.clear(); - pass(); + pass(game); return false; } return false; diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index bae61e1c33..59754c112b 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -721,7 +721,7 @@ public class ComputerPlayer> extends PlayerImpl i break; } } - pass(); + pass(game); return true; } diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java index c9bbf7aa25..e79cae270e 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java @@ -106,7 +106,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements switch (game.getTurn().getStepType()) { case UPKEEP: case DRAW: - pass(); + pass(game); return false; case PRECOMBAT_MAIN: case BEGIN_COMBAT: @@ -123,7 +123,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements return true; case END_TURN: case CLEANUP: - pass(); + pass(game); return false; } return false; @@ -131,7 +131,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements protected void act(Game game) { if (actions == null || actions.size() == 0) - pass(); + pass(game); else { boolean usedStack = false; while (actions.peek() != null) { @@ -143,7 +143,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements usedStack = true; } if (usedStack) - pass(); + pass(game); } logger.info("Turn " + game.getTurnNum() + " Step " + game.getStep().toString() + " Player " + name + " Life " + life); } @@ -405,7 +405,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements } if (!sim.isGameOver() && action.isUsesStack()) { // only pass if the last action uses the stack - sim.getPlayer(currentPlayer.getId()).pass(); + sim.getPlayer(currentPlayer.getId()).pass(game); sim.getPlayerList().getNext(); } SimulationNode newNode = new SimulationNode(node, sim, action, currentPlayer.getId()); diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java index c151a8d4f9..1aa4d27b6f 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java @@ -74,7 +74,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player { switch (game.getTurn().getStepType()) { case UPKEEP: case DRAW: - pass(); + pass(game); return false; case PRECOMBAT_MAIN: if (game.getActivePlayerId().equals(playerId)) { @@ -85,10 +85,10 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player { return true; } else - pass(); + pass(game); return false; case BEGIN_COMBAT: - pass(); + pass(game); return false; case DECLARE_ATTACKERS: if (!game.getActivePlayerId().equals(playerId)) { @@ -99,13 +99,13 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player { return true; } else - pass(); + pass(game); return false; case DECLARE_BLOCKERS: case FIRST_COMBAT_DAMAGE: case COMBAT_DAMAGE: case END_COMBAT: - pass(); + pass(game); return false; case POSTCOMBAT_MAIN: if (game.getActivePlayerId().equals(playerId)) { @@ -116,11 +116,11 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player { return true; } else - pass(); + pass(game); return false; case END_TURN: case CLEANUP: - pass(); + pass(game); return false; } return false; 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 d968ec52ad..785efbe361 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 @@ -391,24 +391,24 @@ public class HumanPlayer extends PlayerImpl { passed = false; if (!abort) { if (passedAllTurns) { - pass(); + pass(game); return false; } if (passedTurn && game.getStack().isEmpty()) { - pass(); + pass(game); return false; } updateGameStatePriority("priority", game); game.firePriorityEvent(playerId); waitForResponse(); if (response.getBoolean() != null) { - pass(); + pass(game); return false; } else if (response.getInteger() != null) { /*if (response.getInteger() == -9999) { passedAllTurns = true; }*/ - pass(); + pass(game); //passedTurn = true; return false; } else if (response.getString() != null && response.getString().equals("special")) { diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index 039984c00b..2b28dc211f 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -570,6 +570,17 @@ public class MageServerImpl implements MageServer { }); } + @Override + public void undo(final UUID gameId, final String sessionId) throws MageException { + execute("undo", sessionId, new Action() { + @Override + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + GameManager.getInstance().undo(gameId, userId); + } + }); + } + @Override public void passPriorityUntilNextYourTurn(final UUID gameId, final String sessionId) throws MageException { execute("passPriorityUntilNextYourTurn", sessionId, new Action() { diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index bd15915f65..9304e8ab02 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -255,6 +255,10 @@ public class GameController implements GameCallback { game.concede(getPlayerId(userId)); } + public void undo(UUID userId) { + game.undo(getPlayerId(userId)); + } + public void passPriorityUntilNextYourTurn(UUID userId) { game.passPriorityUntilNextYourTurn(getPlayerId(userId)); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameManager.java b/Mage.Server/src/main/java/mage/server/game/GameManager.java index 69e208ce0d..8389a73508 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameManager.java +++ b/Mage.Server/src/main/java/mage/server/game/GameManager.java @@ -96,6 +96,11 @@ public class GameManager { gameControllers.get(gameId).concede(userId); } + public void undo(UUID gameId, UUID userId) { + if (gameControllers.containsKey(gameId)) + gameControllers.get(gameId).undo(userId); + } + public void passPriorityUntilNextYourTurn(UUID gameId, UUID userId) { if (gameControllers.containsKey(gameId)) gameControllers.get(gameId).passPriorityUntilNextYourTurn(userId); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 0f8b6315d7..b10c8d683b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -45,6 +45,7 @@ import mage.player.ai.ComputerPlayer; import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanentAmount; import org.junit.Ignore; import java.io.Serializable; @@ -52,7 +53,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; -import mage.target.common.TargetCreaturePermanentAmount; /** * @@ -124,7 +124,7 @@ public class TestPlayer extends ComputerPlayer { } } } - pass(); + pass(game); return false; } diff --git a/Mage/src/mage/abilities/effects/common/PassEffect.java b/Mage/src/mage/abilities/effects/common/PassEffect.java index 4b4b671d91..54fad6a24f 100644 --- a/Mage/src/mage/abilities/effects/common/PassEffect.java +++ b/Mage/src/mage/abilities/effects/common/PassEffect.java @@ -51,7 +51,7 @@ public class PassEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - player.pass(); + player.pass(game); return true; } diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 917267ef75..1b711253db 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -164,6 +164,7 @@ public interface Game extends MageItem, Serializable { void mulligan(UUID playerId); void quit(UUID playerId); void concede(UUID playerId); + void undo(UUID playerId); void emptyManaPools(); void addEffect(ContinuousEffect continuousEffect, Ability source); void addEmblem(Emblem emblem, Ability source); @@ -200,6 +201,7 @@ public interface Game extends MageItem, Serializable { int bookmarkState(); void restoreState(int bookmark); void removeBookmark(int bookmark); + int getSavedStateSize(); // game options void setGameOptions(GameOptions options); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index cdd829ba25..75c220052a 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -420,7 +420,7 @@ public abstract class GameImpl> implements Game, Serializa savedStates.push(gameStates.getSize() - 1); return savedStates.size(); } - return 0; + return savedStates.size(); } @Override @@ -444,10 +444,31 @@ public abstract class GameImpl> implements Game, Serializa while (savedStates.size() > bookmark) { savedStates.pop(); } + gameStates.remove(bookmark); } } } + private void clearAllBookmarks() { + if (!simulation) { + while (!savedStates.isEmpty()) { + savedStates.pop(); + } + gameStates.remove(0); + for (Player player : getPlayers().values()) { + player.setStoredBookmark(-1); + } + } + } + + @Override + public int getSavedStateSize() { + if (!simulation) { + return savedStates.size(); + } + return 0; + } + @Override public void start(UUID choosingPlayerId) { start(choosingPlayerId, this.gameOptions != null ? gameOptions : GameOptions.getDefault()); @@ -721,6 +742,19 @@ public abstract class GameImpl> implements Game, Serializa } } + @Override + public synchronized void undo(UUID playerId) { + Player player = state.getPlayer(playerId); + if (player != null) { + int bookmark = player.getStoredBookmark(); + if (bookmark != -1) { + restoreState(bookmark); + player.setStoredBookmark(-1); + fireUpdatePlayersEvent(); + } + } + } + @Override public synchronized void passPriorityUntilNextYourTurn(UUID playerId) { Player player = state.getPlayer(playerId); @@ -748,6 +782,7 @@ public abstract class GameImpl> implements Game, Serializa @Override public void playPriority(UUID activePlayerId, boolean resuming) { int bookmark = 0; + clearAllBookmarks(); try { while (!isPaused() && !isGameOver()) { if (!resuming) { diff --git a/Mage/src/mage/game/GameStates.java b/Mage/src/mage/game/GameStates.java index 0fda26e4fc..8c6e8bd049 100644 --- a/Mage/src/mage/game/GameStates.java +++ b/Mage/src/mage/game/GameStates.java @@ -28,10 +28,11 @@ package mage.game; +import org.apache.log4j.Logger; + import java.io.Serializable; import java.util.LinkedList; import java.util.List; -import org.apache.log4j.Logger; /** * @@ -66,6 +67,15 @@ public class GameStates implements Serializable { return null; } + public int remove(int index) { + if (states.size() > 0 && index < states.size()) { + while (states.size() > index && states.size() > 0) { + states.remove(states.size() - 1); + } + } + return states.size(); + } + public GameState get(int index) { if (index < states.size()) // return new Copier().uncompressCopy(states.get(index)); diff --git a/Mage/src/mage/game/turn/Turn.java b/Mage/src/mage/game/turn/Turn.java index 67c498d883..55d39d083a 100644 --- a/Mage/src/mage/game/turn/Turn.java +++ b/Mage/src/mage/game/turn/Turn.java @@ -28,17 +28,18 @@ package mage.game.turn; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.UUID; import mage.Constants.PhaseStep; import mage.Constants.TurnPhase; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + /** * @@ -161,7 +162,7 @@ public class Turn implements Serializable { if (phase.resumePlay(game, stepType, wasPaused)) { //20091005 - 500.4/703.4n game.emptyManaPools(); - game.saveState(); + //game.saveState(); //20091005 - 500.8 playExtraPhases(game, phase.getType()); } @@ -175,7 +176,7 @@ public class Turn implements Serializable { if (phase.play(game, activePlayerId)) { //20091005 - 500.4/703.4n game.emptyManaPools(); - game.saveState(); + //game.saveState(); //20091005 - 500.8 playExtraPhases(game, phase.getType()); } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 5081dd723f..4a05188217 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -94,7 +94,7 @@ public interface Player extends MageItem, Copyable { void setMaxHandSize(int maxHandSize); boolean isPassed(); boolean isEmptyDraw(); - void pass(); + void pass(Game game); void resetPassed(); boolean hasLost(); boolean hasWon(); @@ -202,6 +202,10 @@ public interface Player extends MageItem, Copyable { void passPriorityUntilNextYourTurn(Game game); void passTurnPriority(Game game); void restorePriority(Game game); + + int getStoredBookmark(); + void setStoredBookmark(int bookmark); + void resetStoredBookmark(Game game); void revealCards(String name, Cards cards, Game game); void lookAtCards(String name, Cards cards, Game game); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 8c50b99be5..fe4df43a02 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -101,6 +101,7 @@ public abstract class PlayerImpl> implements Player, Ser protected boolean passed; protected boolean passedTurn; protected int turns; + protected int storedBookmark = -1; /** * This indicates that player passed all turns until his own turn starts. @@ -175,6 +176,7 @@ public abstract class PlayerImpl> implements Player, Ser this.userData = player.userData; this.canPayLifeCost = player.canPayLifeCost; this.canPaySacrificeCost = player.canPaySacrificeCost; + this.storedBookmark = player.storedBookmark; } @Override @@ -537,6 +539,7 @@ public abstract class PlayerImpl> implements Player, Ser game.fireEvent(event); game.fireInformEvent(name + spellAbility.getActivatedMessage(game)); game.removeBookmark(bookmark); + resetStoredBookmark(game); return true; } game.restoreState(bookmark); @@ -556,6 +559,7 @@ public abstract class PlayerImpl> implements Player, Ser game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), playerId)); game.fireInformEvent(name + " plays " + card.getName()); game.removeBookmark(bookmark); + resetStoredBookmark(game); return true; } game.restoreState(bookmark); @@ -568,7 +572,11 @@ public abstract class PlayerImpl> implements Player, Ser int bookmark = game.bookmarkState(); if (ability.activate(game, false)) { ability.resolve(game); - game.removeBookmark(bookmark); + // #169 + if (storedBookmark == -1) { + setStoredBookmark(bookmark); + } + //game.removeBookmark(bookmark); return true; } game.restoreState(bookmark); @@ -587,6 +595,7 @@ public abstract class PlayerImpl> implements Player, Ser game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATED_ABILITY, ability.getId(), ability.getSourceId(), playerId)); game.fireInformEvent(name + ability.getActivatedMessage(game)); game.removeBookmark(bookmark); + resetStoredBookmark(game); return true; } game.restoreState(bookmark); @@ -596,6 +605,7 @@ public abstract class PlayerImpl> implements Player, Ser if (ability.activate(game, false)) { ability.resolve(game); game.removeBookmark(bookmark); + resetStoredBookmark(game); return true; } game.restoreState(bookmark); @@ -612,6 +622,7 @@ public abstract class PlayerImpl> implements Player, Ser game.fireInformEvent(name + action.getActivatedMessage(game)); if (action.resolve(game)) { game.removeBookmark(bookmark); + resetStoredBookmark(game); return true; } } @@ -628,7 +639,7 @@ public abstract class PlayerImpl> implements Player, Ser } if (ability instanceof PassAbility) { - pass(); + pass(game); return true; } else if (ability instanceof PlayLandAbility) { @@ -1055,8 +1066,9 @@ public abstract class PlayerImpl> implements Player, Ser } @Override - public void pass() { + public void pass(Game game) { this.passed = true; + resetStoredBookmark(game); } @Override @@ -1598,4 +1610,21 @@ public abstract class PlayerImpl> implements Player, Ser return turns; } + @Override + public int getStoredBookmark() { + return storedBookmark; + } + + @Override + public void setStoredBookmark(int storedBookmark) { + this.storedBookmark = storedBookmark; + } + + @Override + public synchronized void resetStoredBookmark(Game game) { + if (this.storedBookmark != -1) { + game.removeBookmark(this.storedBookmark); + } + setStoredBookmark(-1); + } }