BattleTankCanvas.java

/*
 * Copyright © 2012 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.battletank;

import com.nokia.example.battletank.game.Game;
import com.nokia.example.battletank.game.ProtectedContentException;
import com.nokia.example.battletank.game.audio.AudioManager;
import com.nokia.example.battletank.menu.HelpMenu;
import com.nokia.example.battletank.menu.AboutMenu;
import com.nokia.example.battletank.menu.BattleTankMenu;
import com.nokia.example.battletank.menu.BuyMenu;
import com.nokia.example.battletank.menu.Menu;
import java.io.IOException;
import javax.microedition.lcdui.AlertType;
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;

public class BattleTankCanvas
    extends GameCanvas implements GameThread.Listener {

    private static final int MAX_RENDERING_FPS = 12;
    private static final int LEFT_SOFTKEY = -6;
    private static final int RIGHT_SOFTKEY = -7;
    private volatile int pointerKeyState = 0;
    private Main main;
    // menus
    private BattleTankMenu menu;
    private BuyMenu buyMenu;
    private HelpMenu helpMenu;
    private AboutMenu aboutMenu;
    // current visible menu
    private Menu visibleMenu;
    private Game game;
    // touch handler
    private PointerEventHandler pointerEventHandler;
    // the game loop that is run MAX_RENDERING_FPS timer per second
    private GameThread gameLoop;
    private Graphics graphics;

    /**
     * Initializes the canvas
     *
     * @param main Main midlet
     */
    public BattleTankCanvas(Main main) {
        super(false);
        setFullScreenMode(true);
        this.main = main;

        // create menus
        createMenu();
        createGame();
        createBuyMenu();
        createHelpMenu();
        createAboutMenu();

        createPointerEventHandler();
    }

    /**
     * Gets the states of the physical game keys.
     *
     * @return An integer containing the key state information (one bit per
     * key), or 0 if the GameCanvas is not currently shown.
     */
    public int getKeyStates() {
        int keyStates = super.getKeyStates();
        if (keyStates != 0) {
            pointerKeyState = 0;
        }
        else {
            keyStates = pointerKeyState;
            if (pointerKeyState == FIRE_PRESSED) {
                pointerKeyState = 0;
            }
        }
        return keyStates;
    }

    /**
     * Shows menu view.
     */
    public void showMenu() {
        if (visibleMenu == menu) {
            return;
        }
        visibleMenu = menu;
        menu.setSounds(game.soundsEnabled);
        menu.selectItem(hasPointerEvents() ? -1 : 0);
    }

    /**
     * Hides current menu view.
     */
    public void hideCurrentMenu() {
        if (visibleMenu == menu && AudioManager.areSoundsEnabled()
            != game.soundsEnabled) {
            AudioManager.setSoundsEnabled(game.soundsEnabled);
        }
        visibleMenu = null;
    }

    /**
     * Shows buy menu.
     */
    public void showBuyMenu() {
        showMenu();
        buyMenu.selectItem(hasPointerEvents() ? -1 : 0);
        visibleMenu = buyMenu;
    }

    /**
     * Hides the wait indicator in the buy menu.
     */
    public void hideBuyMenuWaitIndicator() {
        buyMenu.hideWaitIndicator();
    }

    /**
     * Saves the current state of the game to RecordStore
     */
    public void saveGame() {
        if (game == null) {
            return;
        }
        try {
            RecordStore gameState = RecordStore.openRecordStore("GameState",
                true);
            if (gameState.getNumRecords() == 0) {
                gameState.addRecord(null, 0, 0);
            }
            byte[] data = game.getState();
            gameState.setRecord(getRecordId(gameState), data, 0, data.length);
            gameState.closeRecordStore();
        }
        catch (Exception e) {
            try {
                RecordStore.deleteRecordStore("GameState");
            }
            catch (RecordStoreException rse) {
            }
        }
    }

    /**
     * Hides buy option from the main menu.
     */
    public void hideBuyOption() {
        menu.setBuy(false);
    }

    /**
     * Shows Help view.
     */
    public void showHelpMenu() {
        showMenu();
        helpMenu.selectItem(hasPointerEvents() ? -1 : 0);
        visibleMenu = helpMenu;
    }

    /**
     * Shows About view.
     */
    public void showAboutMenu() {
        showMenu();
        aboutMenu.selectItem(hasPointerEvents() ? -1 : 0);
        visibleMenu = aboutMenu;
    }

    protected void keyPressed(int key) {
        // delegate key event to proper menu instance
        if (visibleMenu != null) {
            switch (getGameAction(key)) {
                case UP:
                    visibleMenu.selectPrev();
                    break;
                case DOWN:
                    visibleMenu.selectNext();
                    break;
                case FIRE:
                    visibleMenu.clickSelected();
                    break;
            }
        }
        switch (key) {
            case LEFT_SOFTKEY:
                leftSoftkey();
                break;
            case RIGHT_SOFTKEY:
                rightSoftkey();
                break;
        }
    }

    // delegate pointer events to proper menu
    protected void pointerPressed(int x, int y) {
        if (visibleMenu != null) {
            visibleMenu.pointerEvent(Menu.POINTER_PRESSED, x, y);
        }
        else {
            pointerEventHandler.pointerPressed(x, y);
        }
    }

    protected void pointerDragged(int x, int y) {
        if (visibleMenu != null) {
            visibleMenu.pointerEvent(Menu.POINTER_DRAGGED, x, y);
        }
        else {
            pointerEventHandler.pointerDragged(x, y);
        }
    }

    protected void pointerReleased(int x, int y) {
        if (visibleMenu != null) {
            visibleMenu.pointerEvent(Menu.POINTER_RELEASED, x, y);
        }
        else {
            pointerEventHandler.pointerReleased(x, y);
        }
    }

    /**
     * Called when this canvas is shown.
     */
    protected void showNotify() {
        if (!Main.isTrial()) {
            hideBuyOption();
        }
        graphics = getGraphics();
        startGameLoop();
        // show menu view first
        showMenu();
    }

    /**
     * Called when this canvas is hidden.
     */
    protected void hideNotify() {
        AudioManager.disableSounds();
        stopGameLoop();
        graphics = null;
    }

    protected void sizeChanged(int w, int h) {
        if (menu != null) {
            menu.setSize(w, h);
        }
        if (buyMenu != null) {
            buyMenu.setSize(w, h);
        }
        if (helpMenu != null) {
            helpMenu.setSize(w, h);
        }
        if (aboutMenu != null) {
            aboutMenu.setSize(w, h);
        }
        if (game != null) {
            game.setViewportSize(w, h);
        }
        if (pointerEventHandler != null) {
            pointerEventHandler.setSize(w, h);
        }
    }

    private void leftSoftkey() {
        if (visibleMenu != menu && game != null) {
            stopGameLoop();
            try {
                game.leftSoftkeyPressed();
            }
            catch (ProtectedContentException e) {
                newGame();
                showBuyMenu();
            }
            catch (IOException e) {
                main.showAlertMessage("Level loading failed",
                    Messages.LEVEL_LOAD_ERROR, AlertType.ERROR);
            }
            startGameLoop();
        }
    }

    private void rightSoftkey() {
        if (visibleMenu != null) {
            hideCurrentMenu();
        }
        else {
            showMenu();
        }
    }

    private void startGameLoop() {
        stopGameLoop();
        gameLoop = new GameThread(this, MAX_RENDERING_FPS);
        gameLoop.start();
    }

    private void createHelpMenu() {
        helpMenu = new HelpMenu(getWidth(), getHeight(), hasPointerEvents(),
            new Menu.Listener() {

                public void itemClicked(int item) {
                    switch (item) {
                        case HelpMenu.BACK:
                            hideCurrentMenu();
                            showMenu();
                            break;
                    }
                }
            });
    }

    private void createAboutMenu() {
        aboutMenu = new AboutMenu(getWidth(), getHeight(), new Menu.Listener() {

            public void itemClicked(int item) {
                switch (item) {
                    case AboutMenu.BACK:
                        hideCurrentMenu();
                        showMenu();
                        break;
                }
            }
        });
    }

    private void createGame() {
        game = new Game(getWidth(), getHeight());
        try {
            RecordStore gameState = RecordStore.openRecordStore("GameState",
                true);
            if (gameState.getNumRecords() == 0
                || !game.load(gameState.getRecord(getRecordId(gameState)))) {
                newGame();
            }
            gameState.closeRecordStore();
        }
        catch (RecordStoreException e) {
            newGame();
        }
    }

    private void newGame() {
        try {
            game.newGame();
        }
        catch (ProtectedContentException e) {
            // should not happen as there should be at least one trial level
            throw new RuntimeException("No levels.");
        }
        catch (IOException e) {
            // should not happen as there should be at least one trial level
            throw new RuntimeException("No levels.");
        }
    }

    private int getRecordId(RecordStore store)
        throws RecordStoreException {
        RecordEnumeration e = store.enumerateRecords(null, null, false);
        try {
            return e.nextRecordId();
        }
        finally {
            e.destroy();
        }
    }

    private void createPointerEventHandler() {
        pointerEventHandler = new PointerEventHandler(getWidth(), getHeight(),
            new PointerEventHandler.Listener() {

                public void onMoveLeft() {
                    pointerKeyState = LEFT_PRESSED;
                }

                public void onMoveRight() {
                    pointerKeyState = RIGHT_PRESSED;
                }

                public void onMoveUp() {
                    pointerKeyState = UP_PRESSED;
                }

                public void onMoveDown() {
                    pointerKeyState = DOWN_PRESSED;
                }

                public void onFire() {
                    pointerKeyState = FIRE_PRESSED;
                }

                public void onLeftSoftKey() {
                    leftSoftkey();
                }

                public void onRightSoftKey() {
                    rightSoftkey();
                }
            });
    }

    private void createBuyMenu() {
        buyMenu = new BuyMenu(getWidth(), getHeight(), new Menu.Listener() {

            public void itemClicked(int item) {
                switch (item) {
                    case BuyMenu.BUY:
                        if (Main.purchaseFullVersion()) {
                            buyMenu.showWaitIndicator();
                        }
                        break;
                    case BuyMenu.RESTORE:
                        if (Main.restoreFullVersion()) {
                            buyMenu.showWaitIndicator();
                        }
                        break;
                    case BuyMenu.BACK:
                        hideCurrentMenu();
                        showMenu();
                        break;
                }
            }
        });
    }

    private void stopGameLoop() {
        if (gameLoop != null) {
            gameLoop.cancel();
        }
    }

    private void createMenu() {
        menu = new BattleTankMenu(getWidth(), getHeight(), new Menu.Listener() {

            public void itemClicked(int item) {
                switch (item) {
                    case BattleTankMenu.RESUME:
                        hideCurrentMenu();
                        break;
                    case BattleTankMenu.NEWGAME:
                        newGame();
                        hideCurrentMenu();
                        break;
                    case BattleTankMenu.FULL_VERSION:
                        showBuyMenu();
                        break;
                    case BattleTankMenu.SOUNDS:
                        game.soundsEnabled = menu.toggleSounds();
                        break;
                    case BattleTankMenu.HELP:
                        showHelpMenu();
                        break;
                    case BattleTankMenu.ABOUT:
                        showAboutMenu();
                        break;
                    case BattleTankMenu.EXIT:
                        main.close();
                        break;
                }
            }
        });
        menu.setBuy(Main.isTrial());
    }

    public void runGameLoop() {
        if (visibleMenu != null) {
            visibleMenu.render(graphics);
        }
        else {
            game.update(getKeyStates());
            game.render(graphics);
        }

        flushGraphics();
    }
}