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 a 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 main( String[] 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==0 || 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=(Graphics2D) img1.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=(Graphics2D) smallImage.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=(Graphics2D) smallImage.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=(Graphics2D) smallImage.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=(Graphics2D) smallImage.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=(Graphics2D) bigImage.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=(Graphics2D) smallImage.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=(Graphics2D) img1.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(65, 65, 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 = (Graphics2D) strategy.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-2) typeDuration=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<2 || mapx<2 || mapy>=tilesY-2 || 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==0 && 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=(float) Math.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!=0) level=(level<4)?0:level-4;
level++;
break nextTrack;
}
if (i==0) wrongWay=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<0 || 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>0 && 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==0 && 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 (!skid) skidIndex[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<0 || 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 processKeyEvent( KeyEvent evt) {
key[evt.getKeyCode()] = evt.getID()&1;
}
}
|