/** * Copyright (c) 2012-2013 Nokia Corporation. All rights reserved. * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. * Oracle and Java are trademarks or registered trademarks of Oracle and/or its * affiliates. Other product and company names mentioned herein may be trademarks * or trade names of their respective owners. * See LICENSE.TXT for license information. */ package com.nokia.example.explonoid; import com.nokia.example.explonoid.audio.AudioManager; import com.nokia.example.explonoid.effects.ElectricArc; import com.nokia.example.explonoid.effects.LightManager; import com.nokia.example.explonoid.effects.Slideable; import com.nokia.example.explonoid.game.Game; import com.nokia.example.explonoid.game.Resources; import com.nokia.example.explonoid.menu.InfoScreen; import com.nokia.example.explonoid.menu.MainMenu; import com.nokia.example.explonoid.menu.Menu; import java.util.Random; import javax.microedition.io.ConnectionNotFoundException; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.game.GameCanvas; import javax.microedition.rms.RecordEnumeration; import javax.microedition.rms.RecordStore; import javax.microedition.rms.RecordStoreException; /** * Main canvas for game. */ public class ExplonoidCanvas extends GameCanvas implements CommandListener { public static final int INTERVAL = 30; private static final int LEFT_SOFTKEY = -6; private static final int RIGHT_SOFTKEY = -7; private static final double DEFAULT_WIDTH = 240; private static final double DEFAULT_HEIGHT = 320; private boolean pressed = false; private float scaling; private int gameState; private int nextState; private int cornerX; private int cornerY; private int displayWidth; private int displayHeight; private int gameWidth; private int gameHeight; private int x = 0; private int y = 0; private GameThread gameThread; private Main main; private MainMenu menu; private InfoScreen info; private Game game; private Slideable currentView; private Slideable targetView; private boolean electricity; private Resources r; private Random rnd = new Random(); private AudioManager audioManager; private Command backCommand; public ExplonoidCanvas(Main main) { super(false); this.setFullScreenMode(true); this.main = main; audioManager = AudioManager.getInstance(); final String keyboard = System.getProperty("com.nokia.keyboard.type"); if (Main.isFullTouchDevice()) { backCommand = new Command("Back", Command.BACK, 0); addCommand(backCommand); setCommandListener(this); } } /** * Creates a new game and loads the previous status of the game, if it exists */ private void loadOrCreateGame() { game = new Game(cornerX, cornerY, gameWidth, gameHeight, r, new Game.Listener() { public void changeState(int state) { switch (state) { case Game.STATE_MENU: showMenu(); break; case Game.STATE_LEVEL: nextLevel(); break; } } public void handleEvent(int event) { switch (event) { case Game.EVENT_BALL_OUT: audioManager.playSample(r.SAMPLE_DEATH); break; case Game.EVENT_BONUS: audioManager.playSample(r.SAMPLE_BONUS); break; case Game.EVENT_BRICK_EXPLOSION: audioManager.playSample(r.SAMPLE_EXPLOSION); break; case Game.EVENT_BRICK_COLLISION: audioManager.playSample(getRndCollisionSample()); break; case Game.EVENT_BUTTON_PRESSED: audioManager.playSample(r.SAMPLE_BUTTON); break; case Game.EVENT_GAME_OVER: // No sound at the moment break; case Game.EVENT_LEVEL_CHANGE: audioManager.playSample(r.SAMPLE_TELEPORT); break; case Game.EVENT_LEVEL_STARTED: menu.showResume(); break; case Game.EVENT_PLATE_COLLISION: audioManager.playSample(getRndCollisionSample()); break; case Game.EVENT_WALL_COLLISION: audioManager.playSample(r.SAMPLE_WALL); break; } } }); try { //RecordStore.deleteRecordStore("GameSnapshot"); // Clear state data for testing purposes RecordStore snapshot = RecordStore.openRecordStore("GameSnapshot", true); if (snapshot.getNumRecords() == 0 || !game.load(snapshot.getRecord(getRecordId(snapshot)))) { game.newGame(); } else { menu.showResume(); } snapshot.closeRecordStore(); } catch (RecordStoreException e) { game.newGame(); } catch (NumberFormatException nfe) { nfe.printStackTrace(); } showMenu(); } public void commandAction(Command c, Displayable d) { if (c == backCommand) { if (gameState == Game.STATE_MENU) { main.exit(); } else { showMenu(); } } } /** * Returns the name of a randomly selected collision sample * @return The sample name in a String */ public String getRndCollisionSample() { switch (rnd.nextInt(2)) { case 0: return r.SAMPLE_COLLISION; case 1: return r.SAMPLE_COLLISION2; case 2: return r.SAMPLE_COLLISION3; default: return r.SAMPLE_COLLISION; } } /** * Save the state of the game to RecordStore */ public void saveGame() { if (game == null) { return; } try { RecordStore snapshot = RecordStore.openRecordStore("GameSnapshot", true); if (snapshot.getNumRecords() == 0) { snapshot.addRecord(null, 0, 0); } byte[] data = game.getSnapshot(); snapshot.setRecord(getRecordId(snapshot), data, 0, data.length); snapshot.closeRecordStore(); System.out.println("state saved succesfully"); } catch (RecordStoreException e) { System.out.println("exception occured while saving"); } } /** * Get the first Record ID for a given record store */ private int getRecordId(RecordStore store) throws RecordStoreException { RecordEnumeration e = store.enumerateRecords(null, null, false); try { return e.nextRecordId(); } finally { e.destroy(); } } /** * Check the state of keys */ private void checkKeys() { int keyState = getKeyStates(); if (gameState == Game.STATE_LEVEL) { if ((keyState & LEFT_PRESSED) != 0) { game.movePlateLeft(); } else if ((keyState & RIGHT_PRESSED) != 0) { game.movePlateRight(); } } } /** * Handles the key pressed events * @param key */ protected void keyPressed(int key) { switch (gameState) { case Game.STATE_MENU: switch (getGameAction(key)) { case UP: menu.selectPrev(); break; case DOWN: menu.selectNext(); break; case FIRE: menu.clickSelected(); } break; case Game.STATE_INFO: switch (getGameAction(key)) { case UP: info.selectPrev(); break; case DOWN: info.selectNext(); break; case FIRE: info.clickSelected(); } break; case Game.STATE_LEVEL: switch (key) { case LEFT_SOFTKEY: game.leftButtonPressed(); break; case RIGHT_SOFTKEY: game.rightButtonPressed(); break; } if (getGameAction(key) == FIRE) { game.fire(); } break; } } /** * Updates the game logic and transitions */ public void update() { switch (gameState) { case Game.STATE_TRANSITION: if (!(currentView != null && currentView.slideOut())) { currentView = null; if (!(targetView != null && targetView.slideIn())) { currentView = targetView; targetView = null; gameState = nextState; } } break; case Game.STATE_LEVEL: game.update(); break; } } /** * Render a frame */ public void render() { final Graphics g = getGraphics(); paint(g); flushGraphics(); } /** * Renders the view * @param g */ public void paint(Graphics g) { int anchor = Graphics.LEFT | Graphics.TOP; g.setColor(0xFF000000); g.fillRect(0, 0, displayWidth, displayHeight); g.drawImage(r.background, cornerX, displayHeight, Graphics.LEFT | Graphics.BOTTOM); g.drawImage((gameState == Game.STATE_MENU || gameState == Game.STATE_INFO) ? r.signOn : r.signOff, cornerX, cornerY + (int) (scaling * 86 + 0.5), anchor); if(gameState == Game.STATE_TRANSITION) { g.setClip(cornerX, cornerY, gameWidth, gameHeight); } else { g.setClip(0, 0, displayWidth, displayHeight); } try { if (currentView != null) { currentView.paint(g); } if (targetView != null) { targetView.paint(g); } } catch (NullPointerException npe) { // just no painting then } if ((gameState == Game.STATE_MENU || gameState == Game.STATE_INFO) && pressed) { ElectricArc arc = new ElectricArc(); for (int a = 2 + rnd.nextInt(4); a > 0; a--) { g.setColor(rnd.nextInt(255), 255, 255); arc.paint(g, x, y, (int) (scaling * 10), (int) (scaling * 10)); } } } protected void showNotify() { r.loadResources(); if (menu == null) { createMenu(); menu.hideResume(); } if (info == null) { createInfoScreen(); } if (game == null) { loadOrCreateGame(); } startApp(); } protected void hideNotify() { pauseApp(); r.freeResources(); audioManager.stopAll(); } protected void sizeChanged(int w, int h) { // do only stuff when the size actually changes if ((h == displayHeight) && (w == displayWidth)) { return; } calculateScalingFactor(); r.freeResources(); LightManager.allowDimming(); menu = null; info = null; game = null; showNotify(); } /** * Stop the game loop */ public void stopApp() { gameState = Game.STATE_MENU; this.gameThread.requestStop(); LightManager.allowDimming(); } /** * Start the game loop */ public void startApp() { if (gameThread == null) { gameThread = new GameThread(); } gameThread.requestStart(); LightManager.avoidDimming(); } /** * Pause the game loop */ public void pauseApp() { gameThread.requestPause(); LightManager.allowDimming(); } private void calculateScalingFactor() { // Figure out the scaling factor displayWidth = getWidth(); displayHeight = getHeight(); scaling = 1f; if (displayWidth != DEFAULT_WIDTH) { scaling = (float) (DEFAULT_WIDTH / displayWidth); } this.r = new Resources(scaling); gameWidth = (int) (scaling * DEFAULT_WIDTH + 0.5); gameHeight = (int) (scaling * DEFAULT_HEIGHT + 0.5); cornerX = (int) (displayWidth / 2 - gameWidth / 2 + 0.5); cornerY = displayHeight - gameHeight; } /** * * Game Thread */ class GameThread extends Thread { private boolean pause = true; private boolean stop = false; private boolean started = false; public void requestStart() { this.pause = false; if (!started) { this.start(); this.started = true; } else { synchronized (this) { notify(); } } } public void requestPause() { this.pause = true; } public void requestStop() { this.stop = true; } /** * The game loop. This example uses only one thread for updating * game logic and for rendering with constant frequency. */ public void run() { long time = 0; while (!stop) { try { if (pause) { synchronized (this) { wait(); } } else { time = System.currentTimeMillis(); checkKeys(); update(); render(); // Sleep the rest of the time time = INTERVAL - (System.currentTimeMillis() - time); Thread.sleep((time < 0 ? 0 : time)); } } catch (Exception e) { } } } } /** * Handle pointer pressed */ public void pointerPressed(int x, int y) { this.x = x; this.y = y; pressed = true; switch (gameState) { case Game.STATE_LEVEL: game.pointerPressed(x - cornerX, y - cornerY); break; case Game.STATE_MENU: menu.pointerEvent(Menu.POINTER_PRESSED, x, y); audioManager.playSample(r.SAMPLE_BUTTON); break; case Game.STATE_INFO: info.pointerEvent(Menu.POINTER_PRESSED, x, y); audioManager.playSample(r.SAMPLE_BUTTON); break; } if (gameState == Game.STATE_MENU) { electricity = audioManager.loopSample(r.SAMPLE_ELECTRICITY); } } /** * Handle pointer released */ public void pointerReleased(int x, int y) { pressed = false; if (electricity) { audioManager.stopPlayer(r.SAMPLE_ELECTRICITY); electricity = false; } switch (gameState) { case Game.STATE_LEVEL: game.pointerReleased(x - cornerX, y - cornerY); break; case Game.STATE_MENU: menu.pointerEvent(Menu.POINTER_RELEASED, x, y); break; case Game.STATE_INFO: info.pointerEvent(Menu.POINTER_RELEASED, x, y); break; } } /** * Handle pointer dragged */ public void pointerDragged(int x, int y) { this.x = x; this.y = y; switch (gameState) { case Game.STATE_LEVEL: game.pointerDragged(x - cornerX, y - cornerY); break; case Game.STATE_MENU: menu.pointerEvent(Menu.POINTER_DRAGGED, x, y); break; case Game.STATE_INFO: info.pointerEvent(Menu.POINTER_PRESSED, x, y); break; } } /** * Create and initialize game menu */ private void createMenu() { double menuScaling = (float) (displayHeight / DEFAULT_HEIGHT); menu = new MainMenu(cornerX, cornerY, gameWidth, gameHeight, new Menu.Listener() { public void itemClicked(int item) { switch (item) { case MainMenu.RESUME: resumeGame(); break; case MainMenu.NEWGAME: newGame(); break; case MainMenu.SENSORS: game.enableSensors(menu.toggleSensors()); break; case MainMenu.SOUNDS: AudioManager.setAudioEnabled(menu.toggleSounds()); break; case MainMenu.INFO: showInfo(); break; } } }, menuScaling); } /** * Create and initialize information screen */ private void createInfoScreen() { info = new InfoScreen(cornerX, cornerY, gameWidth, gameHeight, new Menu.Listener() { public void itemClicked(int item) { switch (item) { case InfoScreen.LINK: Main midlet = Main.getInstance(); try { if (midlet.platformRequest("http://projects.developer.nokia.com/JMEExplonoid")) { midlet.exit(); } } catch (ConnectionNotFoundException cnfe) { } break; case InfoScreen.BACK: showMenu(); break; } } }, scaling); } /** * Change view * @param targetView The view to change to */ public void changeView(Slideable targetView) { this.targetView = targetView; gameState = Game.STATE_TRANSITION; audioManager.playSample(r.SAMPLE_TRANSITION); } /** * Resume to game preserving the game state */ private void resumeGame() { nextState = Game.STATE_LEVEL; changeView(game); } /** * Start new game */ private void newGame() { game.newGame(); nextState = Game.STATE_LEVEL; changeView(game); } /** * Show menu screen */ public void showMenu() { nextState = Game.STATE_MENU; changeView(menu); } /** * Show info screen */ public void showInfo() { nextState = Game.STATE_INFO; changeView(info); } /** * Change level */ public void nextLevel() { nextState = Game.STATE_LEVEL; changeView(game); } }