Now that we have added an undo feature to the Proximity game, we'll complete our modifications to the game by adding code that redoes actions we have just undone. Defining the Redoable CommandThe first step in making the commands redoable is to create a redoable command class. Define a class, com.peachpit.aas3wdp.proximity.commands.RedoableGamePlayCommand, that extends UndoableGamePlayCommand and adds redo functionality by implementing IRedoableCommand. package com.peachpit.aas3wdp.proximity.commands { import com.peachpit.aas3wdp.proximity.data.PieceData; import com.peachpit.aas3wdp.proximity.data.GamePlayer; import com.peachpit.aas3wdp.proximity.data.GamePlayers; import com.peachpit.aas3wdp.proximity.data.GameboardData; import com.peachpit.aas3wdp.proximity.commands.UndoableGamePlayCommand; import com.peachpit.aas3wdp.iterators.IIterator; import com.peachpit.aas3wdp.proximity.mementos.GamePieceMemento; import com.peachpit.aas3wdp.commands.IRedoableCommand; import com.peachpit.aas3wdp.proximity.data.NullOwner; public class RedoableGamePlayCommand extends UndoableGamePlayCommand implements IRedoableCommand { private var _nextGamePieceMemento:GamePieceMemento; public function RedoableGamePlayCommand(piece:PieceData) { super(piece); } override public function undo():void { _nextGamePieceMemento = GameboardData.getInstance().newGamePiece.getMemento(); super.undo(); } public function redo():void { var gameboard:GameboardData = GameboardData.getInstance(); var newGamePiece:PieceData = gameboard.newGamePiece; var currentGamePlayer:GamePlayer = newGamePiece.owner; _piece.owner = currentGamePlayer; _piece.count = newGamePiece.count; // Retrieve all adjacent pieces. var iterator:IIterator = gameboard.getProximityPieces(_piece); var piece:PieceData; while(iterator.hasNext()) { piece = PieceData(iterator.next()); // If the game piece has the same owner as // the clicked game piece, increment the // count. If they have different owners (and // the owner isn't NullOwner) then test if // the clicked game piece has a higher // count. If so, make it the new owner. if(piece.owner == _piece.owner) { piece.count++; } else if(!(piece.owner is NullOwner)) { if(piece.count < _piece.count) { piece.owner = currentGamePlayer; } } } GameboardData.getInstance().setMemento(_nextGamePieceMemento); } } } The redoable command redoes a command by essentially replaying based on the new game piece. It then uses a memento to restore the next new game piece state. Editing the Factory ClassNext we'll edit the CommandFactory class so that it returns a RedoableGamePlayCommand object when the type property is set to REDOABLE. Here's the updated getGamePlayCommand() method: public static function getGamePlayCommand(data:PieceData):ICommand { if(_type == NORMAL) { return new GamePlayCommand(data); } else if(_type == UNDOABLE) { return new UndoableGamePlayCommand(data); } else if(_type == REDOABLE) { return new RedoableGamePlayCommand(data); } return null; } Editing the Main ClassNow we can edit the main class by assigning a value of REDOABLE rather than UNDOABLE to the CommandFactory.type property. CommandFactory.type = CommandFactory.REDOABLE; Add an if clause to the onKeyboard() method so that it calls the redo() method of the next command object in the stack when the user presses the right-arrow key: private function onKeyboard(event:KeyboardEvent):void { var stack:CommandStack = CommandStack.getInstance(); var command:ICommand; if(event.keyCode == Keyboard.LEFT && stack.hasPreviousCommands()) { command = stack.previous(); if(command is IUndoableCommand) { IUndoableCommand(command).undo(); } else { stack.next(); } } // If the user pressed the right arrow key and there are next // commands in the stack, and if the command is redoable, call // redo(). if(event.keyCode == Keyboard.RIGHT && stack.hasNextCommands()) { command = stack.next(); if(command is IRedoableCommand) { IRedoableCommand(command).redo(); } else { stack.previous(); } } } When you test the application now, you can press the right-arrow key to redo any action that you've previously undone. |