import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.Transparency;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.InputStream;
/**
* @author Nicholas Klaebe
*/
final public class a extends Frame
{
private static int[] key; // Key board input
private static final int SCREEN_WIDTH = 640; // Screen Width
private static final int SCREEN_HEIGHT = 480; // Screen Height
final public static void main( String[] a) throws Exception
{
new a();
}
a() throws Exception
{
int i,j,k; // varibles used in for loops.
int temp,temp1; // temporary integers which are used through out the game for different purposes.
final int TEXT_SCALE=9; // the end of game text scaler
final byte SPRITE_INFO_DIMENSION=6; // the number of attributes per sprite in the info array
final byte SPRITE_INFO_WIDTH_INDEX=0; // the offset into the info array containing the sprite's width
final byte SPRITE_INFO_HEIGHT_INDEX=1; // the offset into the info array containing the sprite's height
final byte SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX=2; // the offset into the info array containing the sprite's rotated square dimension
final byte SPRITE_INFO_TURN_RATE_INDEX=3; // the offset into the info array containing the sprite's rate of turn
final byte SPRITE_INFO_SPEED_INDEX=5; // the offset into the info array containing the sprite's speed
final byte SPRITE_INFO_HEALTH_INDEX=4; // the offset into the info array containing the sprite's base health
final int SCREEN_WIDTH_DIV_2 = SCREEN_WIDTH/2; // Screen Width/2
final int SCREEN_HEIGHT_DIV_2 = SCREEN_HEIGHT/2; // Screen Height/2
final byte SCREEN_WIDTH_MIN_BOUNDS = -20; // the minimum bounds of the screen for sprites (x axis)
final byte SCREEN_HEIGHT_MIN_BOUNDS = -20; // the minimum bounds of the screen for sprites (y axis)
final int SCREEN_HEIGHT_MAX_BOUNDS = SCREEN_HEIGHT+20; // the maximum bounds of the screen for sprites (x axis)
final int SCREEN_WIDTH_MAX_BOUNDS = SCREEN_WIDTH+20; // the maximum bounds of the screen for sprites (y axis)
final byte SCREEN_WIDTH_PLAYER_MIN_BOUNDS = +20; // the minimum bounds of the screen for sprites (x axis)
final byte SCREEN_HEIGHT_PLAYER_MIN_BOUNDS = +20; // the minimum bounds of the screen for sprites (y axis)
final int SCREEN_HEIGHT_PLAYER_MAX_BOUNDS = SCREEN_HEIGHT-20; // the maximum bounds of the screen for sprites (x axis)
final int SCREEN_WIDTH_PLAYER_MAX_BOUNDS = SCREEN_WIDTH-20; // the maximum bounds of the screen for sprites (y axis)
final byte MAX_PLAYER_VEL=5; // the player's maximum velocity
final short STAR_COUNT=250; // the number of scrolling stars
final byte STAR_LEVELS=5; // the number of scroll star planes
final byte STAR_LEVELS_PLUS_2=STAR_LEVELS+2; // used in determining the star's shadow brightness
final float STAR_SCROLL_VEL=3; // the speed of the star scroll at the closest plane
final byte STAR_COLOUR_STEP=(byte) (220/(STAR_LEVELS_PLUS_2)); // the colour brightness step between star scrolling planes
final byte NO_OF_POWER_UPS=5; // the number of allowable player power up gun placements
final byte NO_POWER_UP=0; // enum of indicating no power up is present
final byte PLASMA_GUN_POWER_UP=1; // enum of indicating a plasma gun power up is present
final byte SHRAPNEL_SHOT_POWERUP=2; // enum of indicating shrapnel power up is present
final byte LASER_SHOT_POWERUP=3; // enum of indicating laser power up is present
final byte SHRAPNEL_SPRITE_INDEX=6; // index into the sprite info array for information about the shrapnel sprite
final byte LASER_SPRITE_INDEX=7; // index into the sprite info array for information about the laser sprite
final byte PLAYER_SPRITE_INDEX=2; // index into the sprite info array for information about the Player sprite
final byte FIRE1_SPRITE_INDEX=5; // index into the sprite info array for information about the plasma sprite
final byte FIRE5_SPRITE_INDEX=6; // index into the sprite info array for information about the enemy fire sprite
final byte PLAYER_INDEX=11; // the player's index into entities arrays
final int START_INDEX_OF_POWER_UPS=PLAYER_INDEX+1; // the beginning index of the power up into the entities arrays
final int START_INDEX_OF_SHIPS=START_INDEX_OF_POWER_UPS+100; // the beginning index of the enemy ships into the entities arrays
final int END_INDEX_NO_OF_SHIPS=START_INDEX_OF_SHIPS+200; // the end index of the enemy ships into the entities arrays
final int START_INDEX_OF_PROJECTILES=END_INDEX_NO_OF_SHIPS+1; // the start index of the projectiles (the player's projectiles) into the entities arrays
final int START_INDEX_OF_ENEMY_PROJECTILES=START_INDEX_OF_PROJECTILES+100; // the start index of the enemy projectiles into the entities arrays
final int END_INDEX_OF_PROJECTILES=START_INDEX_OF_ENEMY_PROJECTILES+500; // the end index of the projectiles into the entities arrays
final int MAX_NO_OF_SPRITE_OBJECTS=END_INDEX_OF_PROJECTILES+1; // the maximum number of entities in the game
final int START_INDEX_OF_EXPLOSIONS=MAX_NO_OF_SPRITE_OBJECTS; // maximum number objects in the game
final int MAX_NO_OF_OBJECTS=START_INDEX_OF_EXPLOSIONS+200;
final int WAVE_LENGTH=1000*30; // the length of time of a "wave" in milliseconds
final int NO_OF_SUB_LEVELS_PER_LEVEL=7; // the number of waves per repeating wave cycle
final int NO_OF_LEVELS=5; // the number of cycles
final int MAX_WAVES=NO_OF_LEVELS*NO_OF_SUB_LEVELS_PER_LEVEL; // the total number of waves
final byte PALLETTE_OFFSET=10; // the offset for each image's pallette and transparency information
final byte POWER_UP_RUN_INDEX=PLAYER_INDEX-1; // the index into the entities arrays describing the player's current 'run' of power ups
final byte POWER_UP_PLACEMENT1=PLAYER_INDEX-11; // the index of the player's first power up into the entities arrays
final byte NO_OF_SUB_EXPLOSIONS=7; // number of sub explosions per big explosion
final int NO_OF_SPRITE_VERSIONS=5; // the number of different sprite versions (colours and attibute difficulty increases)
final int NO_OF_SHIP_SPRITES=5; // the number of different enemy sprites
final int NO_OF_PROJECTILE_SPRITES=3; // the number of different projectile sprites
final int BASE_NO_OF_SPRITES=NO_OF_SHIP_SPRITES+NO_OF_PROJECTILE_SPRITES; // total number base (original) of sprites
final int NO_OF_SPRITES=BASE_NO_OF_SPRITES*NO_OF_SPRITE_VERSIONS; // total number of sprites
final byte[] spriteInfo=new byte[SPRITE_INFO_DIMENSION*NO_OF_SPRITES]; // the array which holds information about each sprite
final float DEGTORAD=3.1416F/180; // used to convert degees into radians
final float DEGTORAD_MULT_2=DEGTORAD*2; // used to convert degees into radians
final float RADTODEG=180F/3.1416F; // used to convert degees into radians
Graphics2D g; // a Graphics2D instance which is used through out the game for different purposes.
final BufferedImage[] images=new BufferedImage[NO_OF_SPRITES*180]; // the master array of images used in the game
BufferedImage image; // used in the creation of hte sprites
/****************************************************************************************************************
* Initalize Screen
*/
// the buffer strategey used for the video double buffer
BufferStrategy strategy;
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();
/****************************************************************************************************************
* Load Graphics
*/
// create the different sprite colour versions
for (temp1=0;temp1<NO_OF_SPRITE_VERSIONS;temp1++)
{
InputStream is=this.getClass().getResource("a.class").openStream();
//search for the magic number
while (!(is.read()=='|' && is.read()=='|'));
//read in sprite infomation
is.read(spriteInfo,temp1*SPRITE_INFO_DIMENSION*BASE_NO_OF_SPRITES,SPRITE_INFO_DIMENSION*BASE_NO_OF_SPRITES);
// modify sprite info depending on the version of the sprites
for (i=0;i<BASE_NO_OF_SPRITES;i++)
{
spriteInfo[temp1*SPRITE_INFO_DIMENSION*BASE_NO_OF_SPRITES+i*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEALTH_INDEX]=(byte) ((1F+1.5F*temp1)*spriteInfo[temp1*SPRITE_INFO_DIMENSION*BASE_NO_OF_SPRITES+i*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEALTH_INDEX]);
spriteInfo[temp1*SPRITE_INFO_DIMENSION*BASE_NO_OF_SPRITES+i*SPRITE_INFO_DIMENSION+SPRITE_INFO_SPEED_INDEX]=(byte) ((1F+0.15F*temp1)*spriteInfo[temp1*SPRITE_INFO_DIMENSION*BASE_NO_OF_SPRITES+i*SPRITE_INFO_DIMENSION+SPRITE_INFO_SPEED_INDEX]);
spriteInfo[temp1*SPRITE_INFO_DIMENSION*BASE_NO_OF_SPRITES+i*SPRITE_INFO_DIMENSION+SPRITE_INFO_TURN_RATE_INDEX]=(byte) ((1F+0.15F*temp1)*spriteInfo[temp1*SPRITE_INFO_DIMENSION*BASE_NO_OF_SPRITES+i*SPRITE_INFO_DIMENSION+SPRITE_INFO_TURN_RATE_INDEX]);
}
// the index of the start of the current version of the sprites
k=temp1*BASE_NO_OF_SPRITES;
// reset the colour palette
key=new int[PALLETTE_OFFSET*NO_OF_SPRITES];
do
{
// the index of the start of the current sprite's palette
i=k*PALLETTE_OFFSET;
// read in the index of the transparent colour
key[i++]=is.read();
// read in the number of colours used by the image
key[i++]=is.read();
// read and set the colour into the sprites' palette
do
{
key[i++]=Color.HSBtoRGB(0.003921568F*((temp1*50+(is.read()&0xFF))%255),0.003921568F*(is.read()&0xFF),0.003921568F*(is.read()&0xFF));
} while (i<key[k*PALLETTE_OFFSET+1]+k*PALLETTE_OFFSET+2);
k++;
// loop until the palette is read
} while (k<(temp1+1)*BASE_NO_OF_SPRITES);
// the index of the start of the current version of the sprites
k=temp1*BASE_NO_OF_SPRITES;
do
{
//create the image to fill.
image=new BufferedImage(spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_WIDTH_INDEX],spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEIGHT_INDEX],BufferedImage.TYPE_INT_ARGB);
// iterate over all the pixels reading in the colour index and setting the colour appropriately.
for (j=0;j<spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEIGHT_INDEX]/2;j++)
{
for (i=0;i<spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_WIDTH_INDEX];i++)
{
// set the pixel
image.setRGB(i,j,key[k*PALLETTE_OFFSET]==(temp=is.read())?0:(key[k*PALLETTE_OFFSET+temp+2]|0xFF000000));
// set the mirrored pixel
image.setRGB(i,spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEIGHT_INDEX]-j-1,key[k*PALLETTE_OFFSET]==temp?0:(key[k*PALLETTE_OFFSET+temp+2]|0xFF000000));
}
}
// create rotational images
for (i=0;i<180;i++)
{
g=(Graphics2D) (images[k*180+i]=new BufferedImage(spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX], spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX], Transparency.BITMASK)).getGraphics();
g.rotate (DEGTORAD_MULT_2*i,spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2,spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2);
g.drawImage(image,(spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]-spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_WIDTH_INDEX])/2,(spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]-spriteInfo[k*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEIGHT_INDEX])/2,null);
}
k++;
}
// loop until the sprites have been created
while (k<(temp1+1)*BASE_NO_OF_SPRITES);
}
// reset the key array for storing keyboard input
key=new int[256];
/****************************************************************************************************************
* Main Loop
*/
// loop indefinitely
for (;;)
{
byte wave=0; // the current wave
final float[] x=new float[MAX_NO_OF_OBJECTS]; // the x corordinates of the entities
final float[] y=new float[MAX_NO_OF_OBJECTS]; // the y corordinates of the entities
final float[] speed=new float[MAX_NO_OF_OBJECTS]; // the speed per tick of the entities
final short[] dir=new short[MAX_NO_OF_OBJECTS]; // the direction in degrees of the entities
final int[] misc=new int[MAX_NO_OF_OBJECTS]; // a miscelaneous data item used for different purposes by each entity
final byte[] baseGraphic=new byte[MAX_NO_OF_OBJECTS]; // the index to the 'base' sprite image associated with each entity.
byte[] health=new byte[MAX_NO_OF_OBJECTS]; // the health value of the each entity
// init player
dir[PLAYER_INDEX]=270;
baseGraphic[PLAYER_INDEX]=PLAYER_SPRITE_INDEX;
y[PLAYER_INDEX]=SCREEN_HEIGHT-100;
x[PLAYER_INDEX]=SCREEN_WIDTH_DIV_2;
health[PLAYER_INDEX]=5;
misc[POWER_UP_PLACEMENT1]=NO_OF_POWER_UPS*0+PLASMA_GUN_POWER_UP;
final int[] starX=new int[STAR_COUNT]; // the x coordinates of the stars
final float[] starY=new float[STAR_COUNT]; // the y coordinates of the stars
final byte[] starType=new byte[STAR_COUNT]; // the type (colour, speed) of the stars
// initalise star background
for (i=0;i<STAR_COUNT;i++)
{
for (j=0;j<STAR_LEVELS;j++)
{
if (Math.random()>0.2) starType[i]++;
}
starX[i]=(int) (Math.random()*SCREEN_WIDTH);
starY[i]=(float) (Math.random()*SCREEN_HEIGHT);
}
/****************************************************************************************************************
* Player alive Loop
*/
do
{
// Get hold of a graphics context for the accelerated surface
g = ((Graphics2D) strategy.getDrawGraphics());
long lastTime=System.currentTimeMillis(); // the current timestamp
// reset non-player entities' state to 'dead'
for (i=START_INDEX_OF_POWER_UPS;i<MAX_NO_OF_OBJECTS;)health[i++]=0;
/****************************************************************************************************************
* Level Loop
*/
final long nextWave=lastTime+WAVE_LENGTH; // the timestamp of the next Wave
nextWave:
// loop while the players health is positive
while (health[PLAYER_INDEX]!=0)
{
/****************************************************************************************************************
* Game Logic Loop
*/
// perform game ticks until caught up
while (lastTime<System.currentTimeMillis())
{
lastTime+=25;
// if the wave time limit has been reached then break out of the level loop
if (lastTime>nextWave)
break nextWave;
// Creation of enemy ships
if (Math.random()>0.98-(0.01*(wave/NO_OF_SUB_LEVELS_PER_LEVEL)))
{
// get next index of non-alive entity
i=START_INDEX_OF_SHIPS;
while (health[i]!=0) i++;
// create the ship's starting coordinates.
// ships will progressively spawn further down around the edges dependind on the current wave
temp=(int) (Math.random()*(90+wave*10)-45-wave*5-90);
x[i]=SCREEN_WIDTH_DIV_2;
y[i]=SCREEN_HEIGHT_DIV_2;
// inefficent but cheap in bytes means of finding the screen edge intersection given the angle from the centre point
while (x[i]>SCREEN_WIDTH_MIN_BOUNDS && y[i]>SCREEN_HEIGHT_MIN_BOUNDS && x[i]<SCREEN_WIDTH_MAX_BOUNDS && y[i]<SCREEN_HEIGHT_MAX_BOUNDS)
{
x[i]+=Math.cos(temp*DEGTORAD);
y[i]+=Math.sin(temp*DEGTORAD);
}
// determine the ship type.
// the first after 5 waves of every 7 waves consist of only the one type of ship.
// the 6 and 7th waves consist of any of the previous 5 waves' ships.
baseGraphic[i]=(byte) ((wave/NO_OF_SUB_LEVELS_PER_LEVEL)*BASE_NO_OF_SPRITES+((wave%NO_OF_SUB_LEVELS_PER_LEVEL<NO_OF_SHIP_SPRITES)?wave%NO_OF_SUB_LEVELS_PER_LEVEL:(int) (Math.random()*(NO_OF_SHIP_SPRITES))));
// determine the direction of the ship
// the ships progressivel face the player with better accuracy each wave
dir[i]=(short) (((Math.random()*(145-wave*4)+Math.atan2(y[PLAYER_INDEX]-y[i],x[PLAYER_INDEX]-x[i])*RADTODEG-(145-wave*4)/2)+360)%360);
// the speed of the ship is determined on the ship type
speed[i]=spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_SPEED_INDEX];
temp1=i;
// likewise the health points of the ship is determined by the ship type
health[i]=spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEALTH_INDEX];//(byte) (baseGraphic[i]+1);
// if there will be a 'run' of ships spawning from the same point...
// a random number of spawning ships is chosen pased on the current wave.
for (j=1;j<(int) (Math.random()*(wave/NO_OF_SUB_LEVELS_PER_LEVEL+1))+1;j++)
{
// get next index of non-alive entity
i=START_INDEX_OF_SHIPS;
while (health[i]!=0) i++;
// set the start location
x[i]=x[temp1];
y[i]=y[temp1];
baseGraphic[i]=baseGraphic[temp1];
// set the ship's spawn delay
health[i]=(byte) ((-20+(spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEALTH_INDEX]-6)*5)*j);
// set the ship's direction (if div by 2 then add 45 deg)
dir[i]=baseGraphic[i]%NO_OF_SUB_LEVELS_PER_LEVEL==2?dir[temp1]:(short) ((dir[temp1]+45)%360);
// set the ship's speed
speed[i]=spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_SPEED_INDEX];
}
}
// update the reload delay for player weapons
for (i=0;i<NO_OF_POWER_UPS;i++)
{
if (dir[i]>0) dir[i]--;
}
// handle player input
if (key[KeyEvent.VK_LEFT]!=0)
{
x[PLAYER_INDEX]-=MAX_PLAYER_VEL;
if (x[PLAYER_INDEX]<SCREEN_WIDTH_PLAYER_MIN_BOUNDS) x[PLAYER_INDEX]+=MAX_PLAYER_VEL;
}
if (key[KeyEvent.VK_RIGHT]!=0)
{
x[PLAYER_INDEX]+=MAX_PLAYER_VEL;
if (x[PLAYER_INDEX]>SCREEN_WIDTH_PLAYER_MAX_BOUNDS) x[PLAYER_INDEX]-=MAX_PLAYER_VEL;
}
if (key[KeyEvent.VK_UP]!=0)
{
y[PLAYER_INDEX]-=MAX_PLAYER_VEL;
if (y[PLAYER_INDEX]<SCREEN_HEIGHT_PLAYER_MIN_BOUNDS) y[PLAYER_INDEX]+=MAX_PLAYER_VEL;
}
if (key[KeyEvent.VK_DOWN]!=0)
{
y[PLAYER_INDEX]+=MAX_PLAYER_VEL;
if (y[PLAYER_INDEX]>SCREEN_HEIGHT_PLAYER_MAX_BOUNDS) y[PLAYER_INDEX]-=MAX_PLAYER_VEL;
}
if (key[KeyEvent.VK_SPACE]!=0)
{
// loop through player powerups
for (j=0;j<NO_OF_POWER_UPS;j++)
{
// if the player has a powerup in position "j" and this powerup is reloaded...
if (dir[j]==0 && misc[j]%NO_OF_POWER_UPS!=NO_POWER_UP)
{
k=0;
temp=0;
// loop until number of projectiles generated for this powerup is 0
do
{
// find the next "non-alive" projectile
i=START_INDEX_OF_PROJECTILES;
while (health[i]>0)i++;
x[i]=x[PLAYER_INDEX]+(-10*j+30*(j/2)-20*(j/3)+20*(j/4));//((j%2==0)?-10*(j/2):10*((j+1)/2));
y[i]=y[PLAYER_INDEX]-5+(15*j-15*(j/2));//((j>2)?25:(j>0)?10:-5);
// set the direction of the projectile
dir[i]=(short) (270-50*j+150*(j/2)-180*(j/3)-100*(j/4));
// if the power up is a plasma gun type..
if (misc[j]%NO_OF_POWER_UPS==PLASMA_GUN_POWER_UP)
{
// set the reload delay
dir[j]=10;
// set the projectile base sprite index
temp=FIRE1_SPRITE_INDEX;
}
// if the power up is a laser type..
if (misc[j]%NO_OF_POWER_UPS==LASER_SHOT_POWERUP)
{
// set the reload delay
dir[j]=20;
// set the projectile base sprite index
temp=LASER_SPRITE_INDEX;
// set the number of extra projectiles to be generated;
k+=(k==0)?2:-1;
// adjust the y co-ordinate of the projectile to be under each extra projectile
//y[i]-=k*20;
x[i]+=Math.cos(DEGTORAD*dir[i])*k*20;
y[i]+=Math.sin(DEGTORAD*dir[i])*k*20;
}
//if the power up is a shrapnel gun type..
if (misc[j]%NO_OF_POWER_UPS==SHRAPNEL_SHOT_POWERUP)
{
// deflect the projectile direction off of the direction of fire
dir[i]+=-15+(short) (Math.random()*30);
// set the reload delay
dir[j]=30;
// set the projectile base sprite index
temp=SHRAPNEL_SPRITE_INDEX;
// set the number of extra projectiles to be generated;
k+=(k==0)?4+(misc[j]/(NO_OF_POWER_UPS*2)):-1;
}
// change the projectile state to 'alive'
health[i]=1;
// set the appropriate speed, sprite and damage associate with the projectile
speed[i]=spriteInfo[((misc[j]/NO_OF_POWER_UPS)*BASE_NO_OF_SPRITES+temp)*SPRITE_INFO_DIMENSION+SPRITE_INFO_SPEED_INDEX];
misc[i]=spriteInfo[((misc[j]/NO_OF_POWER_UPS)*BASE_NO_OF_SPRITES+temp)*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEALTH_INDEX];
baseGraphic[i]=(byte)(temp+BASE_NO_OF_SPRITES*(misc[j]/NO_OF_POWER_UPS));
} while (k>0);
}
}
}
// Move Entities
for (i=START_INDEX_OF_POWER_UPS;i<MAX_NO_OF_OBJECTS;i++)
{
// wait to spawn counter
if (health[i]<0)
{
health[i]++;
if (++health[i]==0)
{
// spawn the ship by setting the ship's health greater than 0
health[i]=spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEALTH_INDEX];
}
}
// if the entitiy is 'alive'
if (health[i]>0)
{
// update the entitiy's position
x[i]+=Math.cos(dir[i]*DEGTORAD)*speed[i];
y[i]+=Math.sin(dir[i]*DEGTORAD)*speed[i];
// if the entity is a ship then perform control behaviours
if (i<START_INDEX_OF_PROJECTILES)
{
if (i>=START_INDEX_OF_SHIPS)
{
// check to see if we apply random walk behaviour to enemy ship
if (baseGraphic[i]%BASE_NO_OF_SPRITES==2)
{
dir[i]=(short) ((dir[i]-spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_TURN_RATE_INDEX]+Math.random()*spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_TURN_RATE_INDEX]*2+360)%360);
}
// check to see if we apply sine wave behaviour to enemy ship
if (baseGraphic[i]%BASE_NO_OF_SPRITES==3)
{
dir[i]+=(lastTime%1000<500)?spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_TURN_RATE_INDEX]:-spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_TURN_RATE_INDEX];
}
// check to see if we apply stop, rotate-shoot, move behaviour to enemy ship
if (baseGraphic[i]%BASE_NO_OF_SPRITES==1)
{
// if the ship has stopped then rotate
if (speed[i]==0)
{
dir[i]+=spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_TURN_RATE_INDEX];
// if we have rotated enough then set the speed of the ship to move again
if ((misc[i]-=spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_TURN_RATE_INDEX])<0) speed[i]=spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_SPEED_INDEX];
}
else
{
// delerate the ship
speed[i]-=0.05f;
// if the ship has stopped then choose a random rotation to rotate the ship to.
if (speed[i]<0)
{
speed[i]=0;
misc[i]=(int) (Math.random()*480);
}
}
}
// check to see if we apply "homing" behaviour to enemy ship
if (baseGraphic[i]%BASE_NO_OF_SPRITES==4 && Math.random()>0.8-(0.02*wave))
{
// the enemy ship aims for the player. this happens more fequently the futher waves you complete
dir[i]+=((Math.atan2(y[PLAYER_INDEX]-y[i],x[PLAYER_INDEX]-x[i])*RADTODEG-dir[i]<-180)?180-Math.atan2(y[PLAYER_INDEX]-y[i],x[PLAYER_INDEX]-x[i])*RADTODEG-dir[i]:(Math.atan2(y[PLAYER_INDEX]-y[i],x[PLAYER_INDEX]-x[i])*RADTODEG-dir[i]>180)?-180+Math.atan2(y[PLAYER_INDEX]-y[i],x[PLAYER_INDEX]-x[i])*RADTODEG-dir[i]:Math.atan2(y[PLAYER_INDEX]-y[i],x[PLAYER_INDEX]-x[i])*RADTODEG-dir[i])<0?-1*Math.random()*spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_TURN_RATE_INDEX]:Math.random()*spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_TURN_RATE_INDEX];
}
// keep the ship's direction within bounds
dir[i]=(short) (dir[i]<0?360+dir[i]:dir[i]);
dir[i]=(short) (dir[i]>=360?dir[i]-360:dir[i]);
// handle ship firing
// if the ship has stopped then we shall fire at a predetermined rate.
// or we shall randomly fire based on the current wave.
if ((speed[i]==0 && lastTime%400<25) || Math.random()>0.985-(0.005*(wave/NO_OF_SUB_LEVELS_PER_LEVEL)))
{
// find the next "non-alive" projectile entity index
j=START_INDEX_OF_ENEMY_PROJECTILES;
while (health[j]>0)j++;
// set the enemy projectile to the current ship's postion and direction
x[j]=x[i];
y[j]=y[i];
dir[j]=dir[i];
// set the projectile entity state to "alive"
health[j]=1;
// set the appropriate speed, sprite and damage associated with the projectile
baseGraphic[j]=(byte)(FIRE5_SPRITE_INDEX+BASE_NO_OF_SPRITES*(baseGraphic[i]/BASE_NO_OF_SPRITES));
speed[j]=spriteInfo[((baseGraphic[i]/BASE_NO_OF_SPRITES)*BASE_NO_OF_SPRITES+FIRE5_SPRITE_INDEX)*SPRITE_INFO_DIMENSION+SPRITE_INFO_SPEED_INDEX];
misc[j]=spriteInfo[((baseGraphic[i]/BASE_NO_OF_SPRITES)*BASE_NO_OF_SPRITES+FIRE5_SPRITE_INDEX)*SPRITE_INFO_DIMENSION+SPRITE_INFO_HEALTH_INDEX];
}
}
else
{
// decrement the time to live of the power up icon
health[i]--;
}
// check to see whether there has been a (ship, powerup)/player collision
if ((x[i]-x[PLAYER_INDEX])*(x[i]-x[PLAYER_INDEX])+(y[i]-y[PLAYER_INDEX])*(y[i]-y[PLAYER_INDEX])<(spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2+spriteInfo[PLAYER_SPRITE_INDEX*SPRITE_INFO_DIMENSION+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2)*(spriteInfo[baseGraphic[i]*SPRITE_INFO_DIMENSION+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2+spriteInfo[PLAYER_SPRITE_INDEX*SPRITE_INFO_DIMENSION+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2)/2)
{
// set the projectile state to 'dead'
health[i]=0;
// if we have hit a ship
if (i>=START_INDEX_OF_SHIPS)
{
// flash red
g.setColor(new Color(255,0,0));
misc[POWER_UP_RUN_INDEX]--;
misc[(int) (1+Math.random()*(NO_OF_POWER_UPS-1))]=NO_POWER_UP;
// if the player's health is < 0 then break to the level loop
if ((health[PLAYER_INDEX]-=2)<0) break nextWave;
// create large explosion
for (j=0;j<NO_OF_SUB_EXPLOSIONS;j++)
{
// find the next "non-alive" explosion entity index
k=START_INDEX_OF_EXPLOSIONS;
while (health[k]>0)k++;
// set the explosion coordinates
x[k]=(float) (x[i]+Math.random()*30-15);
y[k]=(float) (y[i]+Math.random()*30-15);
// set the inital explostion diameter
dir[k]=(short) (Math.random()*6) ;
// set the time to live of the explosion
health[k]=(byte) (30-dir[k]);
}
}
// if we have hit a power up
else
{
// choose between increasing player's health or add to the weapons
if (Math.random()<0.3)
{
health[PLAYER_INDEX]++;
}
else
{
misc[POWER_UP_RUN_INDEX]++;
misc[misc[POWER_UP_RUN_INDEX]%NO_OF_POWER_UPS]=(1+misc[POWER_UP_RUN_INDEX]%(NO_OF_POWER_UPS-2)+NO_OF_POWER_UPS*(misc[POWER_UP_RUN_INDEX]/NO_OF_POWER_UPS));
}
}
}
}
// if the entity is a projectile check projectile collisions
else if (i<END_INDEX_OF_PROJECTILES)
{
// if the entity is a projectile fired from the player set the range of possible collidable to be the enemy ships.
if (i<START_INDEX_OF_ENEMY_PROJECTILES)
{
temp=START_INDEX_OF_POWER_UPS;
temp1=END_INDEX_NO_OF_SHIPS;
}
// else the entity is a projectile fired from an enemy and so set the range of possible collidable to be the player
else
{
temp=PLAYER_INDEX;
temp1=START_INDEX_OF_POWER_UPS;
}
// iterate over the range of possible collidable objects
for (j=temp;j<temp1;j++)
{
// if there is a collision...
if (health[j]>0 && (int) (x[j]-x[i]+spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[j]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2)>0 && (int) (y[j]-y[i]-temp+spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[j]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2) >0 && (int) (x[j]-x[i]+spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[j]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2)<spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[j]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX] && (int) (y[j]-y[i]-temp+spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[j]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2)< spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[j]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]
&& images[baseGraphic[j]*180+(dir[j]/2)].getRGB((int) (x[j]-x[i]+spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[j]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2),(int) (y[j]-y[i]-temp+spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[j]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2))!=0)
{
// if the entity which has been hit health is <=0 then create an explosion
if ((health[j]-=misc[i])<=0)
{
// create large explosion
for (misc[j]=0;misc[j]<NO_OF_SUB_EXPLOSIONS;misc[j]++)
{
// find the next "non-alive" explosion entity index
k=START_INDEX_OF_EXPLOSIONS;
while (health[k]>0)k++;
// set the explosion coordinates
x[k]=(float) (x[i]+Math.random()*30-15);
y[k]=(float) (y[i]+Math.random()*30-15);
// set the inital explostion diameter
dir[k]=(short) (Math.random()*6) ;
// set the time to live of the explosion
health[k]=(byte) (30-dir[k]);
}
// if the ship that was destroyed....
if (j>=START_INDEX_OF_SHIPS)
{
// increase the kill count;
misc[PLAYER_INDEX]++;
// create dropped power up
if (misc[PLAYER_INDEX]%(10*(1+wave/NO_OF_SUB_LEVELS_PER_LEVEL))==0)
{
// find the next "non-alive" power up entity index
k=START_INDEX_OF_POWER_UPS;
while (health[k]>0)k++;
x[k]=x[j];
y[k]=y[j];
health[k]=127;
dir[k]=(short) (Math.random()*360);
speed[k]= 1;//(float) (1+(Math.random()*(wave/NO_OF_SUB_LEVELS_PER_LEVEL)/2));
}
}
// set the destroyed ship state to "dead"
health[j]=0;
}
// only a minor hit so display a minor explosion
else
{
// find the next "non-alive" explosion entity index
k=START_INDEX_OF_EXPLOSIONS;
while (health[k]>0)k++;
// set the explosion coordinates
x[k]=x[i];
y[k]=y[i];
// set the inital explostion diameter
dir[k]=(short) (Math.random()*6+3) ;
// set the time to live of the explosion
health[k]=8;
}
// if the hit entity is the player then flash red and reset the weapon power up
if (temp==PLAYER_INDEX)
{
misc[POWER_UP_RUN_INDEX]--;
misc[(int) (1+Math.random()*(NO_OF_POWER_UPS-1))]=NO_POWER_UP;
}
// change the projectile entity to the 'dead' state
health[i]=0;
}
}
}
// the entity is an explosion
else
{
// reduce the time to live of the explosion
health[i]--;
// increase the radius of the explosion
dir[i]++;
}
// if the entity is outside the game bounds then 'kill' the entity
if (x[i]<SCREEN_WIDTH_MIN_BOUNDS || y[i]<SCREEN_HEIGHT_MIN_BOUNDS || x[i]>SCREEN_WIDTH_MAX_BOUNDS || y[i]>SCREEN_HEIGHT_MAX_BOUNDS) health[i]=0;
}
}
// Move the background stars
for (i=0;i<STAR_COUNT;i++)
{
// move the current star down by an displacement based on the star's type
starY[i]+=STAR_SCROLL_VEL/starType[i];
// if the star is below the screen then make a new star at the top of the screen
if (starY[i]>SCREEN_HEIGHT)
{
starX[i]=(int) (Math.random()*SCREEN_WIDTH);
starY[i]=-2;
}
}
}
/****************************************************************************************************************
* Drawing
*/
// blank the screen
g.fillRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
//draw stars
for (i=0;i<STAR_COUNT;i++)
{
g.setColor(new Color(230-(starType[i]+2)*STAR_COLOUR_STEP,230-(starType[i]+2)*STAR_COLOUR_STEP,230-(starType[i]+2)*STAR_COLOUR_STEP));
g.drawOval(starX[i]-1,(int)starY[i]-1,2,2);
g.setColor(new Color(230-starType[i]*STAR_COLOUR_STEP,230-starType[i]*STAR_COLOUR_STEP,230-starType[i]*STAR_COLOUR_STEP));
g.drawOval(starX[i],(int)starY[i],1,0);
}
// draw Text
g.setColor(new Color(255,255,0));
g.drawString("W",10,50);
g.drawString(String.valueOf(wave),30,50);
g.drawString("K",10,65);
g.drawString(String.valueOf(misc[PLAYER_INDEX]),30,65);
g.drawString("H",10,80);
g.drawString(String.valueOf(health[PLAYER_INDEX]),30,80);
// draw entity sprites
for (i=PLAYER_INDEX;i<MAX_NO_OF_SPRITE_OBJECTS;i++)
{
if (health[i]>0)
{
if (i!=PLAYER_INDEX && i<START_INDEX_OF_SHIPS)
{
g.fillRect((int)x[i]-8,(int)y[i]-8,16,16);
g.drawOval((int)x[i]-13,(int)y[i]-13,26,26);
}
else
{
g.drawImage(images[baseGraphic[i]*180+(dir[i]/2)],(int) x[i]-spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[i]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2,(int) y[i]-spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[i]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2,null);
}
}
}
// draw Explosions
for (i=START_INDEX_OF_EXPLOSIONS;i<MAX_NO_OF_OBJECTS;i++)
{
if (health[i]>0)
{
g.setColor(new Color(255-dir[i],255-dir[i]*5,255-dir[i]*7));
g.drawOval((int) x[i]-dir[i]/2,(int) y[i]-dir[i]/2,dir[i],dir[i]);
}
}
// set the default screen blanking colour
g.setColor(new Color(0,0,0));
strategy.show();
// give some cpu time to other processes
Thread.yield();
}
// loop while the player's health is positive and we have not finished the game
} while (health[PLAYER_INDEX]>0 && ++wave<MAX_WAVES);
// draw end of game text
g.scale(TEXT_SCALE,TEXT_SCALE);
g.setColor(new Color(255,255,255));
g.drawString((health[PLAYER_INDEX]>0)?"WELL DONE!":"BAD LUCK!",2,31);
// wait until key pressed
while (key[KeyEvent.VK_ENTER]==0)
{
strategy.show();
Thread.yield();
}
}
}
// key board event listener
final protected void processEvent(AWTEvent e)
{
if (((KeyEvent)e).getKeyCode() == KeyEvent.VK_ESCAPE) System.exit(0);
key[((KeyEvent)e).getKeyCode()] = e.getID()&1;
}
}
|