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 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 mainString[] athrows 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.2starType[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 = ((Graphics2Dstrategy.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]!=0i++;
                            
                            // 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]!=0i++;
                                
                                // 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]>0dir[i]--;
                        }
                        
                        // handle player input
                        if (key[KeyEvent.VK_LEFT]!=0
                        {
                            x[PLAYER_INDEX]-=MAX_PLAYER_VEL;
                            if (x[PLAYER_INDEX]<SCREEN_WIDTH_PLAYER_MIN_BOUNDSx[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_BOUNDSx[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_BOUNDSy[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_BOUNDSy[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]==&& 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])<0speed[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==&& 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]==&& 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)<0break 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]>&& (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>&& (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_BOUNDShealth[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)],(intx[i]-spriteInfo[SPRITE_INFO_DIMENSION*baseGraphic[i]+SPRITE_INFO_ROTATED_SQUARE_SIZE_INDEX]/2,(inty[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((intx[i]-dir[i]/2,(inty[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]>&& ++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_ESCAPESystem.exit(0);
        key[((KeyEvent)e).getKeyCode()] = e.getID()&1;
    }

}
Java2html