import java.io.IOException; import java.io.InputStream; import javax.microedition.lcdui.game.GameCanvas; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Font; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image; import javax.microedition.media.Manager; import javax.microedition.media.Player; import javax.microedition.media.MediaException; import javax.microedition.rms.RecordStore; import java.util.Random; public class FoodChainCanvas extends GameCanvas implements Runnable { // Game Constants private final int NR_OF_HIGHSCORES = 5; private final int NR_OF_FISHCLASSES = 5; private final int PLAYER_MAXSPEED = 3; private final int MAX_NR_OF_POISONFISHES = 7; private final int MAX_NR_OF_ENEMYFISHES = 7; private final int MAX_NR_OF_JELLYFISHES = 7; private final int MAX_NR_OF_MINES = 7; private final int MAX_NR_OF_SEAHORSES = 15; private final int MAP_CHANGE_LEVEL = 4; // LevelInfo: enemyfishes, poisonfishes, jellyfishes, mines and seahorses (bonuslevel) private final int[][] levelInfo = { {5, 2, 2, 1, 0}, {5, 4, 4, 2, 0}, { 7, 2, 2, 2, 0}, {7, 4, 4, 4, 0} , {7, 7, 7, 7, 0}, {0, 0, 0, 0, 15}}; // LevelCodes the last one is for the bonus level private final String[] levelCode = { "uno", "ball", "bermuda" , "quattro", "antz", "seacow"}; private Display display; private long frameDelay; private boolean sleeping; private boolean sound; private boolean music; private boolean gameOver; private Image lifeImage; private Image[] enemyfishImage; private PlayerFishSprite playerSprite; private MineSprite[] mineSprite; private JellyfishSprite[] jellyfishSprite; private PoisonfishSprite[] poisonfishSprite; private EnemyfishSprite[] enemyfishSprite; private EnemyfishSprite[] seahorseSprite; private Player musicPlayer; private Player bonusPlayer; private Player minePlayer; private Player gameOverPlayer; private Player eatenPlayer; private Random rand; private int[] highScores; private int level; private int playerLevel; private int nrOfEnemyfishes; private int nrOfPoisonfishes; private int nrOfJellyfishes; private int nrOfMines; private int nrOfSeahorses; private int skipToLevel = -1; private int inputDelay; public FoodChainCanvas(Display d) { super(true); // Fullscreen mode this.setFullScreenMode(true); display = d; // Set the frame rate (30 fps) frameDelay = 33; // Initialize the input delay inputDelay = 0; // Initialize the randomizer rand = new Random(); // Read settings for music and sound from JMS getOptions(); } public void startAtLevel(int theLevel) { skipToLevel = theLevel; start(); } public void start() { // Set the canvas as the current screen display.setCurrent(this); // Load highscores loadHighScores(); // Initialize the sprites try { // To slow down the frame-animation int[] mineSeq = { 0, 0, 0, 0, 1, 1, 1, 1}; int[] jellyfishSeq = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9 }; int[] poisonfishSeq = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 2, 2, 2, 1, 1, 1}; int[] seahorseSeq = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 1, 1}; lifeImage = Image.createImage("/LifeIcon.png"); mineSprite = new MineSprite[MAX_NR_OF_MINES]; for (int i = 0; i < MAX_NR_OF_MINES; i++) { mineSprite[i] = new MineSprite(Image.createImage("/Mine.png"), 27, 23, 1, 1); mineSprite[i].setFrameSequence(mineSeq); } jellyfishSprite = new JellyfishSprite[MAX_NR_OF_JELLYFISHES]; for (int i = 0; i < MAX_NR_OF_JELLYFISHES; i++) { jellyfishSprite[i] = new JellyfishSprite(Image.createImage("/Jellyfish.png"), 27, 46, 1, 2); jellyfishSprite[i].setFrameSequence(jellyfishSeq); } poisonfishSprite = new PoisonfishSprite[MAX_NR_OF_POISONFISHES]; for (int i = 0; i < MAX_NR_OF_POISONFISHES; i++) { poisonfishSprite[i] = new PoisonfishSprite(Image.createImage("/Poisonfish.png"), 12, 12, 1, 1); poisonfishSprite[i].setFrameSequence(poisonfishSeq); } enemyfishImage = new Image[NR_OF_FISHCLASSES]; for (int i = 0; i < enemyfishImage.length; i++) enemyfishImage[i] = Image.createImage("/Fish0" + Integer.toString(i + 1) + ".png"); enemyfishSprite = new EnemyfishSprite[MAX_NR_OF_ENEMYFISHES]; for (int i = 0; i < MAX_NR_OF_ENEMYFISHES; i++) enemyfishSprite[i] = new EnemyfishSprite(enemyfishImage[0], 1, 1, 1, 1, true); seahorseSprite = new EnemyfishSprite[MAX_NR_OF_SEAHORSES]; for (int i = 0; i < MAX_NR_OF_SEAHORSES; i++) { seahorseSprite[i] = new EnemyfishSprite(Image.createImage("/Seahorse.png"), 12, 20, 3, 1); seahorseSprite[i].setFrameSequence(seahorseSeq); } playerSprite = new PlayerFishSprite(enemyfishImage[1], PLAYER_MAXSPEED); } catch (IOException ioe) { System.err.println("Could not load images!"); } // Initialize the music- and sound-players openPlayers(); // Start a new game newGame(); // Start the animation thread sleeping = false; Thread t = new Thread(this); t.start(); t = null; } private void closePlayers() { // Stop music player try { musicPlayer.stop(); bonusPlayer.stop(); musicPlayer.close(); bonusPlayer.close(); minePlayer.close(); gameOverPlayer.close(); eatenPlayer.close(); } catch (MediaException me) { System.out.println("MediaException occured in pause()"); } } private void openPlayers() { // Initialize the music and wave players try { InputStream is = getClass().getResourceAsStream("ThemeSong.mid"); musicPlayer = Manager.createPlayer(is, "audio/midi"); musicPlayer.prefetch(); musicPlayer.setLoopCount(-1); is = getClass().getResourceAsStream("BonusStage.mid"); bonusPlayer = Manager.createPlayer(is, "audio/midi"); bonusPlayer.prefetch(); is = getClass().getResourceAsStream("MineExplosion.wav"); minePlayer = Manager.createPlayer(is, "audio/X-wav"); minePlayer.prefetch(); is = getClass().getResourceAsStream("Eaten.wav"); eatenPlayer = Manager.createPlayer(is, "audio/X-wav"); eatenPlayer.prefetch(); is = getClass().getResourceAsStream("GameOver.wav"); gameOverPlayer = Manager.createPlayer(is, "audio/X-wav"); gameOverPlayer.prefetch(); } catch (IOException ioe) { System.out.println("IOException occured while trying to initialize the music"); } catch (MediaException me) { System.out.println("MediaException occured while trying to initialize the music"); System.out.println(me.getMessage()); } } public void pause() { closePlayers(); saveHighScores(); // Stop the animation sleeping = true; } public void destroy() { saveHighScores(); } public void continueGame() { openPlayers(); sleeping = false; display.setCurrent(this); getOptions(); if (music) { try { musicPlayer.start(); } catch (MediaException me) { } } Thread t = new Thread(this); t.start(); t = null; } public void run() { // The main loop while (!sleeping) { update(); draw(getGraphics()); try { Thread.sleep(frameDelay); } catch (InterruptedException ie) { System.out.println("An InterrupedException occurred in the run-method: Thread.sleep"); } } } public void update() { // Check to see whether the game is being restarted if (gameOver) { int keyState = getKeyStates(); if ((keyState & FIRE_PRESSED) != 0) { // Start a new game newGame(); } // The game is over, so don't update anything return; } if (++inputDelay > 2) { // Process user input to move the players fishsprite int keyState = getKeyStates(); if (keyState == 0) playerSprite.update(); else { if ((keyState & LEFT_PRESSED) != 0) { playerSprite.update(1, -1); } else if ((keyState & RIGHT_PRESSED) != 0) { playerSprite.update(1, 1); } if ((keyState & UP_PRESSED) != 0) { playerSprite.update(2, -1); } else if ((keyState & DOWN_PRESSED) != 0) { playerSprite.update(2, 2); } //Brake ability else if ((keyState & FIRE_PRESSED) != 0) { playerSprite.brake(); } } inputDelay = 0; } else playerSprite.update(); // Collision detection, pixel-based is "inbakad" in the for-loops // Update the mines for (int i = 0; i < nrOfMines; i++) { mineSprite[i].update(); // If the mine has reach the bottom of the screen "restart" it at the top if (mineSprite[i].getY() > getHeight()) { resetDriftingSprite(mineSprite[i]); playerSprite.updateScore(35); } // Collision detection with mines if (mineSprite[i].collidesWith(playerSprite, true)) { if (playerSprite.lostLife()) { gameOver(); return; } else { if (sound) try { minePlayer.start(); } catch (MediaException me) { System.out.println("MediaException playing MineExplosion"); } resetSpritesToDefaultPositions(); return; } } } // Update the jellyfishes for (int i = 0; i < nrOfJellyfishes; i++) { jellyfishSprite[i].update(); // If the jellyfish has reach the top of the screen "restart" it at the bottom if (jellyfishSprite[i].getY() < -jellyfishSprite[i].getHeight()) { resetDriftingSprite(jellyfishSprite[i]); playerSprite.updateScore(10); } // Collision detection with jellyfishes is done without pixelbased if (jellyfishSprite[i].collidesWith(playerSprite, true)) { playerSprite.updateScore(-10); } } // The members of EnemyfishSprite must be able to know the position of the player int playerXPos = playerSprite.getX(); int playerYPos = playerSprite.getY(); // Update the poisonfishes for (int i = 0; i < nrOfPoisonfishes; i++) { poisonfishSprite[i].update(playerXPos, playerYPos); // Collision detection with poisonfishes if (poisonfishSprite[i].collidesWith(playerSprite, true)) { playerSprite.poisonfishEaten(); resetFish(poisonfishSprite[i]); } // Check if the poisonfish has reach the end of the screen checkBounds(poisonfishSprite[i]); } // Update the enemyfishes for (int i = 0; i < nrOfEnemyfishes; i++) { enemyfishSprite[i].update(playerXPos, playerYPos); // Collision detection with enemyfishes if (enemyfishSprite[i].collidesWith(playerSprite, true)) { if (enemyfishSprite[i].getFishSize() > playerLevel) { if (playerSprite.lostLife()) { gameOver(); return; } else { if (sound) { try { eatenPlayer.start(); } catch (MediaException me) { System.out.println("MediaException playing Eaten"); } } resetSpritesToDefaultPositions(); return; } } else { // if these points make a level-up change image for playerSprite if (playerSprite.experienceRecieved(11)) { playerLevel++; playerSprite.setImage(enemyfishImage[playerLevel], enemyfishImage[playerLevel].getWidth(), enemyfishImage[playerLevel].getHeight()); } playerSprite.updateScore(25); resetFish(enemyfishSprite[i]); } } // Check if the enemyfish has reach the end of the screen checkBounds(enemyfishSprite[i]); } // Update the seahorses for (int i = 0; i < nrOfSeahorses; i++) { seahorseSprite[i].update(playerXPos, playerYPos); // Collision detection with seahorses if (seahorseSprite[i].collidesWith(playerSprite, true)) { playerSprite.updateScore(175); resetFish(seahorseSprite[i]); } // Check if the poisonfish has reach the end of the screen checkBounds(seahorseSprite[i]); // Check if bonusplayer has stopped (returned to prefetchedstate) if (bonusPlayer.getState() == Player.PREFETCHED) { gameOver(); return; } } // Let the players fish wrap around the screen if (playerSprite.getX() < -playerSprite.getWidth()) playerSprite.setPosition(getWidth(), playerSprite.getY()); else if (playerSprite.getX() > getWidth()) playerSprite.setPosition(-playerSprite.getWidth(), playerSprite.getY()); if (playerSprite.getY() < -playerSprite.getHeight()) playerSprite.setPosition(playerSprite.getX(), getHeight()); else if (playerSprite.getY() > getHeight()) playerSprite.setPosition(playerSprite.getX(), -playerSprite.getHeight()); int playerScore = playerSprite.getScore(); // playerLevel == 4 changes map on the 3rd-levelup if (playerLevel == MAP_CHANGE_LEVEL) { nextLevel(); } } private void checkBounds(EnemyfishSprite sprite) { if ((sprite.getX() + sprite.getWidth()) < 0) resetFish(sprite); else if (sprite.getX() > getWidth() && sprite.getDirection() != 2) resetFish(sprite); } private void gameOver() { gameOver = true; if (sound) try { musicPlayer.stop(); gameOverPlayer.start(); } catch (MediaException me) { System.out.println("MediaException playing GameOver sound"); } updateHighScores(); } private void draw(Graphics g) { // Draw the (water)background g.setColor(9, 187, 247); g.fillRect(0, 0, getWidth(), getHeight()); // Draw the Sprites for (int i = 0; i < nrOfPoisonfishes; i++) poisonfishSprite[i].paint(g); for (int i = 0; i < nrOfJellyfishes; i++) jellyfishSprite[i].paint(g); for (int i = 0; i < nrOfMines; i++) mineSprite[i].paint(g); for (int i = 0; i < nrOfEnemyfishes; i++) enemyfishSprite[i].paint(g); for (int i = 0; i < nrOfSeahorses; i++) seahorseSprite[i].paint(g); // Draw the Player playerSprite.paint(g); for (int i = 0; i < playerSprite.getLives(); i++) g.drawImage(lifeImage, getWidth() - (lifeImage.getWidth() * i) - (1 * (i + 1)), 2, Graphics.TOP | Graphics.RIGHT); int imageWidth = lifeImage.getWidth() * 3; g.setColor(0, 0, 0); // black g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL)); g.drawString(" " + playerSprite.getScore(), 0, 0, Graphics.TOP | Graphics.LEFT); g.setColor(0, 0, 255); //blue g.drawRect(60, 2, 60, 10); g.setColor(0, 255, 0); g.fillRect(61, 3, playerSprite.getExperience(), 9); // Draw gameover information if (gameOver) { if (playerSprite.getLives() != 0) { g.setColor(0, 255, 0); // Green g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE)); g.drawString("Game Completed!!!", 90, 60, Graphics.TOP | Graphics.HCENTER); g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL)); g.drawString("Secret Level: " + levelCode[levelCode.length - 1], getWidth() / 2, 130, Graphics.TOP | Graphics.HCENTER); } else { g.setColor(255, 0, 0); // Red g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE)); g.drawString("Game Over!!!", 90, 80, Graphics.TOP | Graphics.HCENTER); g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL)); g.drawString("Level " + Integer.toString((level + 1)), getWidth() / 2, 110, Graphics.TOP | Graphics.HCENTER); g.drawString("Level Code: " + levelCode[level], getWidth() / 2, 130, Graphics.TOP | Graphics.HCENTER); } } // Flush the offscreen graphics buffer flushGraphics(); } private void newGame() { // Initialize the game variables gameOver = false; if (skipToLevel != -1) { level = skipToLevel; skipToLevel = -1; } else level = 0; playerLevel = 1; nrOfEnemyfishes = levelInfo[level][0]; nrOfPoisonfishes = levelInfo[level][1]; nrOfJellyfishes = levelInfo[level][2]; nrOfMines = levelInfo[level][3]; nrOfSeahorses = levelInfo[level][4]; playerSprite.newGame(); // Reset all the Sprites to default positions resetSpritesToDefaultPositions(); // If music is enabled, start the MusicPlayer if (music) { try { if (level != 5) musicPlayer.start(); else bonusPlayer.start(); } catch (MediaException me) { } } } private void nextLevel() { level++; playerLevel = 1; if (level >= levelInfo.length) { gameOver(); } else { nrOfEnemyfishes = levelInfo[level][0]; nrOfPoisonfishes = levelInfo[level][1]; nrOfJellyfishes = levelInfo[level][2]; nrOfMines = levelInfo[level][3]; nrOfSeahorses = levelInfo[level][4]; playerSprite.newLevel(); resetSpritesToDefaultPositions(); } } private void resetSpritesToDefaultPositions() { playerSprite.setImage(enemyfishImage[playerLevel], enemyfishImage[playerLevel].getWidth(), enemyfishImage[playerLevel].getHeight()); playerSprite.setPosition((getWidth() - playerSprite.getWidth()) / 2, (getHeight() - playerSprite.getHeight()) / 2); for (int i = 0; i < nrOfMines; i++) resetDriftingSprite(mineSprite[i]); for (int i = 0; i < nrOfJellyfishes; i++) resetDriftingSprite(jellyfishSprite[i]); for (int i = 0; i < nrOfPoisonfishes; i++) { resetFish(poisonfishSprite[i]); poisonfishSprite[i].setSpeed(Math.abs(rand.nextInt() % 4) + 1); poisonfishSprite[i].setVisible(true); } for (int i = 0; i < nrOfEnemyfishes; i++) { if (Math.abs(rand.nextInt() % 3) < 2) { enemyfishSprite[i].setImage(enemyfishImage[playerLevel - 1], enemyfishImage[playerLevel - 1].getWidth(), enemyfishImage[playerLevel - 1].getHeight()); enemyfishSprite[i].setFishSize(playerLevel - 1); } else { enemyfishSprite[i].setImage(enemyfishImage[playerLevel + 1], enemyfishImage[playerLevel + 1].getWidth(), enemyfishImage[playerLevel + 1].getHeight()); enemyfishSprite[i].setFishSize(playerLevel + 1); } resetFish(enemyfishSprite[i]); } for (int i = 0; i < nrOfSeahorses; i++) resetFish(seahorseSprite[i]); } private void resetFish(EnemyfishSprite sprite) { // To not change size on the poisonfishes which have a fishsize of -1 if (sprite.getFishSize() != -1) { if ((sprite.getFishSize() != (playerLevel - 1)) || (sprite.getFishSize() != (playerLevel + 1))) { if (Math.abs(rand.nextInt() % 3) < 2) { sprite.setImage(enemyfishImage[playerLevel - 1], enemyfishImage[playerLevel - 1].getWidth(), enemyfishImage[playerLevel - 1].getHeight()); sprite.setFishSize(playerLevel - 1); } else { sprite.setImage(enemyfishImage[playerLevel + 1], enemyfishImage[playerLevel + 1].getWidth(), enemyfishImage[playerLevel + 1].getHeight()); sprite.setFishSize(playerLevel + 1); } } } if (Math.abs(rand.nextInt() % 2) == 1) { sprite.setPosition(0 - sprite.getWidth(), Math.abs(rand.nextInt() % getHeight())); sprite.setDirection(1); } else { sprite.setPosition(getWidth() + sprite.getWidth(), Math.abs(rand.nextInt() % (getHeight() - sprite.getHeight()))); sprite.setDirection(2); } } private void resetDriftingSprite(DriftingSprite sprite) { if (sprite.getDirection() == 1) { sprite.setPosition(Math.abs(rand.nextInt() % (getWidth() - sprite.getWidth())), 0 - sprite.getHeight() - (Math.abs(rand.nextInt() % 100))); } else { sprite.setPosition(Math.abs(rand.nextInt() % (getWidth() - sprite.getWidth())), getHeight() + (Math.abs(rand.nextInt() % 100))); } } private void getOptions() { // Open the Options RecordStore RecordStore rs = null; try { rs = RecordStore.openRecordStore("Options", false); } catch (Exception e) { } if (rs != null) { String[] options = new String[2]; // Read the Options records try { int len; byte[] recordData = new byte[8]; for (int i = 1; i <= rs.getNumRecords(); i++) { // Re-allocate record holder if necessary if (rs.getRecordSize(i) > recordData.length) recordData = new byte[rs.getRecordSize(i)]; // Read the option and store it in the option array len = rs.getRecord(i, recordData, 0); options[i - 1] = new String(recordData, 0, len); } } catch (Exception e) { System.err.println("Exception in getOptions() Reading!"); } // Close the record store try { rs.closeRecordStore(); } catch (Exception e) { System.err.println("Exception in getOptions() Closing!"); } for (int i = 0; i < options.length; i++) { if (options[i].startsWith("Music")) { if (options[i].endsWith("on")) music = true; else music = false; } else if (options[i].startsWith("Sound")) { if (options[i].endsWith("on")) sound = true; else sound = false; } } } else { music = true; sound = true; } } private void loadHighScores() { highScores = new int[NR_OF_HIGHSCORES]; // Open the HighScores RecordStore RecordStore rs = null; try { rs = RecordStore.openRecordStore("HighScores", false); } catch (Exception e) { } if (rs != null) { // Read the HighScore records try { int len; byte[] recordData = new byte[8]; for (int i = 1; i <= rs.getNumRecords(); i++) { // Re-allocate record holder if necessary if (rs.getRecordSize(i) > recordData.length) recordData = new byte[rs.getRecordSize(i)]; // Read the score and store it in the hi score array len = rs.getRecord(i, recordData, 0); highScores[i - 1] = (Integer.parseInt(new String(recordData, 0, len))); } } catch (Exception e) { System.err.println("Exception in loadHighScores() Reading!"); } // Close the record store try { rs.closeRecordStore(); } catch (Exception e) { System.err.println("Exception in loadHighScores() Closing!"); } } else { // The RecordStore doesn't exist, so initialize the scores to 0 for (int i = 0; i < NR_OF_HIGHSCORES; i++) highScores[i] = 0; } } private void saveHighScores() { // Delete the previous HighScores RecordStore try { RecordStore.deleteRecordStore("HighScores"); } catch (Exception e) { } // Create the new hi scores record store RecordStore rs = null; try { rs = RecordStore.openRecordStore("HighScores", true); } catch (Exception e) { System.err.println("Exception in saveHighScores() Creating!"); } // Write the scores for (int i = 0; i < NR_OF_HIGHSCORES; i++) { // Format each score for writing byte[] recordData = Integer.toString(highScores[i]).getBytes(); try { // Write the score as a record rs.addRecord(recordData, 0, recordData.length); } catch (Exception e) { System.err.println("Exception in saveHighScores() Writing!"); } } // Close the record store try { rs.closeRecordStore(); } catch (Exception e) { System.err.println("Exception in saveHighScores() Closing!"); } } private void updateHighScores() { // See whether the current score made the HighScore list int i; for (i = 0; i < NR_OF_HIGHSCORES; i++) if (playerSprite.getScore() > highScores[i]) break; // Insert the current score into the HighScore list if (i < NR_OF_HIGHSCORES) { for (int j = (NR_OF_HIGHSCORES - 1); j > i; j--) { highScores[j] = highScores[j - 1]; } highScores[i] = playerSprite.getScore(); } } }