summaryrefslogtreecommitdiff
path: root/src/tetris/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/tetris/gui')
-rw-r--r--src/tetris/gui/BoardUI.java110
-rw-r--r--src/tetris/gui/ImagePanel.java70
-rw-r--r--src/tetris/gui/InfoPanel.java212
-rw-r--r--src/tetris/gui/NextPieceDisplay.java124
-rw-r--r--src/tetris/gui/TetrisGUI.java252
-rw-r--r--src/tetris/gui/TetrisPieceDisplay.java85
-rw-r--r--src/tetris/gui/WrittenButton.java155
-rw-r--r--src/tetris/gui/animations/Animation.java193
-rw-r--r--src/tetris/gui/animations/TetrisPieceAnimation.java86
-rw-r--r--src/tetris/gui/events/TetrisKeyListener.java69
-rwxr-xr-xsrc/tetris/gui/handwriting.ttfbin0 -> 95576 bytes
-rw-r--r--src/tetris/gui/images/ImageRes.java73
-rw-r--r--src/tetris/gui/images/background.pngbin0 -> 386891 bytes
-rw-r--r--src/tetris/gui/images/gameover.pngbin0 -> 9655 bytes
-rw-r--r--src/tetris/gui/images/gameover_background.pngbin0 -> 537717 bytes
-rw-r--r--src/tetris/gui/images/paused.pngbin0 -> 6142 bytes
-rw-r--r--src/tetris/gui/images/square.pngbin0 -> 3689 bytes
-rw-r--r--src/tetris/gui/images/writing_pencil.pngbin0 -> 20101 bytes
18 files changed, 1429 insertions, 0 deletions
diff --git a/src/tetris/gui/BoardUI.java b/src/tetris/gui/BoardUI.java
new file mode 100644
index 0000000..2bdfb6f
--- /dev/null
+++ b/src/tetris/gui/BoardUI.java
@@ -0,0 +1,110 @@
+/*
+ * Jesse Morgan <jesterpm@u.washington.edu>
+ *
+ * TCSS 305 � Autumn 2009
+ * Tetris Project
+ * 17 November 2009
+ */
+
+package tetris.gui;
+
+import java.awt.Graphics;
+import java.awt.Image;
+import java.util.Observable;
+
+import tetris.board.TetrisBoard;
+import tetris.gui.images.ImageRes;
+
+/**
+ * Panel that draws the tetris board.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 23 November 2009
+ */
+@SuppressWarnings("serial")
+public class BoardUI extends TetrisPieceDisplay {
+ /**
+ * Flag to indicate game over.
+ */
+ private boolean my_game_over;
+
+ /**
+ * Flag to indicate game paused.
+ */
+ private boolean my_game_paused;
+
+ /**
+ * The Game Over graphic.
+ */
+ private final Image my_gameover_graphic;
+
+ /**
+ * The Paused graphic.
+ */
+ private final Image my_paused_graphic;
+
+ /**
+ * Constructor.
+ *
+ * @param the_width Number of columns.
+ * @param the_height Number of rows.
+ *
+ */
+ public BoardUI(final int the_width, final int the_height) {
+ // Call parent
+ super(the_width, the_height);
+
+ my_game_over = false;
+ my_game_paused = false;
+
+ my_gameover_graphic = ImageRes.loadImage(ImageRes.GAME_OVER_LABEL);
+
+ my_paused_graphic = ImageRes.loadImage(ImageRes.PAUSED_LABEL);
+
+ }
+
+ /**
+ * Update the board.
+ *
+ * @param the_observable The tetris board we receive updates for.
+ * @param the_argument Unused argument.
+ */
+ public void update(final Observable the_observable, final Object the_argument) {
+ if (the_observable instanceof TetrisBoard) {
+ final TetrisBoard tb = (TetrisBoard) the_observable;
+
+ my_bricks = tb.getTetrisBlocks();
+ my_game_over = tb.isGameOver();
+ my_game_paused = tb.isPaused();
+
+ repaint();
+ }
+
+
+ }
+
+ /**
+ * Paint Game Over or Paused if needed.
+ *
+ * @param the_graphics The Graphics to draw on (expected to be a Graphics2D).
+ */
+ protected void paintComponent(final Graphics the_graphics) {
+ super.paintComponent(the_graphics);
+
+ if (my_game_over) {
+ the_graphics.drawImage(my_gameover_graphic,
+ 0,
+ (getHeight() - my_gameover_graphic.getHeight(null)) / 2,
+ null);
+
+ } else if (my_game_paused) {
+ the_graphics.drawImage(my_paused_graphic,
+ 0,
+ (getHeight() - my_paused_graphic.getHeight(null)) / 2,
+ null);
+ }
+ }
+
+
+
+}
diff --git a/src/tetris/gui/ImagePanel.java b/src/tetris/gui/ImagePanel.java
new file mode 100644
index 0000000..d558788
--- /dev/null
+++ b/src/tetris/gui/ImagePanel.java
@@ -0,0 +1,70 @@
+/*
+ * Jesse Morgan <jesterpm@u.washington.edu>
+ *
+ * TCSS 305 � Autumn 2009
+ * Tetris Project
+ * 17 November 2009
+ */
+
+package tetris.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Image;
+
+import javax.swing.JPanel;
+
+/**
+ * Custom panel that displays an image in the background.
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 4 Dec 2009
+ */
+@SuppressWarnings("serial")
+public class ImagePanel extends JPanel {
+ /**
+ * The image that contains the background.
+ */
+ private Image my_image;
+
+ /**
+ * Constructor. Defaults to BorderLayout.
+ * @param the_image The background Image.
+ */
+ public ImagePanel(final Image the_image) {
+ super();
+
+ setLayout(new BorderLayout());
+
+ my_image = the_image;
+ setPreferredSize(new Dimension(the_image.getWidth(null), the_image.getHeight(null)));
+ }
+
+ /**
+ * @return the current background Image.
+ */
+ public Image getBackgroundImage() {
+ return my_image;
+ }
+
+ /**
+ * Set a new background Image.
+ *
+ * @param the_image the new Image.
+ */
+ public void setBackgroundImage(final Image the_image) {
+ my_image = the_image;
+ setPreferredSize(new Dimension(the_image.getWidth(null), the_image.getHeight(null)));
+ }
+
+ /**
+ * Draws the background image.
+ *
+ * @param the_graphics The Graphics Context.
+ */
+ public void paintComponent(final Graphics the_graphics) {
+ setOpaque(false);
+ the_graphics.drawImage(my_image, 0, 0, null);
+ super.paintComponent(the_graphics);
+ }
+} \ No newline at end of file
diff --git a/src/tetris/gui/InfoPanel.java b/src/tetris/gui/InfoPanel.java
new file mode 100644
index 0000000..a3bdd8f
--- /dev/null
+++ b/src/tetris/gui/InfoPanel.java
@@ -0,0 +1,212 @@
+/*
+ * Jesse Morgan <jesterpm@u.washington.edu>
+ *
+ * TCSS 305 - Autumn 2009
+ * Tetris Project
+ * 17 November 2009
+ */
+
+package tetris.gui;
+
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Observable;
+import java.util.Observer;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import tetris.board.TetrisBoard;
+
+/**
+ * JPanel of info about the game.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 11 December 2009
+ */
+@SuppressWarnings("serial")
+public class InfoPanel extends JPanel implements Observer {
+ // Private Constants
+ /**
+ * The wording of the new game button.
+ */
+ private static final String NEWGAME_LABEL = "New Game";
+
+ /**
+ * The wording of the pause button.
+ */
+ private static final String PAUSE_LABEL = "Pause";
+
+ /**
+ * The wording of the unpause button.
+ */
+ private static final String UNPAUSE_LABEL = "Unpause";
+
+ // Obnoxious UI layout constants follow
+ /**
+ * Space between the top of the panel and the Score.
+ */
+ private static final int UPPER_SPACING = 18;
+
+ /**
+ * Space between the level and the next piece.
+ */
+ private static final int MIDDLE_SPACING = 38;
+
+ /**
+ * Size of the score.
+ */
+ private static final float SCORE_FONT_SIZE = 32f;
+
+ /**
+ * Size of the level.
+ */
+ private static final float LEVEL_FONT_SIZE = 35f;
+
+ /**
+ * The TetrisBoard we're displaying info for.
+ */
+ private final TetrisBoard my_board;
+
+ /**
+ * The score label.
+ */
+ private final JLabel my_score_label;
+
+ /**
+ * The level label.
+ */
+ private final JLabel my_level_label;
+
+ /**
+ * Construct the Info Panel.
+ *
+ * @param the_board The tetris board we belong to.
+ */
+ public InfoPanel(final TetrisBoard the_board) {
+ super();
+
+ my_board = the_board;
+ the_board.addObserver(this);
+
+ // Setup Panel
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ setOpaque(false);
+
+ // Create Components
+ my_score_label = new JLabel("0");
+ my_level_label = new JLabel("Level 1");
+
+ setupPanel();
+ }
+
+ /**
+ * Handle notifications.
+ *
+ * @param the_observable The TetrisBoard.
+ * @param the_arg Unused argument.
+ */
+ public void update(final Observable the_observable, final Object the_arg) {
+ if (the_observable instanceof TetrisBoard) {
+ final TetrisBoard tb = (TetrisBoard) the_observable;
+
+ my_score_label.setText(String.valueOf(tb.getScore()));
+
+ my_level_label.setText("Level " + tb.getLevel());
+ }
+ }
+
+
+ /**
+ * Helper method to setup the east side.
+ */
+ private void setupPanel() {
+ Font base_font;
+
+ // Load the font.
+ try {
+ final InputStream fis = getClass().getResourceAsStream("/tetris/gui/handwriting.ttf");
+ base_font = Font.createFont(Font.TRUETYPE_FONT, fis);
+ fis.close();
+
+ } catch (final FontFormatException the_exception) {
+ base_font = Font.getFont(Font.SANS_SERIF);
+
+ } catch (final IOException the_exception) {
+ base_font = Font.getFont(Font.SANS_SERIF);
+ }
+
+ // Add a Strut to move everything onto a line.
+ add(Box.createVerticalStrut(UPPER_SPACING));
+
+ // Display the Score
+ my_score_label.setFont(base_font.deriveFont(SCORE_FONT_SIZE));
+ my_score_label.setAlignmentX(CENTER_ALIGNMENT);
+ add(my_score_label);
+
+
+ // Display the level
+ my_level_label.setFont(base_font.deriveFont(LEVEL_FONT_SIZE));
+ my_level_label.setAlignmentX(CENTER_ALIGNMENT);
+ add(my_level_label);
+
+ // Add a bit of space between the level and the next piece display.
+ add(Box.createVerticalStrut(MIDDLE_SPACING));
+
+ // Add the next piece component
+ final NextPieceDisplay nextpiece = new NextPieceDisplay();
+ add(nextpiece);
+ nextpiece.setAlignmentX(CENTER_ALIGNMENT);
+ my_board.addObserver(nextpiece);
+
+
+
+ // New Game Button
+ final WrittenButton new_game = new WrittenButton();
+ new_game.setText(NEWGAME_LABEL);
+ new_game.setAlignmentX(CENTER_ALIGNMENT);
+ add(new_game);
+
+ // Pause Game Button
+ final WrittenButton pause_game = new WrittenButton();
+ pause_game.setText(PAUSE_LABEL);
+ pause_game.setAlignmentX(CENTER_ALIGNMENT);
+ add(pause_game);
+
+ // Setup Action Listeners
+
+ // New Game
+ new_game.addActionListener(new ActionListener() {
+ public void actionPerformed(final ActionEvent the_event) {
+ my_board.newGame();
+ }
+ });
+
+ // Pause Game
+ pause_game.addActionListener(new ActionListener() {
+ public void actionPerformed(final ActionEvent the_event) {
+ final WrittenButton b = (WrittenButton) the_event.getSource();
+
+ // Do nothing if the game is over.
+ if (my_board.isGameOver()) {
+ return;
+ }
+
+ if (b.getText().equals(PAUSE_LABEL)) {
+ my_board.setPaused(true);
+ b.setText(UNPAUSE_LABEL);
+
+ } else {
+ my_board.setPaused(false);
+ b.setText(PAUSE_LABEL);
+ }
+ }
+ });
+ }
+}
diff --git a/src/tetris/gui/NextPieceDisplay.java b/src/tetris/gui/NextPieceDisplay.java
new file mode 100644
index 0000000..4f0f543
--- /dev/null
+++ b/src/tetris/gui/NextPieceDisplay.java
@@ -0,0 +1,124 @@
+/*
+ * Jesse Morgan <jesterpm@u.washington.edu>
+ *
+ * TCSS 305 � Autumn 2009
+ * Tetris Project
+ * 17 November 2009
+ */
+
+package tetris.gui;
+
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Observable;
+
+import javax.swing.Timer;
+
+import tetris.board.TetrisBoard;
+import tetris.gui.animations.TetrisPieceAnimation;
+import tetris.gui.images.ImageRes;
+import tetris.model.IntPoint;
+import tetris.piece.TPiece;
+import tetris.piece.TetrisPiece;
+
+/**
+ * Panel that draws the tetris board.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 1 Dec 2009
+ */
+@SuppressWarnings("serial")
+public class NextPieceDisplay extends TetrisPieceDisplay {
+ /**
+ * The Pencil Graphic.
+ */
+ private static final Image PENCIL = ImageRes.loadImage(ImageRes.WRITING_PENCIL);
+
+
+ /**
+ * The next piece.
+ */
+ private TetrisPiece my_next_piece;
+
+ /**
+ * Our animator.
+ */
+ private TetrisPieceAnimation my_animation;
+
+ /**
+ * Animation timer.
+ */
+ private final Timer my_timer;
+
+
+ /**
+ * Constructor.
+ */
+ public NextPieceDisplay() {
+ // Call parent
+ super(TetrisPiece.TETRIS_PIECE_SIZE, TetrisPiece.TETRIS_PIECE_SIZE);
+
+ my_animation = new TetrisPieceAnimation(new TPiece(0, 0));
+
+ my_timer = new Timer(TetrisPieceAnimation.FRAME_RATE, new ActionListener() {
+ public void actionPerformed(final ActionEvent the_event) {
+ my_animation.stepAnimation();
+ repaint();
+ }
+ });
+ }
+
+ /**
+ * Update the next piece display.
+ *
+ * @param the_observable The tetris board we receive updates for.
+ * @param the_argument Unused argument.
+ */
+ public void update(final Observable the_observable, final Object the_argument) {
+ if (the_observable instanceof TetrisBoard) {
+ final TetrisBoard tb = (TetrisBoard) the_observable;
+
+ // If the next piece we know about is not the same next piece...
+ if (tb.getNextPiece() != my_next_piece) {
+ my_next_piece = tb.getNextPiece();
+ my_bricks.clear();
+
+ for (IntPoint p : my_next_piece.getBoardCoordinates()) {
+ my_bricks.add(new IntPoint(p.getX() - my_next_piece.getX(),
+ p.getY() - my_next_piece.getY() +
+ TetrisBoard.NEW_PIECE_BUFFER));
+ }
+
+ my_animation = new TetrisPieceAnimation(my_next_piece);
+ startAnimation();
+ }
+
+ repaint();
+ }
+ }
+
+ @Override
+ protected void paintComponent(final Graphics the_graphics) {
+ super.paintComponent(the_graphics);
+
+ if (my_animation.isRunning()) {
+ final int x = my_animation.getX();
+ final int y = my_animation.getY() - PENCIL.getHeight(null);
+ the_graphics.drawImage(PENCIL, x, y, null);
+ // Normally you'd draw the animation image here, but we just want the pencil to move.
+ }
+ }
+
+ /**
+ * Start the animation and timer.
+ */
+ private void startAnimation() {
+ my_animation.start();
+ my_timer.start();
+ }
+
+
+
+}
diff --git a/src/tetris/gui/TetrisGUI.java b/src/tetris/gui/TetrisGUI.java
new file mode 100644
index 0000000..fd463a8
--- /dev/null
+++ b/src/tetris/gui/TetrisGUI.java
@@ -0,0 +1,252 @@
+/*
+ * Jesse Morgan <jesterpm@u.washington.edu>
+ *
+ * TCSS 305 - Autumn 2009
+ * Tetris Project
+ * 17 November 2009
+ */
+
+package tetris.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.Observable;
+import java.util.Observer;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.Timer;
+
+import tetris.audio.TetrisSounds;
+import tetris.board.TetrisBoard;
+import tetris.board.TetrisBoardEvent;
+import tetris.gui.events.TetrisKeyListener;
+import tetris.gui.images.ImageRes;
+
+/**
+ * Tetris UI Main Frame.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 23 November 2009
+ */
+@SuppressWarnings("serial")
+public class TetrisGUI extends JFrame implements Observer {
+ /**
+ * UI Padding at the top of the frame.
+ */
+ private static final int NORTH_PADDING = 100;
+
+ /**
+ * UI Padding on the west side of the frame.
+ */
+ private static final int WEST_PADDING = 75;
+
+ /**
+ * The TetrisBoard we're playing on.
+ */
+ private final TetrisBoard my_board;
+
+ /**
+ * The gameplay timer.
+ */
+ private Timer my_timer;
+
+ /**
+ * Stores the background image panel.
+ */
+ private final ImagePanel my_background_panel;
+
+ /**
+ * Constructor for the tetris UI.
+ */
+ public TetrisGUI() {
+ super("TCSS 305 Tetris");
+
+ // Setup Global UI Elements
+ my_background_panel = new ImagePanel(ImageRes.loadImage(ImageRes.GAME_BACKGROUND));
+ add(my_background_panel);
+
+ // Create the board.
+ my_board = new TetrisBoard(TetrisBoard.STANDARD_BOARD_WIDTH,
+ TetrisBoard.STANDARD_BOARD_HEIGHT);
+
+ // We Observe the Board.
+ my_board.addObserver(this);
+
+ // Add a board UI
+ final BoardUI board_ui = new BoardUI(TetrisBoard.STANDARD_BOARD_WIDTH,
+ TetrisBoard.STANDARD_BOARD_HEIGHT);
+ my_background_panel.add(board_ui, BorderLayout.CENTER);
+
+ my_board.addObserver(board_ui);
+
+ // Add an InfoPanel
+ final InfoPanel info = new InfoPanel(my_board);
+ my_background_panel.add(info, BorderLayout.EAST);
+
+ // Create a tetris sound player
+ final TetrisSounds ts = new TetrisSounds();
+ my_board.addObserver(ts);
+
+ setupFrame();
+ setupUIPadding();
+ setupListeners();
+ }
+
+ /**
+ * Start the game.
+ */
+ public void start() {
+ my_board.setPaused(false);
+ my_timer.start();
+ }
+
+ /**
+ * Pause the game.
+ */
+ public void pause() {
+ my_board.setPaused(true);
+ my_timer.stop();
+ }
+
+ /**
+ * Update the level and score.
+ *
+ * @param the_observable Should be an instance of TetrisBoard
+ * @param the_arg Unused by this method.
+ */
+ public void update(final Observable the_observable, final Object the_arg) {
+ if (the_observable instanceof TetrisBoard) {
+ final TetrisBoard tb = (TetrisBoard) the_observable;
+ final TetrisBoardEvent event = (TetrisBoardEvent) the_arg;
+
+ switch (event.getType()) {
+ case NEW_GAME:
+ my_background_panel.setBackgroundImage(ImageRes.loadImage(ImageRes.GAME_BACKGROUND));
+ repaint();
+ start();
+ // Purposefully falling through....
+
+ case SCORE_CHANGED:
+ my_timer.setDelay(tb.getSpeed());
+ break;
+
+ case GAME_OVER:
+ my_background_panel.setBackgroundImage(ImageRes.
+ loadImage(ImageRes.GAMEOVER_BACKGROUND));
+ repaint();
+ break;
+
+ default:
+ }
+ }
+ }
+
+ /**
+ * Game Entry Point.
+ *
+ * @param the_args Unused command line arguments.
+ */
+ public static void main(final String[] the_args) {
+ final TetrisGUI ui = new TetrisGUI();
+
+ ui.setVisible(true);
+ }
+
+ /**
+ * Helper method to setup various frame attributes.
+ */
+ private void setupFrame() {
+ // Set frame attributes
+ setSize(my_background_panel.getPreferredSize());
+ setResizable(false);
+ setDefaultCloseOperation(EXIT_ON_CLOSE);
+ }
+
+ /**
+ * Helper method to setup the padding on the UI.
+ */
+ private void setupUIPadding() {
+ // North Side
+ final JPanel north_panel = new JPanel();
+ north_panel.setOpaque(false);
+ north_panel.setPreferredSize(new Dimension(0, NORTH_PADDING));
+
+ // West Side
+ final JPanel west_panel = new JPanel();
+ west_panel.setOpaque(false);
+ west_panel.setPreferredSize(new Dimension(WEST_PADDING, 0));
+
+ my_background_panel.add(north_panel, BorderLayout.NORTH);
+ my_background_panel.add(west_panel, BorderLayout.WEST);
+ }
+
+ /**
+ * Helper methods to setup event listeners.
+ */
+ private void setupListeners() {
+ // Add Key Listener
+ addKeyListener(new TetrisKeyListener(my_board));
+
+ // Add Window Listener
+ addWindowListener(new TetrisWindowListener());
+
+ // Add a timer
+ my_timer = new Timer(my_board.getSpeed(), new ActionListener() {
+ public void actionPerformed(final ActionEvent the_event) {
+ my_board.progressBoard();
+ }
+ });
+ }
+
+ /**
+ * Starts and stops the timer based off of WindowEvents.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 1 Dec 2009
+ */
+ private class TetrisWindowListener extends WindowAdapter {
+ /**
+ * Flag to store if we lost focus.
+ */
+ private boolean my_lost_focus_flag;
+
+ /**
+ * Create the TetrisWindowListener.
+ */
+ public TetrisWindowListener() {
+ super();
+ my_lost_focus_flag = false;
+ }
+
+ @Override
+ public void windowActivated(final WindowEvent the_event) {
+ super.windowActivated(the_event);
+ if (my_lost_focus_flag && my_board.isPaused()) {
+ start();
+ my_lost_focus_flag = false;
+ }
+ }
+
+ @Override
+ public void windowClosed(final WindowEvent the_event) {
+ super.windowClosed(the_event);
+ pause();
+ }
+
+ @Override
+ public void windowDeactivated(final WindowEvent the_event) {
+ super.windowDeactivated(the_event);
+
+ // Don't pause if we're already paused.
+ if (!my_board.isPaused()) {
+ my_lost_focus_flag = true;
+ pause();
+ }
+ }
+ }
+}
diff --git a/src/tetris/gui/TetrisPieceDisplay.java b/src/tetris/gui/TetrisPieceDisplay.java
new file mode 100644
index 0000000..409942c
--- /dev/null
+++ b/src/tetris/gui/TetrisPieceDisplay.java
@@ -0,0 +1,85 @@
+/*
+ * Jesse Morgan <jesterpm@u.washington.edu>
+ *
+ * TCSS 305 � Autumn 2009
+ * Tetris Project
+ * 17 November 2009
+ */
+
+package tetris.gui;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Observer;
+
+import javax.swing.JPanel;
+
+import tetris.board.TetrisBoard;
+import tetris.gui.images.ImageRes;
+import tetris.model.IntPoint;
+
+/**
+ * Abstract component capable of drawing out tetris pieces.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ * @version 1.0 1 Dec 2009
+ */
+@SuppressWarnings("serial")
+public abstract class TetrisPieceDisplay extends JPanel implements Observer {
+ // Private contants
+ /**
+ * Pixel size of one tetris piece.
+ */
+ private static final int BRICK_SIZE = 27;
+
+ //Private fields
+ /**
+ * List of the bricks on the screen.
+ */
+ protected List<IntPoint> my_bricks;
+
+ /**
+ * Storage for the brick image.
+ */
+ private final Image my_brick_image;
+
+ /**
+ * Constructor.
+ *
+ * @param the_width Number of columns.
+ * @param the_height Number of rows.
+ */
+ public TetrisPieceDisplay(final int the_width, final int the_height) {
+ // Call parent
+ super();
+
+ // Load brick image
+ my_brick_image = ImageRes.loadImage(ImageRes.TETRIS_BLOCK);
+
+ // Create our brick list
+ my_bricks = new ArrayList<IntPoint>();
+
+ // Set some hints for the layout manager
+ final Dimension d = new Dimension(the_width * my_brick_image.getWidth(null),
+ the_height * my_brick_image.getHeight(null));
+ setPreferredSize(d);
+ setMinimumSize(d);
+ setMaximumSize(d);
+ setOpaque(false);
+ }
+
+ @Override
+ protected void paintComponent(final Graphics the_graphics) {
+ super.paintComponent(the_graphics);
+
+ for (IntPoint brick : my_bricks) {
+ the_graphics.drawImage(my_brick_image,
+ brick.getX() * BRICK_SIZE,
+ (brick.getY() - TetrisBoard.NEW_PIECE_BUFFER) * BRICK_SIZE,
+ null);
+ }
+ }
+}
diff --git a/src/tetris/gui/WrittenButton.java b/src/tetris/gui/WrittenButton.java
new file mode 100644
index 0000000..2a0e55f
--- /dev/null
+++ b/src/tetris/gui/WrittenButton.java
@@ -0,0 +1,155 @@
+package tetris.gui;
+
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.swing.AbstractButton;
+import javax.swing.JLabel;
+
+import tetris.gui.images.ImageRes;
+
+/**
+ * WrittenButton is a button with a handwritten font and a pencil roll-over.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 8 December 2009.
+ */
+@SuppressWarnings("serial")
+public class WrittenButton extends AbstractButton {
+ /**
+ * The pencil icon used in the roll-over.
+ */
+ private static final Image PENCIL = ImageRes.loadImage(ImageRes.WRITING_PENCIL);
+
+ /**
+ * Default font size.
+ */
+ private static final float DEFAULT_FONT_SIZE = 36;
+
+ /**
+ * How much pencil to show.
+ */
+ private static final int PENCIL_WIDTH = 50;
+
+ /**
+ * An adjustment to the Y of the pencil.
+ */
+ private static final int PENCIL_ADJUSTMENT = 10;
+
+ /**
+ * The button label.
+ */
+ private final JLabel my_label;
+
+ /**
+ * Is the mouse over the button?
+ */
+ private boolean my_rolledover;
+
+ /**
+ * Default constrcutor.
+ */
+ public WrittenButton() {
+ super();
+
+ my_rolledover = false;
+
+ // Setup Button
+ addMouseListener(new MouseHandler());
+ setOpaque(false);
+
+ // Setup Label
+ my_label = new JLabel();
+ add(my_label);
+ }
+
+ @Override
+ public void setText(final String the_label) {
+ super.setText(the_label);
+
+ Font base_font;
+
+ try {
+ final InputStream fis = getClass().getResourceAsStream("/tetris/gui/handwriting.ttf");
+ base_font = Font.createFont(Font.TRUETYPE_FONT, fis);
+ fis.close();
+
+ } catch (final FontFormatException the_exception) {
+ base_font = Font.getFont(Font.SANS_SERIF);
+
+ } catch (final IOException the_exception) {
+ base_font = Font.getFont(Font.SANS_SERIF);
+ }
+
+ my_label.setText(the_label);
+ my_label.setFont(base_font.deriveFont(DEFAULT_FONT_SIZE));
+
+ // Set some hints for the layout manager.
+ final Dimension ls = my_label.getPreferredSize();
+ ls.width += PENCIL_WIDTH;
+ setPreferredSize(ls);
+ setMinimumSize(ls);
+ setMaximumSize(ls);
+ }
+
+ @Override
+ public String getText() {
+ return my_label.getText();
+ }
+
+ @Override
+ protected void paintComponent(final Graphics the_graphics) {
+ super.paintComponent(the_graphics);
+
+ if (my_rolledover) {
+ final int x = my_label.getX() + my_label.getWidth();
+ final int y = my_label.getY() + my_label.getHeight() -
+ PENCIL.getHeight(null) - PENCIL_ADJUSTMENT;
+
+ the_graphics.drawImage(PENCIL, x, y, null);
+ }
+ }
+
+ /**
+ * Mouse handler for the button.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 8 Dec 2009
+ */
+ private class MouseHandler extends MouseAdapter {
+ @Override
+ public void mouseEntered(final MouseEvent the_event) {
+ super.mouseEntered(the_event);
+
+ my_rolledover = true;
+ repaint();
+ }
+
+ @Override
+ public void mouseExited(final MouseEvent the_event) {
+ super.mouseExited(the_event);
+
+ my_rolledover = false;
+ repaint();
+ }
+
+ @Override
+ public void mouseClicked(final MouseEvent the_event) {
+ super.mouseClicked(the_event);
+
+ final ActionEvent e = new ActionEvent(WrittenButton.this,
+ ActionEvent.ACTION_PERFORMED,
+ getText());
+
+ WrittenButton.this.fireActionPerformed(e);
+ }
+ }
+}
diff --git a/src/tetris/gui/animations/Animation.java b/src/tetris/gui/animations/Animation.java
new file mode 100644
index 0000000..899459d
--- /dev/null
+++ b/src/tetris/gui/animations/Animation.java
@@ -0,0 +1,193 @@
+/*
+ * Jesse Morgan <jesterpm@u.washington.edu>
+ *
+ * TCSS 305 - Autumn 2009
+ * Tetris Project
+ * 17 November 2009
+ */
+
+package tetris.gui.animations;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+
+import tetris.model.IntPoint;
+
+
+/**
+ * Abstract class that processes animations.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 11 Dec 2009
+ */
+public abstract class Animation {
+ // Public Constants
+ /**
+ * How quickly animation should be updated.
+ */
+ public static final int FRAME_RATE = 60;
+
+ // Private Constants
+ /**
+ * How much we show in each step.
+ */
+ private static final int R = 5;
+
+ /**
+ * The current step in the animation.
+ */
+ private int my_step;
+
+ /**
+ * The current position in the animation.
+ */
+ private IntPoint my_current_pos;
+
+ /**
+ * Current Step Point.
+ */
+ private IntPoint my_step_point;
+
+ /**
+ * Animation Running Flag.
+ */
+ private boolean my_animation_running;
+
+ /**
+ * What the animation presently looks like.
+ */
+ private final BufferedImage my_image;
+
+ /**
+ * What the animation will look like.
+ */
+ private final Image my_end_image;
+
+ /**
+ * Create a new animation.
+ *
+ * @param the_end_image End result.
+ * @param the_width Animation width.
+ * @param the_height Animation height.
+ */
+ public Animation(final Image the_end_image, final int the_width, final int the_height) {
+ my_step = 0;
+ my_animation_running = false;
+
+ my_current_pos = new IntPoint(0, 0);
+ my_step_point = new IntPoint(0, 0);
+
+ my_end_image = the_end_image;
+ my_image = new BufferedImage(the_width, the_height,
+ BufferedImage.TYPE_INT_ARGB);
+
+ }
+
+ /**
+ * Start the animation.
+ */
+ public void start() {
+ my_step = 0;
+
+ my_current_pos = getStep(0);
+ my_step_point = getStep(0);
+
+ my_animation_running = true;
+ }
+
+ /**
+ * Stop the animation.
+ */
+ public void stop() {
+ my_animation_running = false;
+ }
+
+ /**
+ * @return true if the animation is running.
+ */
+ public boolean isRunning() {
+ return my_animation_running;
+ }
+
+ /**
+ * Draw the next increment of the animation.
+ */
+ public void stepAnimation() {
+ // If we're near our destination, move to the next step.
+ if (
+ Math.abs(my_current_pos.getX() - my_step_point.getX()) < R &&
+ Math.abs(my_current_pos.getY() - my_step_point.getY()) < R
+ ) {
+ // If we're out of steps, stop.
+ if (my_step >= stepCount() - 1) {
+ my_animation_running = false;
+ return;
+ }
+
+ my_step++;
+ my_step_point = getStep(my_step);
+ }
+
+ // Draw!
+ final Graphics2D g2d = my_image.createGraphics();
+
+ g2d.drawImage(my_end_image,
+ my_current_pos.getX(), my_current_pos.getY(),
+ my_current_pos.getX() + R, my_current_pos.getY() + R,
+
+ my_current_pos.getX() % my_end_image.getWidth(null),
+ my_current_pos.getY() % my_end_image.getHeight(null),
+ (my_current_pos.getX() + R) % my_end_image.getWidth(null),
+ (my_current_pos.getY() + R) % my_end_image.getHeight(null),
+ null);
+
+ // Some math to draw in the correct "direction"
+ final int x = my_step_point.getX() - my_current_pos.getX();
+ final int y = my_step_point.getY() - my_current_pos.getY();
+ double theta = Math.asin(y / Math.sqrt(x * x + y * y));
+
+ // Correction if we're moving toward the Y axis.
+ if (x < 0) {
+ theta = -theta + Math.PI;
+ }
+
+ // Calculate the next point
+ my_current_pos = new IntPoint((int) (my_current_pos.getX() + R * Math.cos(theta)),
+ (int) (my_current_pos.getY() + R * Math.sin(theta)));
+
+ }
+
+ /**
+ * @return the image of the current animation.
+ */
+ public Image getImage() {
+ return my_image;
+ }
+
+ /**
+ * @return the current X in the animation.
+ */
+ public int getX() {
+ return my_current_pos.getX();
+ }
+
+ /**
+ * @return the current Y in the animation.
+ */
+ public int getY() {
+ return my_current_pos.getY();
+ }
+
+ /**
+ * Returns the point at the given step.
+ * @param the_step The Step.
+ * @return the Point.
+ */
+ protected abstract IntPoint getStep(final int the_step);
+
+ /**
+ * @return the number of steps in the animation.
+ */
+ protected abstract int stepCount();
+}
diff --git a/src/tetris/gui/animations/TetrisPieceAnimation.java b/src/tetris/gui/animations/TetrisPieceAnimation.java
new file mode 100644
index 0000000..b503c88
--- /dev/null
+++ b/src/tetris/gui/animations/TetrisPieceAnimation.java
@@ -0,0 +1,86 @@
+/*
+ * Jesse Morgan <jesterpm@u.washington.edu>
+ *
+ * TCSS 305 - Autumn 2009
+ * Tetris Project
+ * 17 November 2009
+ */
+
+package tetris.gui.animations;
+
+import tetris.gui.images.ImageRes;
+import tetris.model.IntPoint;
+import tetris.piece.TetrisPiece;
+
+/**
+ * Animation that knows how to animate a tetris piece.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 11 December 2009
+ */
+public class TetrisPieceAnimation extends Animation {
+ /**
+ * Size of a brick.
+ */
+ private static final int BRICK_SIZE = 30;
+
+
+ /**
+ * Size of the animation area.
+ */
+ private static final int ANIMATION_SIZE = BRICK_SIZE * 4;
+
+ /**
+ * The steps of a brick animation.
+ */
+ private static final IntPoint[] STEPS = {
+ new IntPoint(0, 0),
+ new IntPoint(26, 0),
+ new IntPoint(26, 26),
+ new IntPoint(0, 26),
+ new IntPoint(0, 0),
+
+ new IntPoint(8, 0),
+ new IntPoint(0, 8),
+ new IntPoint(3, 8),
+ new IntPoint(16, 2),
+ new IntPoint(4, 20),
+ new IntPoint(20, 4),
+ new IntPoint(12, 20),
+ new IntPoint(24, 14),
+ new IntPoint(24, 20),
+ new IntPoint(20, 26),
+ };
+
+ /**
+ * A list of tetris piece points.
+ */
+ private final IntPoint[] my_bricks;
+
+ /**
+ * Animate the drawing of a tetris piece.
+ *
+ * @param the_piece the piece to draw.
+ */
+ public TetrisPieceAnimation(final TetrisPiece the_piece) {
+ super(ImageRes.loadImage(ImageRes.TETRIS_BLOCK), ANIMATION_SIZE, ANIMATION_SIZE);
+
+ my_bricks = the_piece.translate(-the_piece.getX(),
+ -the_piece.getY()).getBoardCoordinates();
+ }
+
+ @Override
+ protected IntPoint getStep(final int the_step) {
+ final IntPoint ani_step = STEPS[the_step % STEPS.length];
+ final int brick = the_step / STEPS.length;
+
+ return new IntPoint(ani_step.getX() + my_bricks[brick].getX() * BRICK_SIZE,
+ ani_step.getY() + my_bricks[brick].getY() * BRICK_SIZE);
+ }
+
+ @Override
+ protected int stepCount() {
+ return my_bricks.length * STEPS.length;
+ }
+
+}
diff --git a/src/tetris/gui/events/TetrisKeyListener.java b/src/tetris/gui/events/TetrisKeyListener.java
new file mode 100644
index 0000000..10b999e
--- /dev/null
+++ b/src/tetris/gui/events/TetrisKeyListener.java
@@ -0,0 +1,69 @@
+/*
+ * Jesse Morgan <jesterpm@u.washington.edu>
+ *
+ * TCSS 305 � Autumn 2009
+ * Tetris Project
+ * 17 November 2009
+ */
+
+package tetris.gui.events;
+
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+import tetris.board.TetrisBoard;
+
+/**
+ * Keyboard Input handler for Tetris board.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 30 Nov 2009
+ */
+public class TetrisKeyListener extends KeyAdapter {
+ /**
+ * The tetris board we update.
+ */
+
+ private final TetrisBoard my_board;
+
+ /**
+ * Constructor.
+ *
+ * @param the_board The board to update.
+ */
+ public TetrisKeyListener(final TetrisBoard the_board) {
+ super();
+
+ my_board = the_board;
+ }
+
+ @Override
+ public void keyPressed(final KeyEvent the_event) {
+ super.keyPressed(the_event);
+
+ switch (the_event.getKeyCode()) {
+ case KeyEvent.VK_LEFT:
+ my_board.moveLeft();
+ break;
+
+ case KeyEvent.VK_RIGHT:
+ my_board.moveRight();
+ break;
+
+ case KeyEvent.VK_UP:
+ my_board.rotateRight();
+ break;
+
+ case KeyEvent.VK_DOWN:
+ my_board.moveDown();
+ break;
+
+ case KeyEvent.VK_SPACE:
+ my_board.dropPiece();
+ break;
+
+ default:
+ }
+ }
+
+}
diff --git a/src/tetris/gui/handwriting.ttf b/src/tetris/gui/handwriting.ttf
new file mode 100755
index 0000000..a70b0fc
--- /dev/null
+++ b/src/tetris/gui/handwriting.ttf
Binary files differ
diff --git a/src/tetris/gui/images/ImageRes.java b/src/tetris/gui/images/ImageRes.java
new file mode 100644
index 0000000..ba44104
--- /dev/null
+++ b/src/tetris/gui/images/ImageRes.java
@@ -0,0 +1,73 @@
+/*
+ * Jesse Morgan <jesterpm@u.washington.edu>
+ *
+ * TCSS 305 - Autumn 2009
+ * Tetris Project
+ * 17 November 2009
+ */
+
+package tetris.gui.images;
+
+import java.awt.Image;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Enumeration of the various image resources.
+ *
+ * @author Jesse Morgan <jesterpm@u.washington.edu>
+ * @version 1.0 23 November 2009
+ */
+public final class ImageRes {
+ /**
+ * Game board background image.
+ */
+ public static final String GAME_BACKGROUND = "background.png";
+
+ /**
+ * Game over board background image.
+ */
+ public static final String GAMEOVER_BACKGROUND = "gameover_background.png";
+
+ /**
+ * Tetris block.
+ */
+ public static final String TETRIS_BLOCK = "square.png";
+
+ /**
+ * Game Over wording.
+ */
+ public static final String GAME_OVER_LABEL = "gameover.png";
+
+ /**
+ * Game Over wording.
+ */
+ public static final String PAUSED_LABEL = "paused.png";
+
+ /**
+ * Writing Pencil.
+ */
+ public static final String WRITING_PENCIL = "writing_pencil.png";
+
+ /**
+ * You're not allowed to create an instance of this class.
+ */
+ private ImageRes() { }
+
+ /**
+ * Utility to handle loading an image.
+ *
+ * @param the_file The filename we need to load.
+ * @return The loaded Image or null if the image can't be loaded.
+ */
+ public static Image loadImage(final String the_file) {
+ try {
+ return ImageIO.read(ImageRes.class.
+ getResourceAsStream(the_file));
+
+ } catch (final IOException the_exception) {
+ return null;
+ }
+ }
+}
diff --git a/src/tetris/gui/images/background.png b/src/tetris/gui/images/background.png
new file mode 100644
index 0000000..39b58bf
--- /dev/null
+++ b/src/tetris/gui/images/background.png
Binary files differ
diff --git a/src/tetris/gui/images/gameover.png b/src/tetris/gui/images/gameover.png
new file mode 100644
index 0000000..f8c6c48
--- /dev/null
+++ b/src/tetris/gui/images/gameover.png
Binary files differ
diff --git a/src/tetris/gui/images/gameover_background.png b/src/tetris/gui/images/gameover_background.png
new file mode 100644
index 0000000..25b7cd5
--- /dev/null
+++ b/src/tetris/gui/images/gameover_background.png
Binary files differ
diff --git a/src/tetris/gui/images/paused.png b/src/tetris/gui/images/paused.png
new file mode 100644
index 0000000..3d26abe
--- /dev/null
+++ b/src/tetris/gui/images/paused.png
Binary files differ
diff --git a/src/tetris/gui/images/square.png b/src/tetris/gui/images/square.png
new file mode 100644
index 0000000..75fc613
--- /dev/null
+++ b/src/tetris/gui/images/square.png
Binary files differ
diff --git a/src/tetris/gui/images/writing_pencil.png b/src/tetris/gui/images/writing_pencil.png
new file mode 100644
index 0000000..f4250de
--- /dev/null
+++ b/src/tetris/gui/images/writing_pencil.png
Binary files differ