GameCanvasView.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.spacemission;

import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.GameCanvas;

public class GameCanvasView
        extends GameCanvas
        implements GameStatusListener, Softkey.Listener {

    private static final int LEFT_SOFTKEY = -6;
    private static final int RIGHT_SOFTKEY = -7;
    private final static int DRAG_THRESHOLD = 10;
    private GameMIDlet midlet;
    private GameThread gameThread;
    private Game3DManager game3DManager;
    private GameSoundManager soundManager;
    private Image instructionsImage;
    private Image pausedImage;
    private Image wellDoneImage;
    private Image badLuckImage;
    private Image weaponImage;
    private Image firedImage;
    private Softkey playKey;
    private Softkey pauseKey;
    private Softkey newGameKey;
    private Softkey exitKey;
    private Timer firingTimer;
    private boolean firstLoad;
    private boolean paused;
    private boolean gameOverWellDone;
    private boolean gameOverBadLuck;
    private boolean weaponFired;
    private boolean pointerDragged;
    private int score;
    private int startX = -1;
    private int startY = -1;

    public GameCanvasView(GameMIDlet midlet, GameSoundManager soundManager, Image frameworkInstructions,
                          Image frameworkPaused, Image frameworkWellDone,
                          Image frameworkBadLuck, Image gameWeapon, Image firedImage) {
        super(false);
        this.setFullScreenMode(true);
        this.soundManager = soundManager;
        this.createCanvas(midlet, frameworkInstructions, frameworkPaused,
                          frameworkWellDone, frameworkBadLuck, gameWeapon, firedImage);
    }

    /**
     * Create canvas
     */
    private void createCanvas(GameMIDlet midlet, Image frameworkInstructions,
                              Image frameworkPaused, Image frameworkWellDone,
                              Image frameworkBadLuck, Image gameWeapon, Image firedImage) {
        this.pointerDragged = false;
        this.paused = true;
        this.firstLoad = true;
        this.weaponFired = false;
        this.gameOverBadLuck = false;
        this.gameOverWellDone = false;
        this.instructionsImage = frameworkInstructions;
        this.pausedImage = frameworkPaused;
        this.wellDoneImage = frameworkWellDone;
        this.badLuckImage = frameworkBadLuck;
        this.weaponImage = gameWeapon;
        this.firedImage = firedImage;
        this.midlet = midlet;
        this.gameThread = new GameThread();
        this.game3DManager = new Game3DManager(getWidth(), getHeight());
        this.game3DManager.setGameStatusListener(this);
        this.firingTimer = new Timer();

        playKey = new Softkey("/framework/play.png", Softkey.PLAY, this, this);
        pauseKey = new Softkey("/framework/pause.png", Softkey.PAUSE, this, this);
        newGameKey = new Softkey("/framework/new_game.png", Softkey.NEW_GAME, this, this);
        exitKey = new Softkey("/framework/exit.png", Softkey.EXIT, this, this);

        playKey.setVisible(true);
        pauseKey.setVisible(false);
        newGameKey.setVisible(false);
        exitKey.setVisible(true);
    }

    /**
     * Render the canvas
     * @param g Graphics object
     */
    public void paint(Graphics g) {
        this.game3DManager.paint(g);
        drawScore(g);
        drawWeapon(g);
        if (firstLoad) {
            //play the midi file here
            soundManager.playMIDISound(0);
            drawGreyOut(g);
            g.drawImage(instructionsImage, getWidth() / 2, getHeight() / 2,
                        Graphics.HCENTER | Graphics.VCENTER);
        }
        else if (paused) {
            drawGreyOut(g);
            g.drawImage(pausedImage, getWidth() / 2, getHeight() / 2,
                        Graphics.HCENTER | Graphics.VCENTER);
        }
        else if (gameOverWellDone) {
            drawGreyOut(g);
            g.drawImage(wellDoneImage, getWidth() / 2, getHeight() / 2,
                        Graphics.HCENTER | Graphics.VCENTER);
        }
        else if (gameOverBadLuck) {
            drawGreyOut(g);
            g.drawImage(badLuckImage, getWidth() / 2, getHeight() / 2,
                        Graphics.HCENTER | Graphics.VCENTER);
        }
        drawSoftkeys(g);
    }

    /**
     * Render the weapon
     * @param g
     */
    private void drawWeapon(Graphics g) {
        if (weaponFired) {
            g.drawImage(firedImage, getWidth() / 2, getHeight() - weaponImage.getHeight() / 2 - Softkey.SOFTKEY_HEIGHT,
                        Graphics.BOTTOM | Graphics.HCENTER);
        }
        g.drawImage(weaponImage, getWidth() / 2, getHeight() - Softkey.SOFTKEY_HEIGHT,
                    Graphics.BOTTOM | Graphics.HCENTER);
    }

    /**
     * Render the grey out
     * @param g
     */
    private void drawGreyOut(Graphics g) {
        int[] buf = new int[getWidth()];
        for (int i = 0; i < getWidth(); i++) {
            buf[i] = 0x88ffffff;
        }
        g.drawRGB(buf, 0, 0, 0, 0, getWidth(), getHeight(), true);
    }

    /**
     * Render the scores
     * @param g
     */
    private void drawScore(Graphics g) {
        Font font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD,
                                 Font.SIZE_SMALL);
        g.setFont(font);
        int width = font.stringWidth("Score: ");
        g.setColor(27, 179, 23);
        g.drawRect(5, 5, width + 30, 20);
        g.setColor(101, 101, 101);
        g.fillRect(6, 6, width + 29, 19);
        g.setColor(27, 179, 23);
        g.drawString("Score: " + score, 7, 7, Graphics.TOP | Graphics.LEFT);
    }

    /**
     * Render the softkeys
     * @param g
     */
    private void drawSoftkeys(Graphics g) {
        g.setColor(0x656565);
        g.fillRect(0, getHeight() - Softkey.SOFTKEY_HEIGHT, getWidth(), getHeight());
        playKey.paint(g);
        pauseKey.paint(g);
        newGameKey.paint(g);
        exitKey.paint(g);
        int centerX = getWidth() / 2 - 1;
        g.drawLine(centerX, getHeight() - Softkey.SOFTKEY_HEIGHT + 5, centerX, getHeight() - 5);
    }

    /**
     * Check game keys for controlling movement and firing
     */
    private void checkKeys() {
        if (!pointerDragged && (!gameOverWellDone || !gameOverBadLuck)) {
            boolean leftPressed = false;
            boolean rightPressed = false;
            boolean upPressed = false;
            boolean downPressed = false;
            int keyState = getKeyStates();
            if ((keyState & FIRE_PRESSED) != 0) {
                fire();
            }
            if ((keyState & GameCanvas.LEFT_PRESSED) != 0) {
                leftPressed = true;
            }
            if ((keyState & GameCanvas.RIGHT_PRESSED) != 0) {
                rightPressed = true;
            }
            if ((keyState & GameCanvas.UP_PRESSED) != 0) {
                upPressed = true;
            }
            if ((keyState & GameCanvas.DOWN_PRESSED) != 0) {
                downPressed = true;
            }
            game3DManager.setKeysPressed(leftPressed, rightPressed, upPressed, downPressed);
        }
    }

    /**
     * Update the game state
     */
    protected void updateGame() {
        score = this.game3DManager.updateGame();
    }

    /**
     * Render the canvas
     */
    protected void render() {
        Graphics g = getGraphics();
        paint(g);
        flushGraphics();
    }

    /**
     * Stop the game loop
     */
    public void stopGame() {
        this.gameThread.requestStop();
        this.gameThread = null;
    }

    /**
     * Start/Continue playing
     */
    public void playGame() {
        soundManager.stopMIDISound(0);
        this.paused = false;
        this.firstLoad = false;
        this.gameThread.requestPlay();
        pauseKey.setVisible(true);
        playKey.setVisible(false);
        newGameKey.setVisible(false);
        exitKey.setVisible(false);
    }

    /**
     * Pause game loop
     */
    public void pauseGame() {
        this.paused = true;
        updateGame();
        this.gameThread.requestPause();
        pauseKey.setVisible(false);
        playKey.setVisible(true);
        exitKey.setVisible(true);
    }

    /**
     * Game loop
     */
    class GameThread
            extends Thread {

        private boolean pause = true;
        private boolean stop = false;
        private boolean started = false;

        public void requestPlay() {
            this.pause = false;
            if (!started) {
                this.start();
                this.started = true;
            }
        }

        public void requestPause() {
            this.pause = true;
        }

        public void requestStop() {
            this.stop = true;
        }

        public void run() {
            long time = 0;
            long delay = 10;

            while (!stop) {
                time = System.currentTimeMillis();
                if (!pause) {
                    checkKeys();
                    updateGame();
                    render();
                }
                time = delay - (System.currentTimeMillis() - time);
                try {
                    Thread.sleep((time < 0 ? 0 : time));
                }
                catch (InterruptedException e) {
                }
            }
        }
    }

    /**
     * Game over
     * @param reason
     */
    public void gameOver(int reason) {
        if (reason == GameStatusListener.GAME_COMPLETED) {
            gameOverWellDone = true;
            gameThread.requestStop();
        }
        else if (reason == GameStatusListener.PLAYER_DIED) {
            gameOverBadLuck = true;
            gameThread.requestStop();
        }
        pauseKey.setVisible(false);
        newGameKey.setVisible(true);
        exitKey.setVisible(true);
    }

    /**
     * Prepare things for new game
     */
    public void reset() {
        this.gameThread = null;
        this.game3DManager = null;
        System.gc();
        this.createCanvas(midlet, instructionsImage, pausedImage,
                          wellDoneImage, badLuckImage, weaponImage, firedImage);
        this.paused = false;
    }

    /**
     * Shoot freezing ammo
     */
    public void fire() {
        weaponFired = true;
        game3DManager.setFirePressed();
        firingTimer.schedule(new TimerTask() {

            public void run() {
                weaponFired = false;
                if (paused) {
                    render();
                }
            }
        }, 100);
        //play the first game sound here
        soundManager.playGameSound(0);
    }

    /**
     * Handle softkey clicked events
     * @param type
     */
    public void clicked(int type) {
        switch (type) {
            case Softkey.PLAY:
                playGame();
                break;
            case Softkey.PAUSE:
                pauseGame();
                break;
            case Softkey.NEW_GAME:
                reset();
                break;
            case Softkey.EXIT:
                midlet.notifyDestroyed();
                break;
            default:
                System.out.println("Unknown softkey type");
        }
    }

    /**
     * Map softkeys
     * @param key
     */
    protected void keyPressed(int key) {
        switch(key) {
            case LEFT_SOFTKEY:
                if(playKey.isVisible()) {
                    playGame();
                } else if(pauseKey.isVisible()) {
                    pauseGame();
                } else if(newGameKey.isVisible()) {
                    reset();
                }
                render();
                break;
            case RIGHT_SOFTKEY:
                if(exitKey.isVisible()) {
                    midlet.notifyDestroyed();
                }
                break;
        }
    }

    /**
     * Handle pressed events
     * @param x
     * @param y
     */
    protected void pointerPressed(int x, int y) {
        if (!paused) {
            firingTimer.schedule(new TimerTask() {

                public void run() {
                    if (!pointerDragged) {
                        fire();
                    }
                }
            }, 100);
        }

        startX = x;
        startY = y;

        playKey.pointerPressed(x, y);
        pauseKey.pointerPressed(x, y);
        newGameKey.pointerPressed(x, y);
        exitKey.pointerPressed(x, y);

        render();
    }

    /**
     * Handle dragged events
     * @param x
     * @param y
     */
    protected void pointerDragged(int x, int y) {
        pointerDragged = true;

        if (!paused) {
            int dX = startX - x;
            int dY = startY - y;

            boolean leftPressed = false;
            boolean rightPressed = false;
            boolean upPressed = false;
            boolean downPressed = false;

            // Control movement by dragging
            if (Math.abs(dX) > Math.abs(dY)) {
                if (dX > DRAG_THRESHOLD) {
                    leftPressed = true;
                }
                else if (dX < -DRAG_THRESHOLD) {
                    rightPressed = true;
                }
            }
            else {
                if (dY > DRAG_THRESHOLD) {
                    upPressed = true;
                }
                else if (dY < -DRAG_THRESHOLD) {
                    downPressed = true;
                }
            }
            game3DManager.setKeysPressed(leftPressed, rightPressed, upPressed, downPressed);
        }
    }

    /**
     * Handle released events
     * @param x
     * @param y
     */
    protected void pointerReleased(int x, int y) {
        playKey.pointerReleased(x, y);
        pauseKey.pointerReleased(x, y);
        newGameKey.pointerReleased(x, y);
        exitKey.pointerReleased(x, y);

        pointerDragged = false;

        render();
    }

    protected void sizeChanged(int width, int height) {
        int y = height - Softkey.SOFTKEY_HEIGHT;
        playKey.setPosition(0, y);
        pauseKey.setPosition(0, y);
        newGameKey.setPosition(0, y);
        exitKey.setPosition(width / 2, y);
    }
}