GameView.java

/*
 * Copyright © 2011 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.racer.views;

import com.nokia.example.racer.helpers.ImageLoader;
import com.nokia.example.racer.helpers.SimpleThread;
import com.nokia.example.racer.helpers.Engine;
import com.nokia.example.racer.views.components.Track;
import com.nokia.example.racer.views.tracks.LapListener;
import com.nokia.example.racer.views.tracks.Monza;
import javax.microedition.lcdui.game.GameCanvas;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;

public class GameView extends GameCanvas implements LapListener {

    private static final int MAX_SPEED = 12;

    private Timer mTimer;
    private Timer raceTimer;
    private TimerTask clock;

    private long currentLap = 0;
    private long bestLap = 0;
    private long latestLap = 0;

    private Graphics g;
    
    private int trackX = 0;
    private int trackY = 0;
    protected int speed = 2;
    private int surfaceMultiplier = 0;
    protected int friction = 2;
    private int x_increment = 0;
    private int y_increment = 0;
    
    private int carXPosition = 72;
    private int carYPosition = 160;
    private int currentFrame = 0;
    private int lastFrame = 0;
    
    private int curRed = 0;
    private int curGreen = 0;
    private int curBlue = 0;

    
    private double[] sins;
    private double[] coss;
    
    private int turnYposition = -80;

    private Image turnHelp;
    private Image topBar;
    private Image speedMeter;
    private Image startInfo;
    private Track track;
    private Sprite carSprite;
    private Sprite countDown;
    private Sprite speedNeedle;

    private boolean showInstructions = false;
    private boolean stopped = true;
    private boolean applyGas = false;
    private boolean turnLeft = false;
    private boolean turnRight = false;

    private Engine engine = new Engine();

    private SimpleThread gateKeeper;
    private SimpleThread gameLogic;
    private Thread countdownThread;

    private int pauseCounter = 0;
    private String error = "";
    private Font smallFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_SMALL);
    private Font bigFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_LARGE);
    public GameView() {
        super(false);
        setFullScreenMode(true);
        loadGraphics();
        preCalculate();
        engine.start();
    }

    private void loadGraphics() {
        try {
            ImageLoader loader = ImageLoader.getInstance();
            
            carSprite = new Sprite(loader.loadImage("/car_sprite.png"), 64, 64);
            carSprite.setRefPixelPosition(32, 32);
            carSprite.setPosition(carXPosition, carYPosition);
            carSprite.setFrame(currentFrame);
            turnHelp = loader.loadImage("/turn_help.png");
            countDown = new Sprite(loader.loadImage("/countdown_240x121.png"), 240, 121);
            countDown.setPosition(0, 100);
            countDown.setFrame(0);
            speedMeter = loader.loadImage("/speedo.png");
            topBar = loader.loadImage("/top_bar.png");
            speedNeedle = new Sprite(loader.loadImage("/speed_sprite_46x46.png"),46,46);
            speedNeedle.setFrame(0);
            speedNeedle.setPosition(190, 265);
            startInfo = loader.loadImage("/press_info.png");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void preCalculate() {
        //calculate angles
        sins = new double[24];
        coss = new double[24];
        for (int i = 0; i < 24; i++) {
            int angle = 15 * i;
            sins[i] = Math.sin(Math.toRadians(angle));
            coss[i] = Math.cos(Math.toRadians(angle));
        }
    }

    public void showNotify() {
        
        if(track == null) {
                setTrack(new Monza(this));
        }
        mTimer = new Timer();
        g = getGraphics();
        TimerTask ui = new TimerTask() {

            public void run() {
                render(g);
                flushGraphics();
            }
        };
        gameLogic = new SimpleThread(40) {

            public void execute() {
                if (!stopped) {
                    calculateX();
                    calculateY();
                    checkSurface();
                    handleInstructions();
                    
                }
            }
        };

        TimerTask speedUpdate = new TimerTask() {

            public void run() {
                int frame = lastFrame;

                updateForce(frame);

                lastFrame = currentFrame;

            }
        };
        TimerTask keyturning = new TimerTask() {
          public void run() {
              if (turnRight) {
                  turnRight();
              } else if (turnLeft) {
                  turnLeft();
              }
          }
        };
        gateKeeper = new SimpleThread(40) {

            public void execute() {
                track.checkGatePass((-trackX) + (carXPosition + 32), (-trackY) + (carYPosition + 32));
            }
        };
        gameLogic.start();
        gateKeeper.start();
        mTimer.schedule(ui, 0, 40);

        mTimer.schedule(speedUpdate, 0, 200);
        mTimer.schedule(keyturning, 0, 100);
         
        raceTimer = new Timer();
        clock = new TimerTask() {

            public void run() {
                currentLap += 10;
            }
        };

        countdownThread = new Thread() {
            public void run() {
                try {
                    countDown.setFrame(0);
                    pauseCounter = 1;
                    Thread.sleep(1000);
                    countDown.nextFrame();
                    pauseCounter = 2;
                    Thread.sleep(1000);
                    countDown.nextFrame();
                    pauseCounter = 3;
                    Thread.sleep(1000);
                    countDown.nextFrame();
                    Thread.sleep(700);
                    stopped = false;
                    raceTimer.scheduleAtFixedRate(clock, 10, 10);
                } catch (InterruptedException ie) {
                }
            }
        };
        countdownThread.start();
        engine.on();
    }

    protected void hideNotify() {
        countdownThread.interrupt();
        mTimer.cancel();
        raceTimer.cancel();        
        gateKeeper.stop();
        gameLogic.stop();
        engine.off();
        stopped = true;        
    }
    public void reset() {
        this.carXPosition = track.getCarXPosition();
        this.carYPosition = track.getCarYPosition();
        this.trackX = track.getStartX();
        this.trackY = track.getStartY();
        currentFrame = track.getCarStartFrame();
        carSprite.setFrame(currentFrame);
        speed = 2;
        x_increment = 0;
        y_increment = 0;
        currentLap = 0;
        bestLap = 0;
        latestLap = 0;
        track.resetLaps();
        track.resetPosition();        
    }

    private void render(Graphics g) {
        int anchor = g.TOP | g.LEFT;
        
        g.setColor(0xFFFFFF);
        g.fillRect(0, 0, getWidth(), getHeight());
        carSprite.setPosition(carXPosition, carYPosition);
        track.setPosition(trackX, trackY);
        track.render(g);
        carSprite.paint(g);
        g.drawImage(turnHelp, 0, turnYposition, anchor);
        if (stopped) {
            g.drawImage(startInfo, 0, 66, anchor);
            countDown.paint(g);
        }
        g.drawImage(speedMeter, 240, 320, g.BOTTOM | g.RIGHT);
        g.drawImage(topBar, 0, 0, g.TOP|g.LEFT);
        speedNeedle.paint(g);
        g.setFont(bigFont);
        g.drawString(track.getLaps()+"", 100,0, anchor);
        g.setFont(smallFont);
        g.drawString((currentLap / 1000)+":"+(currentLap % 1000) + ":"+ (currentLap % 100), 177, 0, anchor);
        g.drawString((bestLap / 1000)+":"+(bestLap % 1000) + ":" + (bestLap % 100), 177, 12, anchor);
        //drawDebugData(g);
    }

    private void drawDebugData(Graphics g) {
        int anchor = g.TOP | g.LEFT;
        g.setColor(0xFF0000);
        g.drawString("x_increment:" + x_increment, 0, 0, anchor);
        g.drawString("y_increment:" + y_increment, 0, 25, anchor);
        g.drawString("speed:" + speed, 0, 50, anchor);
        g.drawString("bestLap:" + bestLap, 0, 70, anchor);
        g.drawString("LatestLap:" + latestLap, 0, 90, anchor);
        g.drawString("currentLap:" + currentLap, 0, 110, anchor);
        g.drawString("laps:" + track.getLaps(), 0, 150, anchor);
        g.drawString("counter:" + pauseCounter, 0, 130, anchor);
        g.drawString(error, 0, 170, anchor);
    }

    private void updateForce(int frame) {
        if (frame == -1) {
            frame = currentFrame;
        }
        if (applyGas) {
            if (speed < MAX_SPEED) {
                speed += 1;
            }
        } else if (!applyGas && speed > friction) {
            speed -= 2;
        }
        
        int speedCalc = ((speed + surfaceMultiplier) - friction);
        if (speedCalc < 0) {
            speedCalc = 0;
        }
        y_increment = (int) (speedCalc * coss[frame]);
        x_increment = (int) (speedCalc * sins[frame]);
        if(speed >= 0 && speed <= 12) {
            int s = speed - 1;
            if(s < 0)
                s = 0;
            if(!applyGas && speed == friction)
                s = 0;
            speedNeedle.setFrame(s);
            
        }
    }

    private void calculateX() {
        int trackXAbs = Math.abs(trackX);

        if (trackXAbs >= (track.getWidth() - 240)) {

            if (carXPosition < 176 && carXPosition < 220 && x_increment > 0) {
                carXPosition += x_increment;
            } else if (carXPosition >= 144 && x_increment < 0) {
                carXPosition += x_increment;
            } else if (carXPosition <= 144 && x_increment < 0) {
                trackX -= x_increment;
            }

        } else if (trackX >= 0) {

            if (carXPosition >= 0 && x_increment < 0) {
                carXPosition += x_increment;
            } else if ((carXPosition < 72 && carXPosition > 0) && x_increment < 0) {
                carXPosition += x_increment;
            } else if (x_increment > 0 && carXPosition < 72) {
                carXPosition += x_increment;
            } else if (carXPosition >= 72 && x_increment > 0) {
                trackX -= x_increment;
            }

        } else {
            trackX -= x_increment;
        }

    }

    private void calculateY() {
        if (trackY <= -(track.getHeight() - 320)) {
            if (carYPosition >= 244 && carYPosition <= 256) {
                carYPosition += -y_increment;
            } else if (carYPosition < 244 && y_increment < 0) {
                carYPosition += -y_increment;
            } else if (carYPosition <= 244 && y_increment > 0) {
                trackY += y_increment;
            }
        } else if (trackY >= 0) {
            if (carYPosition >= 0 && carYPosition < 96) {
                carYPosition += -y_increment;
            } else if (carYPosition >= 96 && y_increment < 0) {
                trackY += y_increment;
            } else if (carYPosition >= 0 && y_increment > 0) {
                carYPosition -= y_increment;
            }
        } else {
            trackY += y_increment;
        }
        if (carYPosition < 0) {
            carYPosition = 0;
        }
        if (carYPosition > 256) {
            carYPosition = 256;
        }
    }

    private void checkSurface() {
        int[] rgb = new int[1];
        int absX = (trackX < 0) ? -trackX : trackX;
        int absY = (trackY < 0) ? -trackY : trackY;
        track.getTrackImage().getRGB(rgb, 0, 1, absX + carXPosition + 32, absY + carYPosition + 32, 1, 1);//32 is the half of car images width and height
        int currentRGB = rgb[0];//rgb is in format 0xAARRGGBB where AA is alpha channel values
        //check if the values of rgb are greater than
        int red = (currentRGB & 0x00FF0000) >> 16;
        int green = (currentRGB & 0x0000FF00) >> 8;
        int blue = (currentRGB & 0x000000FF);
        curRed = red;
        curGreen = green;
        curBlue = blue;
        if (red > 70 && green > 70 && blue > 70) {
            surfaceMultiplier = -2;
        } else {
            surfaceMultiplier = 0;
        }
    }

    protected void turnLeft() {
        lastFrame = currentFrame;

        if (currentFrame == 0) {
            currentFrame = 23;
        } else {
            currentFrame--;
        }
        carSprite.setFrame(currentFrame);

    }

    protected void turnRight() {
        lastFrame = currentFrame;

        if (currentFrame == 23) {
            currentFrame = 0;
        } else {
            currentFrame++;
        }
        carSprite.setFrame(currentFrame);

    }

    protected void keyPressed(int keyCode) {
        switch (keyCode) {
            case GameCanvas.KEY_NUM7:
                applyGas = true;
                engine.accelerate();
                break;
            case GameCanvas.KEY_NUM0:
                if (showInstructions) {
                    showInstructions = false;
                } else {
                    showInstructions = true;
                }
                break;
            case GameCanvas.KEY_NUM4: //turn left
                turnLeft = true;
                break;
            case GameCanvas.KEY_NUM6: //turn right
                turnRight = true;
                break;
        }
    }

    protected void keyReleased(int keyCode) {
        switch (keyCode) {
            case GameCanvas.KEY_NUM7:
                applyGas = false;
                engine.decelerate();
                break;
            case GameCanvas.KEY_NUM4:
                turnLeft = false;
                break;
            case GameCanvas.KEY_NUM6:
                turnRight = false;
                break;
        }
    }

    

    public void lapDriven(int laps) {        
        latestLap = currentLap;
        currentLap = 0;
        synchronized(this) {
            raceTimer.cancel();
            raceTimer = new Timer();
            clock = new TimerTask() {
                public void run() {
                    currentLap += 10;
                }
            };
            raceTimer.scheduleAtFixedRate(clock, 10, 10);
        }
        if (latestLap < bestLap || bestLap == 0) {
            bestLap = latestLap;
        }        
    }
    public void setTrack(Track track) {
        this.track = null;
        this.track = track;
        this.carXPosition = track.getCarXPosition();
        this.carYPosition = track.getCarYPosition();
        this.trackX = track.getStartX();
        this.trackY = track.getStartY();
        currentFrame = track.getCarStartFrame();
        carSprite.setFrame(currentFrame);
    }
    public void cleanTrackResources() {
        track = null;
        System.gc();
    }
    private void handleInstructions() {
        if (showInstructions) {
            if (turnYposition < 38) {
                turnYposition += 5;
            }

        } else {
            if (turnYposition > -120) {
                turnYposition -= 5;
            }
        }
    }
}