/* * @(#)GameDemo.java 1.6 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package basis.demos; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import basis.Builder; public class GameDemo extends Demo implements KeyListener, FocusListener { private static final int OUTSIDE = 0; private static final int INSIDE = 1; private static final int ACTIVE = 2; private static final int FIXED = 3; private static final int SPARE = 4; private static final int BLANK = 5; private static final Color COLOR_OUTSIDE = Builder.SUN_YELLOW; private static final Color COLOR_INSIDE = Builder.SUN_BLUE; private static final Color COLOR_ACTIVE = Builder.SUN_RED; private static final Color COLOR_SPARE = Builder.SUN_LIGHTBLUE; private static final Color COLOR_BLANK = Color.white; private static final int DEFAULT_DELAY = 101; private static final int DELAY_INCREMENT = 10; private static final int DEFAULT_LIVES = 3; private static final Color[] winnerColors = { Builder.SUN_RED, Builder.SUN_BLUE }; private int demoWidth; private int demoHeight; private int rows = 30; private int cols = 30; private int[][] cells = new int[rows][cols]; private int cellWidth; private int cellHeight; private int target; private int level = 1; private int total; private int delay = DEFAULT_DELAY; private int lives = DEFAULT_LIVES; private boolean initialized; private boolean stopped; // player stopped by user private boolean paused; // game paused by user private boolean aborted; // game aborted by user private boolean halted; // game halted by system private boolean crashed; private boolean gameover; private boolean showHighScores; private boolean showHelpStrings; private Animator animator = new Animator(); private Player player; private ArrayList enemies; private ArrayList highScores = new ArrayList(); private ArrayList helpStrings = new ArrayList(); private Image pausedImage; private Image abortedImage; private Image gameoverImage; private boolean newGame; private boolean winner; private int percentageArea; private int percentageTime; private long startTime; private long pausedTime; private long haltedTime; private boolean warmed; public GameDemo() { // start early to "warm up" animator.start(); pausedImage = ImageDemo.loadImage(this, "images/paused.gif"); abortedImage = ImageDemo.loadImage(this, "images/aborted.gif"); gameoverImage = ImageDemo.loadImage(this, "images/gameover.gif"); addKeyListener(this); addFocusListener(this); addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { requestFocus(); } }); helpStrings.add("n/1-9 New game"); helpStrings.add("arrows Move"); helpStrings.add("i/j/k/l Move"); helpStrings.add("space Stop"); helpStrings.add("p Pause"); helpStrings.add("a/ESC Abort"); helpStrings.add("s/- Slower"); helpStrings.add("d/= Default"); helpStrings.add("f/+ Faster"); helpStrings.add("z Highscores"); helpStrings.add("h Help"); loadHighScores(); } public void paint(Graphics g) { requestFocus(); if (winner) { return; } Dimension d = getSize(); demoWidth = d.width; demoHeight = d.height; cellWidth = demoWidth / cols; cellHeight = demoHeight / (rows + 1); if (!initialized) { play(false); halted = true; showHelpStrings = true; initialized = true; } if (showHelpStrings) { drawStrings(g, "Jonix", helpStrings); return; } if (showHighScores) { loadHighScores(); drawStrings(g, "High Scores", highScores, 10); return; } for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { if (cells[row][col] == OUTSIDE) { g.setColor(COLOR_OUTSIDE); } if (cells[row][col] == INSIDE) { g.setColor(COLOR_INSIDE); } if (cells[row][col] == ACTIVE) { g.setColor(COLOR_ACTIVE); } if (cells[row][col] == SPARE) { g.setColor(COLOR_SPARE); } if (cells[row][col] == BLANK) { g.setColor(COLOR_BLANK); } g.fillRect(col * cellWidth, row * cellHeight, cellWidth, cellHeight); if (cells[row][col] == SPARE) { g.setColor(COLOR_BLANK); g.drawRect(col * cellWidth, row * cellHeight, cellWidth - 1, cellHeight - 1); } } } player.draw(g); for (int i = 0; i < enemies.size(); i++) { Enemy enemy = (Enemy) enemies.get(i); enemy.draw(g); } if (paused || aborted || gameover) { Image image = pausedImage; if (aborted) { image = abortedImage; } if (gameover) { image = gameoverImage; } ((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); g.drawImage(image, demoWidth / 10, 4 * demoHeight / 9, 8 * demoWidth / 10, demoHeight / 9, this); } drawPercentageTime(); } private void drawStrings(Graphics g, String heading, ArrayList strings) { drawStrings(g, heading, strings, strings.size()); } private void drawStrings(Graphics g, String heading, ArrayList strings, int number) { g.setColor(COLOR_OUTSIDE); g.fillRect(0, 0, demoWidth, demoHeight); String longest = ""; for (int i = 0; i < strings.size(); i++) { String s = (String) strings.get(i); if (s.length() > longest.length()) { longest = s; } } int fontSize = demoHeight / (number + 2) - 2; g.setColor(Builder.SUN_RED); int headingFontSize = 2 * fontSize; int fw = 0; while (true) { Font font = new Font("Monospaced", Font.BOLD, headingFontSize); g.setFont(font); FontMetrics fm = g.getFontMetrics(font); fw = fm.stringWidth(heading); if (fw < demoWidth) { break; } headingFontSize -= 4; } g.drawString(heading, (demoWidth - fw) / 2, headingFontSize); g.setColor(Builder.SUN_BLUE); Font font = new Font("Monospaced", Font.PLAIN, fontSize); g.setFont(font); FontMetrics fm = g.getFontMetrics(font); fw = fm.stringWidth(longest); for (int i = 0; i < strings.size() && i < number; i++) { String s = (String) strings.get(i); g.drawString(s, (demoWidth - fw) / 2, (i + 3) * fontSize); } } private void play(boolean preserve) { if (preserve) { for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { if (cells[row][col] == ACTIVE) { cells[row][col] = INSIDE; } } } } else { target = 0; for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { if (row < rows / 10 || row > 9 * rows / 10 || row < 3 || row >= rows - 3 || col < cols / 10 || col > 9 * cols / 10 || col < 3 || col >= cols - 3) { cells[row][col] = OUTSIDE; } else { cells[row][col] = INSIDE; target++; } } } int col = cols / 2 - DEFAULT_LIVES / 2; for (int c = 0; c < DEFAULT_LIVES; c++) { if (c < lives) { cells[0][col + c] = SPARE; } else { cells[0][col + c] = BLANK; } } } player = new Player(); ArrayList newEnemies = new ArrayList(); if (preserve) { for (int i = 0; i < enemies.size(); i++) { Enemy enemy = (Enemy) enemies.get(i); if (enemy.inside) { newEnemies.add(enemy); } } } for (int i = 0; i < level; i++) { newEnemies.add(new Enemy(false)); if (!preserve) { newEnemies.add(new Enemy(true)); } } enemies = newEnemies; percentageArea = percentageArea(); setStatus("Level " + level + " Score " + (total + percentageArea)); startTime = System.currentTimeMillis(); percentageTime = 100; crashed = false; aborted = false; gameover = false; } private void crash() { halted = true; lives--; percentageArea = percentageArea(); setStatus("Level " + level + " Score " + (total + percentageArea)); if (lives > 0) { repaint(); sleep(3000); // In case user aborted during delay... if (!aborted) { play(true); halted = false; repaint(); } return; } total += percentageArea; if (total > 0) { String scoreString = "" + total; while (scoreString.length() < 3) { scoreString = " " + scoreString; } boolean newHighScore = false; boolean inserted = false; for (int i = 0; i < highScores.size(); i++) { String s = (String) highScores.get(i); s = s.trim(); try { int score = Integer.parseInt(s); if (total >= score) { highScores.add(i, scoreString); inserted = true; if (i == 0) { newHighScore = true; } break; } } catch (NumberFormatException nfe) { } } if (!inserted) { highScores.add(scoreString); if (highScores.size() == 1) { newHighScore = true; } } while (highScores.size() > 10) { highScores.remove(highScores.size() - 1); } saveHighScores(); if (newHighScore) { sleep(1000); winner(); showHighScores = true; repaint(); } } level = 1; gameover = true; repaint(); } private int percentageArea() { if (target == 0) { return 0; } int todo = 0; for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { if (cells[row][col] == INSIDE || cells[row][col] == ACTIVE) { todo++; } } } return (target - todo) * 100 / target; } private int percentageTime() { long currentTime = System.currentTimeMillis(); int lapsedTime = (int) ((currentTime - startTime) / 1000); int totalTime = 60 * level; int x = 100 * (totalTime - lapsedTime) / totalTime; x = x < 0 ? 0 : x; return x; } private void fix(int row, int col) { if (row < 0 || row >= rows) { return; } if (col < 0 || col >= cols) { return; } if (cells[row][col] != INSIDE) { return; } cells[row][col] = FIXED; fix(row - 1, col - 1); fix(row - 1, col); fix(row - 1, col + 1); fix(row, col - 1); fix(row, col + 1); fix(row + 1, col - 1); fix(row + 1, col); fix(row + 1, col + 1); } private void winner() { winner = true; Graphics g = getGraphics(); String string = "High Score!"; int fontSize = demoWidth / string.length(); fontSize = fontSize > demoHeight / 2 ? demoHeight / 2 : fontSize; int fw = 0; while (true) { Font font = new Font("SansSerif", Font.BOLD, fontSize); g.setFont(font); FontMetrics fm = g.getFontMetrics(font); fw = fm.stringWidth(string); if (fw < demoWidth) { break; } fontSize -= 4; } int color = 0; for (int i = 0; i < 300; i++) { if (!hasFocus()) { break; } if (i % 10 == 0) { g.setColor(winnerColors[color++ % winnerColors.length]); g.fillRect(0, 0, demoWidth, demoHeight); } g.setColor(randomColor()); g.drawString(string, demoWidth / 2 - fw / 2, demoHeight / 2 + fontSize / 2); sleep(10); } winner = false; } private Color randomColor() { float min = 0.3f; float red = (float) (Math.random() + min); float green = (float) (Math.random() + min); float blue = (float) (Math.random() + min); red = red > 1.0f ? 1.0f : red; green = green > 1.0f ? 1.0f : green; blue = blue > 1.0f ? 1.0f : blue; return new Color(red, green, blue); } private void sleep(int millis) { try { Thread.sleep(millis); } catch (InterruptedException ie) { } startTime += millis; } public void keyPressed(KeyEvent e) { char keyChar = e.getKeyChar(); int keyCode = e.getKeyCode(); long currentTime = e.getWhen(); if (keyChar == 'n') { level = 1; newGame = true; } try { int value = Integer.parseInt("" + keyChar); if (value >= 1 && value <= 9) { level = value; } newGame = true; } catch (NumberFormatException nfe) { } if (newGame) { warmed = true; // all the fields animator waits on... paused = false; aborted = false; halted = false; gameover = false; showHelpStrings = false; showHighScores = false; if (animator.isAlive()) { animator.interrupt(); } return; } int dir = Player.NONE; if (keyChar == 'i' || keyCode == KeyEvent.VK_UP) { dir = Player.UP; } if (keyChar == 'k' || keyCode == KeyEvent.VK_DOWN) { dir = Player.DOWN; } if (keyChar == 'j' || keyCode == KeyEvent.VK_LEFT) { dir = Player.LEFT; } if (keyChar == 'l' || keyCode == KeyEvent.VK_RIGHT) { dir = Player.RIGHT; } if (dir == Player.UP || dir == Player.DOWN || dir == Player.LEFT || dir == Player.RIGHT) { if (player != null) { player.dir = dir; stopped = false; } return; } if (keyChar == 'p') { paused = paused ? false : true; if (paused) { pausedTime = currentTime; } else { if (pausedTime > 0) { startTime += (currentTime - pausedTime); } } repaint(); return; } if (keyCode == KeyEvent.VK_ENTER) { showHelpStrings = false; showHighScores = false; if (halted && haltedTime > 0) { startTime += (currentTime - haltedTime); halted = false; } repaint(); } if (keyChar == ' ') { stopped = true; return; } if (keyChar == '+' || keyChar == 'f') { delay -= DELAY_INCREMENT; if (delay <= 0) { delay = 1; } return; } if (keyChar == '=' || keyChar == 'd') { delay = DEFAULT_DELAY; return; } if (keyChar == '-' || keyChar == 's') { delay += DELAY_INCREMENT; if (delay > 201) { delay = 201; } return; } if (keyChar == 'z') { showHighScores = true; showHelpStrings = false; if (!halted) { halted = true; haltedTime = currentTime; } repaint(); return; } if (keyChar == 'h') { showHelpStrings = true; showHighScores = false; repaint(); if (!halted) { halted = true; haltedTime = currentTime; } return; } if (keyChar == 'a' || keyCode == KeyEvent.VK_ESCAPE) { lives = 0; aborted = true; animator.interrupt(); repaint(); return; } } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } public void focusGained(FocusEvent e) { long currentTime = System.currentTimeMillis(); if (!paused && halted && haltedTime > 0) { startTime += currentTime - haltedTime; haltedTime = 0; halted = false; } } public void focusLost(FocusEvent e) { long currentTime = System.currentTimeMillis(); if (!paused && !halted) { halted = true; haltedTime = currentTime; } } private void loadHighScores() { ArrayList newHighScores = new ArrayList(); try { FileInputStream fis = new FileInputStream("highscores"); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); while (true) { String s = br.readLine(); if (s == null || s.length() <= 0) { break; } newHighScores.add(s); } br.close(); } catch (IOException ioe) { } catch (SecurityException se) { } highScores = newHighScores; } private void saveHighScores() { try { FileOutputStream fos = new FileOutputStream("highscores"); PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos)); for (int i = 0; i < highScores.size(); i++) { String s = (String) highScores.get(i); pw.println(s); } pw.flush(); pw.close(); } catch (IOException ioe) { } } private void drawPercentageTime() { Graphics g = getGraphics(); if (g == null) { return; } int x = 0; int y = rows * cellHeight; int w = cols * cellWidth; int h = demoHeight - y; g.setColor(Color.white); g.fillRect(x, y, w, h); g.setColor(Builder.SUN_RED); g.fillRect(x, y, percentageTime * w / 100, h); g.setColor(Color.black); g.drawRect(x, y, w - 1, h - 1); } class Animator extends Thread { public void run() { setPriority(MAX_PRIORITY); while (true) { while (!warmed) { try { Thread.sleep(delay); } catch (InterruptedException ie) { } } if (newGame) { total = 0; lives = DEFAULT_LIVES; play(false); repaint(); newGame = false; } if (player != null) { player.move(); } if (enemies != null) { for (int i = 0; i < enemies.size(); i++) { Enemy enemy = (Enemy) enemies.get(i); enemy.move(); } } int x = percentageTime(); if (x < percentageTime) { percentageTime = x; drawPercentageTime(); } if (percentageTime <= 0) { crash(); } do { try { Thread.sleep(delay); } catch (InterruptedException ie) { } } while (paused || aborted || halted || gameover || showHelpStrings || showHighScores); } } } class Player { static final int NONE = 0; static final int UP = 1; static final int DOWN = 2; static final int LEFT = 3; static final int RIGHT = 4; Color color = Builder.SUN_RED; int row; int col; int dir = UP; boolean inside; public Player() { stopped = true; for (int row = 0; row < rows; row++) { for (int col = cols - 1; col >= 0; col--) { if (cells[row][col] == SPARE) { cells[row][col] = BLANK; this.row = row; this.col = col; break; } } } } public void move() { if (crashed) { crash(); crashed = false; return; } if (stopped || paused || aborted || halted || gameover || showHelpStrings || showHighScores) { return; } int oldRow = row; int oldCol = col; if (dir == UP) { row = oldRow > 0 ? oldRow - 1 : oldRow; } if (dir == DOWN) { row = oldRow < rows - 1 ? oldRow + 1 : oldRow; } if (dir == LEFT) { col = oldCol > 0 ? oldCol - 1 : oldCol; } if (dir == RIGHT) { col = oldCol < cols - 1 ? oldCol + 1 : oldCol; } if (row != oldRow || col != oldCol) { Graphics g = getGraphics(); if (cells[oldRow][oldCol] == ACTIVE) { g.setColor(COLOR_ACTIVE); } if (cells[oldRow][oldCol] == OUTSIDE) { g.setColor(COLOR_OUTSIDE); } if (cells[oldRow][oldCol] == SPARE) { g.setColor(COLOR_SPARE); } if (cells[oldRow][oldCol] == BLANK) { g.setColor(COLOR_BLANK); } g.fillRect(oldCol * cellWidth, oldRow * cellHeight, cellWidth, cellHeight); if (cells[oldRow][oldCol] == SPARE) { g.setColor(COLOR_BLANK); g.drawRect(oldCol * cellWidth, oldRow * cellHeight, cellWidth - 1, cellHeight - 1); } draw(g); } if (cells[row][col] == ACTIVE) { crashed = true; crash(); return; } if (cells[row][col] == INSIDE) { inside = true; cells[row][col] = ACTIVE; } if (inside && cells[row][col] == OUTSIDE) { percentageTime = percentageTime(); stopped = true; inside = false; halted = true; for (int i = 0; i < enemies.size(); i++) { Enemy enemy = (Enemy) enemies.get(i); fix(enemy.row, enemy.col); } for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { if (cells[row][col] == ACTIVE) { cells[row][col] = OUTSIDE; } if (cells[row][col] == INSIDE) { cells[row][col] = OUTSIDE; } if (cells[row][col] == FIXED) { cells[row][col] = INSIDE; } } } repaint(); percentageArea = percentageArea(); setStatus("Level " + level + " Score " + (total + percentageArea)); if (percentageArea >= 75) { total += percentageArea + percentageTime; setStatus("Level " + level + " Score " + total); level++; sleep(3000); play(false); repaint(); } halted = false; } } void draw(Graphics g) { g.setColor(color); g.fillRect(col * cellWidth, row * cellHeight, cellWidth, cellHeight); g.setColor(Color.black); g.drawRect(col * cellWidth, row * cellHeight, cellWidth - 1, cellHeight - 1); } } class Enemy { static final int SE = 0; static final int SW = 1; static final int NW = 2; static final int NE = 3; int[][] array = { {SE, NE, SW, NW}, {SW, SE, NW, NE}, {NW, SW, NE, SE}, {NE, NW, SE, SW} }; Color color; boolean inside; int row; int col; int dir = (int) (Math.random() * 4); public Enemy(boolean inside) { this.inside = inside; if (inside) { color = COLOR_OUTSIDE; } else { color = COLOR_INSIDE; } while (true) { row = (int) (Math.random() * rows); col = (int) (Math.random() * cols); if (!inside && ((row > rows / 10 && row < 9 * rows / 10) || (col > cols / 10 && col < 9 * cols / 10))) { continue; } if (inside && cells[row][col] == INSIDE) { break; } if (!inside && cells[row][col] == OUTSIDE) { break; } } } public void move() { if (paused || aborted || halted || crashed || gameover || showHelpStrings || showHighScores) { return; } int oldRow = row; int oldCol = col; int oldDir = dir; for (int i = 0; i < 4; i++) { dir = array[oldDir][i]; if (dir == SE) { row = oldRow + 1; col = oldCol + 1; } if (dir == SW) { row = oldRow + 1; col = oldCol - 1; } if (dir == NW) { row = oldRow - 1; col = oldCol - 1; } if (dir == NE) { row = oldRow - 1; col = oldCol + 1; } if (row < 0 || row >= rows || col < 0 || col >= cols) { continue; } if (inside && cells[row][col] != OUTSIDE) { break; } if (!inside && cells[row][col] == OUTSIDE) { break; } } Graphics g = getGraphics(); boolean hit = false; if (inside && cells[player.row][player.col] == INSIDE || !inside && cells[player.row][player.col] == OUTSIDE) { if (row == player.row && col == player.col || row - 1 == player.row && col == player.col || row + 1 == player.row && col == player.col || row == player.row && col - 1 == player.col || row == player.row && col + 1== player.col) { hit = true; } } if (inside) { if (cells[row][col] == ACTIVE || row > 0 && cells[row - 1][col] == ACTIVE || row < rows - 1 && cells[row + 1][col] == ACTIVE || col > 0 && cells[row][col - 1] == ACTIVE || col < cols - 1 &&cells[row][col + 1] == ACTIVE) { hit = true; } } draw(g); if (inside) { g.setColor(COLOR_INSIDE); } else { g.setColor(COLOR_OUTSIDE); } g.fillRect(oldCol * cellWidth, oldRow * cellHeight, cellWidth, cellHeight); if (hit) { crashed = true; } } void draw(Graphics g) { g.setColor(color); g.fillRect(col * cellWidth, row * cellHeight, cellWidth, cellHeight); g.setColor(Color.black); g.drawRect(col * cellWidth, row * cellHeight, cellWidth - 1, cellHeight - 1); } } }