import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Transparency;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;

/**
 @author Nicholas Klaebe
 */
public class extends JFrame  
{
    private static int[] key;                                                    // Key board input
    
    final private int TILE_SIZE=150;                                            // the size of the pre-rotated tiles
    final private int TILE_SIZE_ISO= 212;                                        // the size of the rotated tiles - Math.sqrt(TILE_SIZE*TILE_SIZE*2)
    final private int TILE_SIZE_ISO_DIV2= (TILE_SIZE_ISO/2);                    // the size of the rotated tiles divided by two    

    final public static  void mainString[] arg)
    {
        new a()
    }
    
    final private BufferedImage[] images=new BufferedImage[3000];                // the master array of images used in the game
    
    /**
     * Swaps pixels in the given image which are either equal to or when "useLessThan" is true, less than the given backroundColour with
     * a random value based on the red,blue and green variances and the red, green blue offsets given. If createRotatedImages is set then the
     * method will also create the rotated tiles from the given image.
     
     @param img
     @param spriteIndex
     @param createRotatedImages
     @param longcurve
     @param imageSize
     @param backgroundColour
     @param useLessThan
     @param redVarience
     @param greenVarience
     @param blueVarience
     @param redOffset
     @param greenOffset
     @param blueOffset
     */
    final void a(BufferedImage img,int spriteIndex,boolean createRotatedImages,boolean longCurve,int imageSize,int backgroundColour,boolean useLessThan,int redVarience,int greenVarience,int blueVarience,int redOffset,int greenOffset,int blueOffset)
    {
        final int TILE_SIZE_ISO_MINUS_TILE_SIZE_DIV2=(TILE_SIZE_ISO-TILE_SIZE)/2;    
        final float PI_DIV_2=3.1416F/2;                                
        final float NEG_PI_DIV_4=-3.1416F/4;                            
        
        int i=0;
        for (i=0;i<imageSize;i++)
        {
            for (int j=0;j<imageSize;j++)
            {
                if (backgroundColour==|| useLessThan && (img.getRGB(i,j)&0xFF00)<backgroundColour || 
                        !useLessThan && (img.getRGB(i,j)&0xFFFFFFFF)==backgroundColour
                {
                    int temp=(int) (Math.random()*redVarience+ redOffset;
                    temp=(temp<<8)+(int) (Math.random()*greenVarience+ greenOffset; //green
                    temp=(temp<<8)+(int) (Math.random()*blueVarience+ blueOffset; //blue
                    
                    img.setRGB(i,j,temp);
                }
            }
        }
        
        if (createRotatedImages)
        {
            float ftemp=NEG_PI_DIV_4;
            

            
            for (i=0;i<4;i++)
            {
                BufferedImage img1 =new BufferedImage(TILE_SIZE_ISO, TILE_SIZE_ISO, Transparency.BITMASK);
                Graphics2D g=(Graphics2Dimg1.getGraphics();
                g.rotate (ftemp+=PI_DIV_2,TILE_SIZE_ISO_DIV2,TILE_SIZE_ISO_DIV2);
                g.drawImage(img,TILE_SIZE_ISO_MINUS_TILE_SIZE_DIV2,TILE_SIZE_ISO_MINUS_TILE_SIZE_DIV2,null);
                
                images[spriteIndex+((longCurve)?i*4:i)]=img1;
                

            }
        }
    }
    
    a()
    {
        int i,j,k;                                                        // varibles used in for loops.
        int temp,temp1,temp2,temp3,temp4;                                // temporary integers which are used through out the game for different purposes.
        float ftemp,ftemp1,ftemp2,ftemp3;                                // temporary floats which are used through out the game for different purposes.

        
        // car characteristics
         final float MAX_SPEED=7.5F;                                    // the maximum speed 
            final float STEER_INCREMENT=1.75F;                                // amount the direction of a car can turn per frame
            final float NORMAL_BREAKING_INCREMENT=0.2F;                    // the amount of speed lost when breaking normally
            final float HARD_BREAKING_INCREMENT=0.55F;                        // the amount of speed lost when breaking Heavily
            final float COAST_BREAKING_INCREMENT=0.035F;                    // the amount of speed lost when coasting
            final float VELOCITY_INCREMENT=0.10F;                            // the acceleration rate
        


                        
        
            final int DESIRED_FPS=60;                                        // the optimum game update rate
            final float DESIRED_WAIT=1000f/DESIRED_FPS;                        // used to determine how long we should sleep/yield between game frames
         final int NO_OF_CARS=127;                                        // maximum number of cars
         final int SCREEN_WIDTH = 800;                                    // Screen Width
         final int SCREEN_HEIGHT = 600;                                    // Screen Height
         final float DEGTORAD=3.1416F/180;                                // used to convert degees into radians
         final float RADTODEG=180F/3.1416F;                                // used to convert degees into radians
         final float CAR_BOUNDS_ANGLE_RAD=0.49782234F;                    // the absolute angle from the centre of the car to each of the car's corners in radians
         final int CAR_BOUNDS_ANGLE_DEG=(int) (CAR_BOUNDS_ANGLE_RAD*RADTODEG);                        // the absolute angle from the centre of the car to each of the car's corners in degrees
         
        
         final int USER_CAR_POS_X=SCREEN_WIDTH/2-33;                            // The user's car's graphic on screen X position
         final int USER_CAR_POS_Y=SCREEN_HEIGHT/2-33;                            // The user's car's graphic on screen Y position 

         final int TARGET_AREA=100*100;            // the distance to the target in determining that the target has been reached

         final byte KERB_OFFSET=10;                // the size of the kerb (pixels) between the side of the tile and the start of the road


         final int TILE_SIZE_ISO_SQUARED=TILE_SIZE_ISO*TILE_SIZE_ISO;    // the size of the rotated tiles squared    
         final int TILE_SIZE_ISO_DIV4=TILE_SIZE_ISO/4;                    // the size of the rotated tiles divided by four    

         final  float HITTING_CAR_SLOW_DOWN=0.75F;                        // a multiplier used to determine the speed of a car hitting another car
         final  float HIT_FRONT_OF_CAR_SLOW_DOWN=0.95F;                    // a multiplier used to determine the speed of a car hitting another car front on
         final  float HIT_CAR_BUMP=0.25F;                                // a multiplier used to increase a car speed when being hit by another car  
         final  byte COLLISION_DETECTION_FIELD_OFFEST_LENGTH_FROM_CAR_MIDDLE=13;    
         final  int COLLISION_DETECTION_FIELD_RADIUS_SQUARED=13*13;
         final  float CAR_CORNER_TO_MIDDLE_LENGTH=26.177F;
         final  float WHEEL_TO_MIDDLE_LENGTH=18F;
        
         final int TARMAC_COLOUR_INT=0xFF404040;                        // the colour of the Tarmac tracks
         final int BACKGROUND_TEMP_COLOUR_INT=0xFF000001;                // a temp colour which is use as a background indicatior
         

         final BufferedImage smallImage = new BufferedImage(TILE_SIZE,TILE_SIZE,1);        // the inital tile before converting into isometric
         final BufferedImage bigImage = new BufferedImage(TILE_SIZE*2,TILE_SIZE*2,1);    // the inital large tile (2x2 small tiles) before converting into isometric
         

        
         final int tilesX=200;                                                    // the X dimension of the tilemap
         final int tilesY=200;                                                    // the Y dimension of the tilemap
         
         
         final int tilesPerScreenX=SCREEN_WIDTH/TILE_SIZE_ISO +3;            // the number of tiles per screen on the X axis
         final int tilesPerScreenY=SCREEN_HEIGHT/TILE_SIZE_ISO_DIV2 +3;        // the number of tiles per screen on the Y axis
    
        key = new int[256];                                            // a boolean array reflecting the state of the keyboard
        Graphics2D g;                                                     // a Graphics2D instance which is used through out the game for different purposes.


        final int START_LOCATION_X_OFFSET=-USER_CAR_POS_X-33;    
        final int START_LOCATION_Y_OFFSET=-USER_CAR_POS_Y-33;

        
        final int SCREEN_MIDDLE_OFFSET_X=33+USER_CAR_POS_X;
        final int SCREEN_MIDDLE_OFFSET_Y=33+USER_CAR_POS_Y;

        
         final int TARGET_LOCATION_X_OFFSET=START_LOCATION_X_OFFSET+TILE_SIZE_ISO_DIV2;
         final int TARGET_LOCATION_Y_OFFSET=START_LOCATION_Y_OFFSET+TILE_SIZE_ISO_DIV2;
         
         
         final int NO_BREAKING=0;        // braking type: no braking
         final int HARD_BREAKING=2;        // braking type: hard braking
         final int NORMAL_BREAKING=3;    // braking type: normal braking
         final int COAST_BREAKING=1;        // braking type: coast braking
         
         final int MAX_SKIDS=1000;        // maximum number of skid segments
         final int SKID_LIFE=1*1000;        // the onscreen life of the skid segments (in milliseconds)
         



        final short[] DATA_VARIABLES=
        {
                // COLLISION VARIABLES
                0,CAR_BOUNDS_ANGLE_DEG,-CAR_BOUNDS_ANGLE_DEG,180,CAR_BOUNDS_ANGLE_DEG,-CAR_BOUNDS_ANGLE_DEG,

                //LONG_TURN_VARIABLES
                0,0,
                TILE_SIZE_ISO_DIV2,TILE_SIZE_ISO_DIV2,
                -TILE_SIZE_ISO_DIV2,TILE_SIZE_ISO_DIV2,
                0,TILE_SIZE_ISO,
                0,1,2,3,
                  2,0,3,1,
                  3,2,1,0,
                  1,3,0,2
        };
        
        


        
        
        final long[] skidTime=new long[MAX_SKIDS];        // the timestamp of skid segment creation
        final float[][] skidX=new float[MAX_SKIDS][4];  // the x co-ordinates of skid segments
        final float[][] skidY=new float[MAX_SKIDS][4];    // the y co-ordinates of skid segments




        BufferedImage img1;     // temporary working image                    

        

/****************************************************************************************************************
 * Initalize Screen
 */
        // the buffer strategey used for the video double buffer
        BufferStrategy strategy;
        

        // initalize screen
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(SCREEN_WIDTH, SCREEN_HEIGHT)
        show();
        
        
        

        // enable keyboard events
        enableEvents(KeyEvent.KEY_EVENT_MASK);

        // create the buffering strategy which will allow AWT
        // to manage our accelerated graphics
        createBufferStrategy(2);
        strategy = getBufferStrategy();

        
                 
/****************************************************************************************************************
 * Grass
 */

              
        // create "grass" background using random green pixels
        a(smallImage,0,true,false,TILE_SIZE,0,false,0,50,0,0,200,0);

        
/****************************************************************************************************************
 * Straight thin Road
 */
        
        // create "grass" background
        a(smallImage,0,false,false,TILE_SIZE,0,false,0,50,0,0,200,0);
        
        g=(Graphics2DsmallImage.getGraphics();
        
        // create road
        g.setColor(new Color(TARMAC_COLOUR_INT));
        g.fillRect(KERB_OFFSET,0,TILE_SIZE-KERB_OFFSET*2,TILE_SIZE);
        
        // create edges
        g.setColor(new Color(0xFFFFFFFF));
        g.drawRect(KERB_OFFSET,-1,TILE_SIZE-KERB_OFFSET*2,TILE_SIZE+1);
        g.drawRect(KERB_OFFSET+1,-1,TILE_SIZE-(KERB_OFFSET+1)*2,TILE_SIZE+1);
        
        
        
        // tarmac
       
        a(smallImage,201,true,false,TILE_SIZE,1,false,0,0,0,0,0,0);
        
        // dirt
        a(smallImage,1,true,false,TILE_SIZE,TARMAC_COLOUR_INT,false,25,25,25,118,105,0);
        
        // gravel
        a(smallImage,101,true,false,TILE_SIZE,0x9600,true,25,25,25,200,200,200);
        
       
/****************************************************************************************************************
 * START thin Road
 */
        

        // create "grass" background
        a(smallImage,0,false,false,TILE_SIZE,0,false,0,50,0,0,200,0);
        
        g=(Graphics2DsmallImage.getGraphics();
        
        // create road
        g.setColor(new Color(TARMAC_COLOUR_INT));
        g.fillRect(KERB_OFFSET,0,TILE_SIZE-KERB_OFFSET*2,TILE_SIZE);
        
        // create edges
        g.setColor(new Color(0xFFFFFFFF));
        g.drawRect(KERB_OFFSET,-1,TILE_SIZE-KERB_OFFSET*2,TILE_SIZE+1);
        g.drawRect(KERB_OFFSET+1,-1,TILE_SIZE-(KERB_OFFSET+1)*2,TILE_SIZE+1);

        g.drawRect(KERB_OFFSET,TILE_SIZE/2-1,TILE_SIZE-KERB_OFFSET*2,TILE_SIZE/2+1);
   

        // tarmac

        a(smallImage,229,true,false,TILE_SIZE,1,false,0,00,0,0,0,0);
        
        // dirt
        a(smallImage,29,true,false,TILE_SIZE,TARMAC_COLOUR_INT,false,25,25,25,118,105,0);

        // gravel
        a(smallImage,129,true,false,TILE_SIZE,0x9600,true,25,25,25,200,200,200);

        
/****************************************************************************************************************
 * cross thin Road
 */
        // create "grass" background
        a(smallImage,0,false,false,TILE_SIZE,0,false,0,50,0,0,200,0);
        
        g=(Graphics2DsmallImage.getGraphics();
        
        // create vertical road component
        g.setColor(new Color(TARMAC_COLOUR_INT));
        g.fillRect(KERB_OFFSET,0,TILE_SIZE-KERB_OFFSET*2,TILE_SIZE);
        
        // create vertical edges component
        g.setColor(new Color(0xFFFFFFFF));
        g.drawRect(KERB_OFFSET,-1,TILE_SIZE-KERB_OFFSET*2,TILE_SIZE+1);
        g.drawRect(KERB_OFFSET+1,-1,TILE_SIZE-(KERB_OFFSET+1)*2,TILE_SIZE+1);
        
        // create horizontal road component
        g.setColor(new Color(TARMAC_COLOUR_INT));
        g.fillRect(0,KERB_OFFSET,TILE_SIZE,TILE_SIZE-KERB_OFFSET*2);
        
        // create horizontal edges component
        g.setColor(new Color(0xFFFFFFFF));
        g.drawRect(-1,KERB_OFFSET,TILE_SIZE+1,TILE_SIZE-KERB_OFFSET*2);
        g.drawRect(-1,KERB_OFFSET+1,TILE_SIZE+1,TILE_SIZE-(KERB_OFFSET+1)*2);
        
        // over paint the extra lines
        g.setColor(new Color(TARMAC_COLOUR_INT));
        g.fillRect(KERB_OFFSET+2,0,TILE_SIZE-(KERB_OFFSET+2)*2,TILE_SIZE);
        
        // tarmac
        a(smallImage,225,true,false,TILE_SIZE,1,false,0,00,0,0,0,0);

        // dirt
        a(smallImage,25,true,false,TILE_SIZE,TARMAC_COLOUR_INT,false,25,25,25,118,105,0);

        // gravel
        a(smallImage,125,true,false,TILE_SIZE,0x9600,true,25,25,25,200,200,200);


/****************************************************************************************************************
 * Sharp Corner thin Road
 */

        g=(Graphics2DsmallImage.getGraphics();
        
        // reset the smallImage using the background colour
        g.setColor(new Color(BACKGROUND_TEMP_COLOUR_INT));
        g.fillRect(0,0,TILE_SIZE,TILE_SIZE);
        
        // create the outside edge
        g.setColor(new Color(0xFFFFFFFF));
        g.fillOval(KERB_OFFSET,KERB_OFFSET,(TILE_SIZE-KERB_OFFSET*2)*2,(TILE_SIZE-KERB_OFFSET*2)*2);
        
        // create the road
        g.setColor(new Color(TARMAC_COLOUR_INT));
        g.fillOval(KERB_OFFSET+2,KERB_OFFSET+2,(TILE_SIZE-(KERB_OFFSET-2)*2)*2,(TILE_SIZE-(KERB_OFFSET-2)*2)*2);
        
        // create the inside edge
        g.setColor(new Color(0xFFFFFFFF));
        g.fillOval(TILE_SIZE-KERB_OFFSET-2,TILE_SIZE-KERB_OFFSET-2,(KERB_OFFSET+2)*2,(KERB_OFFSET+2)*2);
        
        // convert the inner most wedge back to the background colour
        g.setColor(new Color(BACKGROUND_TEMP_COLOUR_INT));
        g.fillOval(TILE_SIZE-KERB_OFFSET,TILE_SIZE-KERB_OFFSET,(KERB_OFFSET)*2,(KERB_OFFSET)*2);
   
        // create grass and tarmac
        a(smallImage,205,true,false,TILE_SIZE,BACKGROUND_TEMP_COLOUR_INT,false,0,50,0,0,200,0);
        
        // dirt
        a(smallImage,5,true,false,TILE_SIZE,TARMAC_COLOUR_INT,false,25,25,25,118,105,0);
        
        // gravel
        a(smallImage,105,true,false,TILE_SIZE,0x9600,true,25,25,25,200,200,200);
        
        
/****************************************************************************************************************
 * Long Corner thin Road
 */
        
        
        g=(Graphics2DbigImage.getGraphics();
        
        // reset the bigImage using the background colour
        g.setColor(new Color(BACKGROUND_TEMP_COLOUR_INT));
        g.fillRect(0,0,TILE_SIZE*2,TILE_SIZE*2);
        
        // create the outside Edge
        g.setColor(new Color(0xFFFFFFFF));
        g.fillOval(KERB_OFFSET,KERB_OFFSET,(TILE_SIZE*2-KERB_OFFSET*2)*2,(TILE_SIZE*2-KERB_OFFSET*2)*2);
        
        // create the road
        g.setColor(new Color(TARMAC_COLOUR_INT));
        g.fillOval(KERB_OFFSET+2,KERB_OFFSET+2,(TILE_SIZE*2-(KERB_OFFSET-2)*2)*2,(TILE_SIZE*2-(KERB_OFFSET-2)*2)*2);
        
        // create the inside edge
        g.setColor(new Color(0xFFFFFFFF));
        g.fillOval(TILE_SIZE-KERB_OFFSET-2,TILE_SIZE-KERB_OFFSET-2,(TILE_SIZE+KERB_OFFSET+2)*2,(TILE_SIZE+KERB_OFFSET+2)*2);
        
        // convert the inner most wedge back to the background colour
        g.setColor(new Color(BACKGROUND_TEMP_COLOUR_INT));
        g.fillOval(TILE_SIZE-KERB_OFFSET,TILE_SIZE-KERB_OFFSET,(TILE_SIZE+KERB_OFFSET)*2,(TILE_SIZE+KERB_OFFSET)*2);
   
        // create grass
        a(bigImage,0,false,false,TILE_SIZE*2,BACKGROUND_TEMP_COLOUR_INT,false,0,50,0,0,200,0);
        
        
        // for each small image in the big image (2x2)
        for (j=0;j<4;j++)
        {
            g=(Graphics2DsmallImage.getGraphics();
            
            // draw the current quadrent of the big image into the small image
            g.drawImage(bigImage,-(j%2)*TILE_SIZE,(-(j/2))*TILE_SIZE,null);
            
            // tarmac
            a(smallImage,209+j,true,true,TILE_SIZE,1,false,0,0,0,0,0,0);
            
            // dirt
            a(smallImage,9+j,true,true,TILE_SIZE,TARMAC_COLOUR_INT,false,25,25,25,118,105,0);

            // convert road into gravel using random varience of grey colour
            a(smallImage,109+j,true,true,TILE_SIZE,0x9600,true,25,25,25,200,200,200);
        }
        
/****************************************************************************************************************
 * Car
 */


        for (j=0;j<2;j++)
        {

            Color col1=new Color((j==0)?0xFF0000FF:0xFFFF0000);
            Color col2=new Color((j==0)?0xFF0000C0:0xFFC00000);
            

            
          // make a new image to hold the car
          img1 = new BufferedImage(46,25, Transparency.BITMASK);
          g=(Graphics2Dimg1.getGraphics();
          g.setColor(col1);
          g.fillOval(0,2,6,21);
          g.fillOval(30,2,16,21);
          g.fillRect(3,2,35,21);
          g.drawLine(23,0,23,25);
          
          g.setColor(new Color(0xFFFFFFFF));
          g.fillRect(1,10,44,5);
          
          g.setColor(col1);
          g.drawLine(1,12,44,12);
          
          g.setColor(new Color(0xFF000000));
          g.fillOval(22,3,8,19);
          g.fillOval(6,4,8,17);
          
          g.drawLine(17,3,23,3);
          g.drawLine(16,4,22,4);
          g.drawLine(16,5,21,5);
          
          g.drawLine(17,21,23,21);
          g.drawLine(16,20,22,20);
          g.drawLine(16,19,21,19);
          
          g.setColor(col2);
          g.fillRect(40,11,3,3);

          g.setColor(new Color(0xFFb0b0C0));
          g.fillRect(39,3,2,3);
          g.fillRect(39,19,2,3);

          
          // create rotational images of the car
          for (i=300;i<660;i++)
          {
              g=(Graphics2D) (images[i+j*400]=new BufferedImage(6565, Transparency.BITMASK)).getGraphics();
              g.rotate (DEGTORAD*(i-300),33,33);
              g.drawImage(img1,10,20,null);

          }
        }
 


/****************************************************************************************************************
 * Map
 */
        
        int lastTile=0;            // the last track segment of the current race track
        int level=1;            // the current level

        
        // Get hold of a graphics context for the accelerated surface
        g = (Graphics2Dstrategy.getDrawGraphics();

        while (true)
        {
            
            int[] map=null;
            final float[] speed=new float[NO_OF_CARS];            // the current speed of the cars                                                            
            final float[] targetX=new float[512];                // the x corordinates of the track segment targets
            final float[] targetY=new float[512];                // the y corordinates of the track segment targets
            final float[] tspeed=new float[512];                // the desired speed for the the AI at the track segements
            final int[] skidIndex=new int[NO_OF_CARS];            // the previous skid segment index for the cars
            final float[] direction=new float[NO_OF_CARS];        // the current direction of the cars        
            final float[] prevDistance=new float[NO_OF_CARS];    // the previous distance to the next track target for each car
            
            final float[] xx=new float[NO_OF_CARS];                // the X position of the cars
            final float[] yy=new float[NO_OF_CARS];                // the Y position of the cars

            final int[] targetIndex=new int[NO_OF_CARS];        // the current target the cars are heading for
            
            int[] breaking=new int[NO_OF_CARS];                    // the current breaking type of the cars
            

        

            int surface=0;                // current the surface type  
            int surfaceDuration=0;        // the duration of this surface

            int type=0;                    // current track segment type
            int typeDuration=0;            // duration of the track segment type


            temp1=Integer.MAX_VALUE;    // the track length
            
            
            // Generate a track
            while (j<temp1)
            {
                
                map =new int[tilesX*tilesY];

                // track start positions            
                temp2=TILE_SIZE_ISO*100-(50%2)*TILE_SIZE_ISO_DIV2;
                temp3=TILE_SIZE_ISO_DIV2*100;
                
                // set the starting location for the human car
                xx[0]=temp2+TARGET_LOCATION_X_OFFSET;
                yy[0]=temp3+TARGET_LOCATION_Y_OFFSET;
    

                // track length
                temp1=20+level*5;
                
                // inital direction
                temp4=(int) (Math.random()*4);    
    
                // set the inital starting map tile
                map[100*tilesX+100]=surface*100+1+temp4;
                j=0;
            
                gotStuck:
                while (j<temp1)
                {
    
                    
                    i=0;
                    
                    type=0;
                    typeDuration=1;
                    
                    // create the leading track segments
                    if (j==0);
                    // create the the start line segment
                    else if (j==1)
                    {
                        
                        type=3;
                        
                        // set the start locations for the opponents
                        for (k=1;k<level/3+2;k++)
                        {
                            xx[k]=temp2+TARGET_LOCATION_X_OFFSET-TILE_SIZE_ISO_DIV4+(int) (Math.random()*TILE_SIZE_ISO_DIV4*2)
                            yy[k]=temp3+TARGET_LOCATION_Y_OFFSET-TILE_SIZE_ISO_DIV4+(int) (Math.random()*TILE_SIZE_ISO_DIV4*2);
                            
                        }
                    }

                    // create the body of the track
                    else if (j<temp1-2)
                    {
                        type=(int) (Math.random()*3);
                        typeDuration=9-level*3;
                        typeDuration=(type>0)?((int) (Math.random()*(level/2)))%3+1:((typeDuration<0)?0:typeDuration)+(int) (Math.random()*6)+1;

                        
                        if (typeDuration+j>temp1-2typeDuration=temp1-2-j;
                        
                    }
                    // create the finish line
                    else if (j==temp1-2)
                    {
                        type=3;
                    }

                    // keep creating the same track type until reaching the track duration
                    while (i<typeDuration)
                    {
                        surfaceDuration--;
                        
                        // if we have reached the surface duration limit choose a new surface and duration based on the current level
                        if (surfaceDuration<0
                        {    
                            surface=2-((int) (Math.random()*((level/3))))%3;
                            surfaceDuration=14-level*2;
                            surfaceDuration=((surfaceDuration<0)?0:surfaceDuration)+(int) (Math.random()*10);
                        }
                        
                        // move the current track segment pointer based on the current direction
                           temp2+=TILE_SIZE_ISO_DIV2-TILE_SIZE_ISO*(temp4/2);
                           temp3-=TILE_SIZE_ISO_DIV2-(((temp4+1)%4>1)?TILE_SIZE_ISO:0);

                           // convert the pointer into the map co-ordinates
                        int mapy=temp3/TILE_SIZE_ISO_DIV2;
                        int mapx=(temp2+((1+temp3)%2)*TILE_SIZE_ISO_DIV2)/TILE_SIZE_ISO;
                        
                        // if out of range then attempt to create another track
                        if (mapy<|| mapx<|| mapy>=tilesY-|| mapx>=tilesX-2)
                        {
                            break gotStuck;
                        }
                        
                        
                        // set the default track segment speed to full speed
                        tspeed[j]=1;
                        
                        // extract the track segment type at the map co-ordinates
                        int track=map[mapy*tilesX+mapx];
                        
                        // if there is a track segment at this map position...
                        if (track>0)
                        {
                            //if both the current track type and the track segment at the map position are "straight road" segments then change the
                            //track segment at the map position to a "cross road".
                            if (type==&& track%100<5)
                            {
                                track=surface*100+25+temp4;
                            }
                            // else attempt to create another track
                            else
                            {
                                    break gotStuck;
                            }
                        }
                        else
                        {
                            // set the track segment at this map position to "straight road"
                            if (type==0
                            {
                                track=surface*100+1+temp4;
                            }
                            // set the track segment at this map position to "start/finish line"
                            else if (type==3
                            {
                                track=surface*100+29+temp4;
                            }
                            else 
                            {
                                temp=(int) (Math.random()*2);
                                // set the track segment at this map position to "sharp corner"
                                if (type==1
                                
                                {
                                    // turn left/right 
                                    temp=(int) (Math.random()*2);
                                    
                                    track=surface*100+5+(temp4+temp)%4;
                                    
                                    // move the current track segment pointer to the end of the turn
                                       temp4=temp4-temp*2+1;
                                       temp4=(temp4<0)?4+temp4:temp4%4;
                                       
                                       // set the desired speed 
                                       tspeed[j]=0.50F*(0.6F+0.2F*surface)
                                }
                                
                                // set the track segment at this map position to "long corner"
                                else
                                {
                                            
                                       // temporary position values
                                       int sx,sy;
                                       
                                       // determine the starting position of the top left tile in the long corner graphics
                                       if (temp4==0)
                                       {
                                           sx=temp2-TILE_SIZE_ISO_DIV2*(temp-1);
                                           sy=temp3-TILE_SIZE_ISO-TILE_SIZE_ISO_DIV2*(temp-1);
                                       }
                                       else if (temp4==1)
                                       {
                                           sx=temp2+TILE_SIZE_ISO_DIV2*temp;
                                           sy=temp3-TILE_SIZE_ISO_DIV2*temp;
        
                                       }
                                       else if (temp4==2)
                                       {
                                           sx=temp2+TILE_SIZE_ISO_DIV2*(temp-1);
                                           sy=temp3+TILE_SIZE_ISO_DIV2*(temp-1);
                                       }
                                       else
                                       {
                                           sx=temp2-TILE_SIZE_ISO_DIV2*temp;
                                           sy=temp3-TILE_SIZE_ISO+TILE_SIZE_ISO_DIV2*temp;
                                       }
                                           
                                    // set the desired speed 
                                       tspeed[j]=0.75F*(0.6F+0.2F*surface)
                                    targetX[j]=sx+TARGET_LOCATION_X_OFFSET;
                                    targetY[j]=sy+TILE_SIZE_ISO_DIV2+TARGET_LOCATION_Y_OFFSET;
                                    
                                    
                                    // set the tiles for the long corner
                                    for (k=0;k<4;k++)
                                    {
                                        mapy=sy+DATA_VARIABLES[k*2+7];
                                        mapx=(sx+DATA_VARIABLES[k*2+6]+(1+mapy%2)*TILE_SIZE_ISO_DIV2)/TILE_SIZE_ISO;
                                        mapy=mapy/TILE_SIZE_ISO_DIV2;
                                        
                                        // if we are attempting to overwrite a previous track segment then attempt to create a new track
                                        if (map[mapy*tilesX+mapx]!=0
                                        {
                                            break gotStuck;
                                        }
                                        
                                        map[mapy*tilesX+mapx]=surface*100+9+DATA_VARIABLES[14+((temp4+temp)%4)*4+k]+((temp4+temp)%4)*4;
                                    }
                                    
                                    
                                    
                                    
                                    
                                    // move the current track segment pointer to the end of the turn
                                       temp2+=TILE_SIZE_ISO_DIV2-TILE_SIZE_ISO*(temp4/2);
                                       temp3-=TILE_SIZE_ISO_DIV2-(((temp4+1)%4>1)?TILE_SIZE_ISO:0);
                                       temp4=temp4-temp*2+1;
                                       temp4=(temp4<0)?4+temp4:temp4%4;
                                       temp2+=TILE_SIZE_ISO_DIV2-TILE_SIZE_ISO*(temp4/2);
                                       temp3-=TILE_SIZE_ISO_DIV2-(((temp4+1)%4>1)?TILE_SIZE_ISO:0);
                                       

                                   
                                }
                            }

    
                        }
                        
                        // if the track segment is not a long corner set track on the map and the track target co-ordinates. 
                        if (type!=2)
                        {
                            targetX[j]=temp2+TARGET_LOCATION_X_OFFSET;
                            targetY[j]=temp3+TARGET_LOCATION_Y_OFFSET;
                            map[mapy*tilesX+mapx]=track;
                        }
                        
                        i++;
                        j++;
                    }
                }
            }

            // avergage the desired speed at each tile  
            for (i=0;i<j-1;i++)
            {
                if (tspeed[i+1]<tspeed[i]) tspeed[i]=(tspeed[i]+tspeed[i+1])/2;
            }
            tspeed[j]=(tspeed[j-1]+tspeed[0])/2;
              
            

            lastTile=j;
            
            // set the start direction of the AI cars
            for (i=0;i<NO_OF_CARS;i++)
            {
                direction[i]=(float)Math.atan2(targetY[1]-yy[i],targetX[1]-xx[i])*RADTODEG;
            }
            

            
            
/****************************************************************************************************************
 * Game Loop
 */        
            
            
    
            
            
            long lastFrame=System.currentTimeMillis()-20;    // the current timestamp
            float yield=10000f;                                // nubmer of yield() calls
            float frameAverage=DESIRED_WAIT;                 //start with desired msec per frame
            
            float distance;                // the distance to the current track target
            float turnLimit;            // the nominal turning rate given the surface type
            boolean wrongWay=false;        // flag determining if the player is going the wrong way
            boolean firstRun=true;        // flag indicating the "inital" state before racing

               
            nextTrack:
                
            while (true)
            {
                
    
                // calculate the delta
                long timeNow = System.currentTimeMillis();
                
                // rolling average for the last 10 time inputs
                frameAverage = (frameAverage * 10 (timeNow - lastFrame)) 11;
                lastFrame=timeNow;
                yield+=yield*((DESIRED_WAIT/frameAverage)-1)*0.1f+0.05f;
                float delta=frameAverage/DESIRED_WAIT;
                
    
                
    
                // loop through each car
              for (i=0;i<level/4+2;i++)
              {
     
                  
                    // calculate the differences between the current car position and the target's position
                    ftemp=targetY[targetIndex[i]]-yy[i];
                    ftemp1=targetX[targetIndex[i]]-xx[i];
                         
                    // calculate the desired angle to head toward the target
                    ftemp2=(floatMath.atan2(ftemp,ftemp1)*RADTODEG+180;
                 
                  // calculate the distance between the current position and the target
                  distance=ftemp1*ftemp1+ftemp*ftemp;
                  
                  // if the car is within TARGET_AREA pixels of the target then head toward the next target
                  if (distance<TARGET_AREA)
                  {
                      targetIndex[i]++;

                      // if the last target has been reached then depending on if the player is the winner, increase the level or decrease the level
                       if (targetIndex[i]==lastTile
                       {
                          
                          if (i!=0level=(level<4)?0:level-4;
                         level++;
                           break nextTrack;
                       }
                      
                       if (i==0wrongWay=false;
                  }
                  
                  // set the desired speed;
                  ftemp1=tspeed[targetIndex[i]];  


    /****************************************************************************************************************
    * determine the current surface
    */        
                    temp3=(int) ((yy[i]+SCREEN_MIDDLE_OFFSET_Y)/TILE_SIZE_ISO)*2;
                    temp2=(int) ((xx[i]+SCREEN_MIDDLE_OFFSET_X)/TILE_SIZE_ISO);
                    temp1=(int) ((yy[i]+SCREEN_MIDDLE_OFFSET_Y)%TILE_SIZE_ISO);
                    temp=(int) ((xx[i]+SCREEN_MIDDLE_OFFSET_X)%TILE_SIZE_ISO);
                    
                    
                    if (temp<TILE_SIZE_ISO_DIV2)
                    {
                        if (temp1<TILE_SIZE_ISO_DIV2)
                        {
                            if (temp1<TILE_SIZE_ISO_DIV2-temp
                            {
                                temp3--;
                                temp+=TILE_SIZE_ISO_DIV2;
                                temp1+=TILE_SIZE_ISO_DIV2;
                            }
                        }
                        else if (temp1>TILE_SIZE_ISO_DIV2+temp)
                        {
                            temp3+=1;
                            temp+=TILE_SIZE_ISO_DIV2;
                            temp1-=TILE_SIZE_ISO_DIV2;
                        }
                        
                    }
                    else 
                    {
                        if (temp1<TILE_SIZE_ISO_DIV2)
                        {
                            if (temp1<temp-TILE_SIZE_ISO_DIV2
                            {
                                temp2++;
                                temp3--;
                                temp-=TILE_SIZE_ISO_DIV2;
                                temp1+=TILE_SIZE_ISO_DIV2;
                            }
                        }
                        else if (temp1>TILE_SIZE_ISO-temp+TILE_SIZE_ISO_DIV2)
                        {
                            temp2++;
                            temp3++;
                            temp-=TILE_SIZE_ISO_DIV2;
                            temp1-=TILE_SIZE_ISO_DIV2;
                        }
                        
                    }    
                    
                    // extract the RGB component of the surface below the car
                    temp4=((temp2>=tilesX || temp2<|| temp3>=tilesY || temp3<0)?images[0]:images[map[((temp3)*tilesX)+temp2]]).getRGB(temp,temp1);
                      
                    temp=temp4&0xFF0000//red
                    temp1=temp4&0xFF00;  //geen
                    temp2=temp4&0xFF;     //blue
                      
                    
                      
                    // we are on tarmac
                    if (temp4==TARMAC_COLOUR_INT)
                    {
                        turnLimit=Integer.MAX_VALUE;
                    }
                    else if (temp1>&& temp>0)
                    {
                        // we are on dirt
                        if (temp2==0)
                        {
                            turnLimit=MAX_SPEED*0.4F;
                        }
                        // we are on an edge
                        else if (temp2==255)
                        {
                            turnLimit=MAX_SPEED*0.85F;
                        }
                        // we are on gravel
                        else
                        {
                            turnLimit=MAX_SPEED*0.66F;
                        }
                    }
                    // we are on grass
                    else 
                    {
                        turnLimit=MAX_SPEED*0.25F;
                    }
                    

                    // if waiting for the inital space bar input...
                    if (firstRun)
                    {
                        firstRun=key[KeyEvent.VK_SPACE]==0;
                    }
                    else
                    {
    
                        ftemp=prevDistance[i];
                        
                        // flag indicating that we are heading the wrong direction
                        boolean b=(distance-ftemp)<1000 && distance>ftemp;
                        
    /****************************************************************************************************************
    * Human Control
    */    
                        
                      if (i==0)
                      {
                            if (key[KeyEvent.VK_LEFT]!=0
                            {
                                ftemp=-STEER_INCREMENT*delta;
                            }
                            else if (key[KeyEvent.VK_RIGHT]!=0
                            {
                               ftemp=STEER_INCREMENT*delta;
                            }
                            else ftemp=0;
        
                            
                            if (key[KeyEvent.VK_SPACE]!=0
                            {
                                //ftemp1=HARD_BREAKING_INCREMENT*delta;
                                breaking[i]=HARD_BREAKING;
                                //skid=true;
                            }
                            else if (key[KeyEvent.VK_UP]!=0
                            {
                                //ftemp1=-VELOCITY_INCREMENT*delta;
                                breaking[i]=NO_BREAKING;
                            }
                            else if (key[KeyEvent.VK_CONTROL]!=0
                            {
                                //ftemp1=NORMAL_BREAKING_INCREMENT*delta;
                                breaking[i]=NORMAL_BREAKING;
                            }
                            else //ftemp1=COAST_BREAKING_INCREMENT*delta;
                                breaking[i]=COAST_BREAKING;
    
                            if (!wrongWay)  
                                wrongWay=b;
                      }

    /****************************************************************************************************************
    * AI Control
    */    
                      
                      else
                      {                 
                          
                          
                          // steer toward the desired angle
                          ftemp3=ftemp2-direction[i];
                          ftemp=STEER_INCREMENT*delta*((ftemp3>((ftemp3<0)?-180:180))?1:-1);
                          ftemp3=(ftemp3<0)?(180/(360+ftemp3)):(180/(ftemp3));
                          ftemp2=(ftemp3>1)?1-(ftemp3-1):ftemp3;
                          ftemp3=ftemp1*MAX_SPEED*(float)Math.pow(ftemp2,(distance<TILE_SIZE_ISO_SQUARED)?1:((float)TILE_SIZE_ISO_SQUARED)/distance);
                        
                          
                          if (b)
                          {
                              breaking[i]=HARD_BREAKING;
                          }
                          
                          // if the current speed is less than the desired speed then we accelerate
                          else if (speed[i]<ftemp3
                          {
                              breaking[i]=NO_BREAKING;
                          }
                          else if (breaking[i]==NO_BREAKING)
                          {
                              // if the current speed is far greater than the desired speed then we deccelerate hard
                              if (ftemp3/speed[i]<.75F)
                              {
                                  breaking[i]=HARD_BREAKING;
                              }
                              // if the current speed is reasonablly faster than the desired speed then we deccelerate normally
                              else if (ftemp3/speed[i]<.95F)
                              {
                                  breaking[i]=NORMAL_BREAKING;
                              }
                              else breaking[i]=COAST_BREAKING;
                          }
                          

                      }

    /****************************************************************************************************************
    * Update car variables
    */    
                      
                      // if we are turning and are going too fast then set skid true
                      boolean skid = speed[i]>turnLimit && (breaking[i]>COAST_BREAKING || (i==&& ftemp!=0));
                      
                      // update the current car direction
                      direction[i]+=(speed[i]>turnLimit)?ftemp*(turnLimit/speed[i]):ftemp;
                      
                      
                                        
                      ftemp1=COAST_BREAKING_INCREMENT*delta;
                        if (breaking[i]==NO_BREAKING)
                        {
                              ftemp1=-VELOCITY_INCREMENT*delta;


                        }
                      
                        else if (breaking[i]==HARD_BREAKING)
                        {
                                ftemp1=HARD_BREAKING_INCREMENT*delta;
                                
                                skid=true;
                        }
                        else if (breaking[i]==NORMAL_BREAKING)
                        {
                                ftemp1=NORMAL_BREAKING_INCREMENT*delta;
                        }
                        
                          
                        speed[i]-=ftemp1;    
                        if (!skidskidIndex[i]=-1;    

                        prevDistance[i]=distance;
                    
                    // keep the speed of the car within bounds
                    speed[i]=(speed[i]<0)?0:speed[i];
                    speed[i]=(speed[i]>MAX_SPEED)?MAX_SPEED:speed[i];
                                
                    
    
                    
                    
    /****************************************************************************************************************
    * Collision Detection
    */    
                    
        
                      for (j=0;j<level/4+2;j++)
                      {
                          if (j!=i)
                          {
                              temp=0;
                              while (temp<6)
                              {
        

                                  
                                  // calc the mipoint of the current detection field 
                                    ftemp2=xx[j]+(float)Math.cos((direction[j]+DATA_VARIABLES[temp])*DEGTORAD)*COLLISION_DETECTION_FIELD_OFFEST_LENGTH_FROM_CAR_MIDDLE;
                                    ftemp3=yy[j]+(float)Math.sin((direction[j]+DATA_VARIABLES[temp++])*DEGTORAD)*COLLISION_DETECTION_FIELD_OFFEST_LENGTH_FROM_CAR_MIDDLE;
                                    
    
            
                                    
                                    while (temp%3>0// loop twice
                                    {
                                        // the current corner co-ordinates
                                        ftemp=xx[i]+(float)Math.cos((direction[i]+DATA_VARIABLES[temp])*DEGTORAD)*CAR_CORNER_TO_MIDDLE_LENGTH;
                                        ftemp1=yy[i]+(float)Math.sin((direction[i]+DATA_VARIABLES[temp++])*DEGTORAD)*CAR_CORNER_TO_MIDDLE_LENGTH;
                
    
                                        
                                      if ((ftemp2-ftemp)*(ftemp2-ftemp)+(ftemp3-ftemp1)*(ftemp3-ftemp1)<COLLISION_DETECTION_FIELD_RADIUS_SQUARED
                                      {
                                          ftemp=(float) (Math.cos((direction[i])*DEGTORAD)+Math.cos((direction[j])*DEGTORAD));
                                          ftemp1=(float) (Math.sin((direction[i])*DEGTORAD)+Math.sin((direction[j])*DEGTORAD));
                                          
                                          direction[j]=direction[i]=(float) (Math.atan2(ftemp1,ftemp)*RADTODEG);
                        
            
                                            if (temp>3// if the car has hit the other car's rear end
                                            {
                                                speed[j]+=speed[i]*HIT_CAR_BUMP;  
                                                speed[i]=speed[i]*HITTING_CAR_SLOW_DOWN;
                                            }
                                            else
                                            {
                                                speed[i]=speed[i]*HIT_FRONT_OF_CAR_SLOW_DOWN;
                                            }
    
                                      }
                                    }
                              }
                          }
                      }

      /****************************************************************************************************************
      * Update car variables
      */    
                      
                      
                        // update car's position
        
                        xx[i]+=Math.cos(direction[i]*DEGTORAD)*speed[i]*delta;
                        yy[i]+=Math.sin(direction[i]*DEGTORAD)*speed[i]*delta;
                    
                        
                        
                        // add skid segment to draw if skid flag true
                        if (skid)
                        {
                            for (j=0;j<MAX_SKIDS;j++)
                            {
                                if (timeNow-skidTime[j]>SKID_LIFE
                                {
                                    
                                    
                                    skidTime[j]=timeNow;
                                    
                                    ftemp1=(direction[i]+180)*DEGTORAD-CAR_BOUNDS_ANGLE_RAD;
                                    for (k=0;k<2;k++)
                                    {    
                                        ftemp1+=2*CAR_BOUNDS_ANGLE_RAD*k;
                                        
                                        skidX[j][k]=xx[i]+(float)Math.cos(ftemp1)*WHEEL_TO_MIDDLE_LENGTH+33;
                                        skidY[j][k]=yy[i]+(float)Math.sin(ftemp1)*WHEEL_TO_MIDDLE_LENGTH+33;
                                        
                                        temp=(skidIndex[i]==-1)?j:skidIndex[i];
                                        skidX[j][2+k]=skidX[temp][k];
                                        skidY[j][2+k]=skidY[temp][k];
                                    }
                                    skidIndex[i]=j;
                                    j=MAX_SKIDS;
                                }
                            }
                        }
                    }

              }
              
              // keep the direction angles within bounds
    
              for (i=0;i<level/4+2;i++)
              {
                  direction[i]=(direction[i]<0)?360+direction[i]:direction[i];
                  direction[i]=(direction[i]>=360)?direction[i]-360:direction[i];
                  
              }
    
    /****************************************************************************************************************
    * Drawing
    */    
              
               // update screen offsets
               temp2=(int) ((xx[0])/TILE_SIZE_ISO);        // the starting X co-ordinate for the tile map
               temp3=(int) ((yy[0])/TILE_SIZE_ISO_DIV2);    // the starting Y co-ordinate for the tile map
    
               // draw the tiles
               for (j=-1;j<tilesPerScreenY;j++)
               {
                   for (i=-1;i<tilesPerScreenX;i++)
                   {
                       img1=(i+temp2>=tilesX || i+temp2<|| j+temp3>=tilesY || j+temp3<0)?images[0]:images[map[((j+temp3)*tilesX)+temp2+i]];
    
                       
                       g.drawImage(img1,(int) (
                               ((temp3+j)%2)*-TILE_SIZE_ISO_DIV2+ // offset for drawing onto screen due to isometric grid
                               i*TILE_SIZE_ISO- 
                               ((xx[0])%TILE_SIZE_ISO)) // the screen offset X co-ordinate for the tile map graphics
                               ,
                               (int) (j*TILE_SIZE_ISO_DIV2-
                               ((yy[0])%TILE_SIZE_ISO_DIV2// the screen offset Y co-ordinate for the tile map graphics
                               ),null);
                       
                   }
    
               }
    
               // set offsets
                ftemp=-xx[0]+USER_CAR_POS_X;
                ftemp1=-yy[0]+USER_CAR_POS_Y;
    
               // draw Skids
    
                for (j=0;j<MAX_SKIDS;j++)
                {
                    if (timeNow-skidTime[j]<SKID_LIFE
                    {
                        for (k=0;k<2;k++)
                        {
                            g.drawLine((int) (skidX[j][k]+ftemp),(int)(skidY[j][k]+ftemp1),(int) (skidX[j][2+k]+ftemp),(int)(skidY[j][2+k]+ftemp1));
                        }
                    }
                }
               
               // draw the user's car
               g.drawImage(images[(int)direction[0]+300],USER_CAR_POS_X,USER_CAR_POS_Y,null);
               
               // draw the opponent cars
               for (i=1;i<level/4+2;i++)
               {
                   g.drawImage(images[(int)direction[i]+700],(int) (xx[i]+ftemp),(int)(yy[i]+ftemp1,null);
               }
             
               // draw the target track segment indicator line
               if (wrongWay)
               {
                       g.drawLine((int) (targetX[targetIndex[0]]-xx[0]+SCREEN_MIDDLE_OFFSET_X),(int)(targetY[targetIndex[0]]-yy[0]+SCREEN_MIDDLE_OFFSET_Y),USER_CAR_POS_X+33,USER_CAR_POS_Y+33);
               }
               
               // draw the level indicator blocks
               for (i=0;i<level;i++)
               {
                       g.fillRect(10+7*i,35,5,5);
               }
               
               strategy.show();
    
    

                
    
                // give some cpu time to other processes
                for(i=0;i<yield;i++)
                Thread.yield();
                
                
            }
        }

    }
    
    // get key events
     final protected void processKeyEventKeyEvent evt) {
         key[evt.getKeyCode()] = evt.getID()&1;
    }

}
Java2html