/*****************************************************************************/
/*                                                                           */
/*  asteroids.java                                                           */
/*  Simple applet demo/game                                                  */
/*  www.xbdev.net                                                            */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*   Keyboard Controls:                                                      */
/*                                                                           */
/*   S            - Start Game    P           - Pause Game                   */
/*   Cursor Left  - Rotate Left   Cursor Up   - Fire Thrusters               */
/*   Cursor Right - Rotate Right  Cursor Down - Fire Retro Thrusters         */
/*   Spacebar     - Fire Cannon   H           - Hyperspace                   */
/*   M            - Toggle Sound  D           - Toggle Graphics Detail       */
/*                                                                           */
/*****************************************************************************/


// Author bkenwright@xbdev.net
// javac -target 1.1 image.java

// Simply add to your html browser text:
// <applet code="asteroids.class" width="320" height="320">


// Well if its an Applet...which is a program which runs in the browser
// we need to add this, so we know what an applet is...and to inherit from it :)
// Just one of those things...nothing to worry about.
// import java.applet.Applet;


import java.awt.*;             // For variables Dimension, Font, myShape, Shape
import java.awt.event.*;       // Keyboard input events
import java.applet.Applet;     // It is an applet!

/*****************************************************************************/
/*                                                                           */
/* myShape class                                                             */
/*                                                                           */
/* Container class holds a set of coords wich make up the shape.  Array of   */
/* x and y values.  These values form an enclosed shape - we can use this    */
/* information in some of the member functions to do collion detection.!     */
/*                                                                           */
/*****************************************************************************/
class myShape
{
	static int MAX_NUM_POINTS = 100;   // Maximum size of our array

	public int[] xpoints;              // Array of points
	public int[] ypoints;
	public int npoints;                // Total number of points

	// Default Constructor
	myShape()
	{
		xpoints = new int[MAX_NUM_POINTS];
		ypoints = new int[MAX_NUM_POINTS];
		npoints = 0;
	}

	public void addPoint(int x, int y)
	{
		xpoints[npoints] = x;
		ypoints[npoints] = y;
		npoints++;
	}

	public boolean contains(int x, int y)
	{
		boolean bInside = false;
		
		bInside = insidePoly(this.xpoints, this.ypoints, this.npoints,
			                  x, y);

		return !bInside;
	}

	// This function isn't used - but is good for simple tests!  Won't
	// work for complicated shapes, such as stars etc.  You could expand
	// the testing though.
	/*
	boolean isInsideLine(double px, double py,
		double x0, double y0, double x1, double y1)
	{
		double dirx = x1 - x0;
		double diry = y1 - y0;


		double ang = -3.14/2.0;
		double nx = dirx*Math.cos( ang ) - diry*Math.sin( ang );
		double ny = dirx*Math.sin( ang ) + diry*Math.cos( ang );

		// Normalize our normals
		double length = Math.sqrt(Math.abs(nx*nx + ny*ny));

		nx = nx/length;
		ny = ny/length;


		double d = nx*x1 + ny*y1;
		double k = nx*px + ny*py;

		//this.Text = "Normal: " + px.ToString() + " " + py.ToString();
		if( k<d )
			return true;
		else
			return false;

	}//End isInsideLine(..)
	*/

	// Simple function - you pass an array of coords and a point, and it
	// determines if the point is inside the object.  The coords must 
	// form a closed object.
	boolean insidePoly( int xcoords[], int ycoords[], int num_coords,
		                int x0, int y0 )
	{
		//return false;
		// Line far away that we can use to reference our point to.
		double px = 100;
		double py = 400;

		int iLen = num_coords;
		//iLen -= 1;

		int totalIntersections = 0;
		for(int i=0; i<iLen; i+=1)        
		{
			int j=i;
			double p0x;
			double p0y;

			double p1x;
			double p1y;

			if( i==(iLen-1))
			{
				// Need to close the shape, first and last points
				p0x = xcoords[num_coords-1];
				p0y = ycoords[num_coords-1];

				p1x = xcoords[0];
				p1y = ycoords[0];
			}
			else
			{
				if( (j+1) >= num_coords ) System.out.println("*****BUFFER ERROR****");
				p0x = xcoords[j+0];
				p0y = ycoords[j+0];

				p1x = xcoords[j+1];
				p1y = ycoords[j+1];
			}

			if( IntersectLines2D( p0x, p0y, p1x, p1y,
				px, py, x0, y0) > 0 )  totalIntersections++;

		}

		if( (totalIntersections % 2)==0 ) // even
			return true;
		else
			return false;
	}// End insidePoly(..)

	// Used by insidePoly(..), it return true or false if the two lines
	// cross each other.
	int IntersectLines2D( double p0x, double p0y, double p1x, double p1y,
		double p2x, double p2y, double p3x, double p3y)
	{
		// Uses parametric lines. (e.g. p = p0 + (p1-p0)*t ) where t is from 0 to 1;
		double p0vx  = p1x - p0x;
		double p0vy  = p1y - p0y;

		double p1vx  = p3x - p2x;
		double p1vy  = p3y - p2y;

		// Test -1- Check for parallel ines.  If the direction vectors are scalar
		// multiples then the lines are parallel and can't possibly intersect unless
		// the lines overlap
		double det_p0p1 = (p0vx*p1vy - p0vy*p1vx);
		if( Math.abs( det_p0p1) <= 0.000001 )
			return 0; // Parallel lines - hence no intersection

		// Test -2- Computer the t0 and t1 values for intersection, where we have
		// two lines of hte form
		// p = p0 + v*t
		// p1 = p10 + v1*t1

		// p1.x = p10.x + v1.x*t1
		// p1.y = p10.y + v1.y*t1

		// p2 = p20 + v2*t2

		// p2.x = p20.x + v2.x*t2
		// p2.y = p20.y + v2.y*t2
		// Solve the system when x1 = x2 and y1 = y2

		double t0 = (p1vx*(p0y - p2y) - p1vy*(p0x - p2x) ) / det_p0p1;
		double t1 = (p0vx*(p0y - p2y) - p0vy*(p0x - p2x) ) / det_p0p1;
		

		if( (t0>=0) && (t0<=1) && (t1>=0) && (t1<=1) )
			return 1; // We have an intersection
		else
			return 0; // Intersects out of bounds.

	}//End IntersectLines2D(..)
};

/*****************************************************************************/
/*                                                                           */
/* AsteroidObject class                                                      */
/*                                                                           */
/* Its so much easier to keep things together, so each object, rocks, our    */
/* ship, its bullets, alien ships are all contained in a class...this        */
/* class.  Tidy eh.  We can do collision detection and rendering all here    */
/* Movement updates and rotations.                                           */
/*                                                                           */
/*****************************************************************************/
class AsteroidObject
{
	// Fields:
	static int width;          // Dimensions of the graphics area.
	static int height;

	myShape shape;             // Base sprite shape, centered at the origin (0,0).
	boolean active;            // Active flag.
	double  angle;             // Current angle of rotation.
	double  deltaAngle;        // Amount to change the rotation angle.
	double  x, y;              // Current position on screen.
	double  deltaX, deltaY;    // Amount to change the screen position.
	myShape sprite;            // Final location and shape of sprite after
								// applying rotation and translation to get screen
								// position. Used for drawing on the screen and in
								// detecting collisions.

	// Constructors:
	public AsteroidObject() 
	{
		this.shape = new myShape();
		this.active = false;
		this.angle = 0.0;
		this.deltaAngle = 0.0;
		this.x = 0.0;
		this.y = 0.0;
		this.deltaX = 0.0;
		this.deltaY = 0.0;
		this.sprite = new myShape();
	}

	// Methods:

	public boolean advance() 
	{
		boolean wrapped;

		// Update the rotation and position of the sprite based on the delta
		// values. If the sprite moves off the edge of the screen, it is wrapped
		// around to the other side and TRUE is returnd.

		this.angle += this.deltaAngle;
		if (this.angle < 0)
			this.angle += 2 * Math.PI;
		if (this.angle > 2 * Math.PI)
			this.angle -= 2 * Math.PI;
		wrapped = false;
		this.x += this.deltaX;
		if (this.x < -width / 2) 
		{
			this.x += width;
			wrapped = true;
		}
		if (this.x > width / 2) 
		{
			this.x -= width;
			wrapped = true;
		}
		this.y -= this.deltaY;
		if (this.y < -height / 2) 
		{
			this.y += height;
			wrapped = true;
		}
		if (this.y > height / 2) 
		{
			this.y -= height;
			wrapped = true;
		}

		return wrapped;
	}//End advance(..)

	public void render() 
	{
		int i;

		// Render the sprite's shape and location by rotating it's base shape and
		// moving it to it's proper screen position.
		this.sprite = new myShape();
		for (i = 0; i < this.shape.npoints; i++)
		this.sprite.addPoint((int) Math.round(this.shape.xpoints[i] * Math.cos(this.angle) + this.shape.ypoints[i] * Math.sin(this.angle)) + (int) Math.round(this.x) + width / 2,
							(int) Math.round(this.shape.ypoints[i] * Math.cos(this.angle) - this.shape.xpoints[i] * Math.sin(this.angle)) + (int) Math.round(this.y) + height / 2);
	}//End render(..)

	public boolean isColliding(AsteroidObject s) 
	{
		int i;

		// Determine if one sprite overlaps with another, i.e., if any vertice
		// of one sprite lands inside the other.
		
		for (i = 0; i < s.sprite.npoints; i++)
		if (this.sprite.contains(s.sprite.xpoints[i], s.sprite.ypoints[i]))
			return true;
		for (i = 0; i < this.sprite.npoints; i++)
		if (s.sprite.contains(this.sprite.xpoints[i], this.sprite.ypoints[i]))
			return true;
		return false;
		

		//for (i = 0; i < s.sprite.npoints; i++)
		//	if (this.sprite.contains(s.sprite.xpoints[i], s.sprite.ypoints[i]))
		//		return true;
		//return false;
		

	}//End isColliding(..)

	// DEBUG FUNCTION!
	public void RenderisCollision(Graphics  offGraphics, AsteroidObject s)
	{
		for (int i = 0; i < s.sprite.npoints; i++)
			if (this.sprite.contains(s.sprite.xpoints[i], s.sprite.ypoints[i]))
			{
				offGraphics.setColor(new Color(255, 0, 0));

				// Draws a sequence of connected lines defined by arrays of x and y coordinates.
				offGraphics.drawPolyline( s.sprite.xpoints, // int[] x points
										s.sprite.ypoints, // int[] y points
										s.sprite.npoints);// number points
			}
	}

	public void drawPolyline(Graphics  offGraphics, Color col)
	{
		if( col != null )
		offGraphics.setColor(col);

		// Draws a sequence of connected lines defined by arrays of x and y coordinates.
		offGraphics.drawPolyline( this.sprite.xpoints, // int[] x points
								  this.sprite.ypoints, // int[] y points
			                      this.sprite.npoints);// number points
	}

	public void fillPolygon(Graphics  offGraphics, Color col)
	{
		if( col != null )
			offGraphics.setColor(col);

		offGraphics.fillPolygon ( this.sprite.xpoints, // int[] x points
			                      this.sprite.ypoints, // int[] y points
			                      this.sprite.npoints);// number points
	}

}//End class AsteroidObject




/*****************************************************************************/
/*                                                                           */
/* asteroids class                                                           */
/*                                                                           */
/* Entry point class - where doing an applet so this is our applet entry     */
/* point, well the class that we start at.  All our main code is in here.    */
/*                                                                           */
/*****************************************************************************/

public class asteroids extends Applet implements Runnable, KeyListener 
{
	// Strings
	String szName = "Asteroids";
	String szVers = "Can you rise to the challenge?";

	// Thread control variables.
	Thread loopThread;

	// Constants
	static final int DELAY = 20;             // Milliseconds between screen and
	static final int FPS   =                 // the resulting frame rate.
		Math.round(1000 / DELAY);

	static final int MAX_SHOTS =  8;          // Maximum number of sprites
	static final int MAX_ROCKS =  8;          // for photons, asteroids and
	static final int MAX_SCRAP = 40;          // explosions.

	static final int SCRAP_COUNT  = 2 * FPS;  // Timer counter starting values
	static final int HYPER_COUNT  = 3 * FPS;  // calculated using number of
	static final int MISSLE_COUNT = 4 * FPS;  // seconds x frames per second.
	static final int STORM_PAUSE  = 2 * FPS;

	static final int    MIN_ROCK_SIDES =   6; // Ranges for asteroid shape, size
	static final int    MAX_ROCK_SIDES =  16; // speed and rotation.
	static final int    MIN_ROCK_SIZE  =  20;
	static final int    MAX_ROCK_SIZE  =  40;
	static final double MIN_ROCK_SPEED =  40.0 / FPS;
	static final double MAX_ROCK_SPEED = 240.0 / FPS;
	static final double MAX_ROCK_SPIN  = Math.PI / FPS;

	static final int MAX_SHIPS = 3;           // Starting number of ships for
												// each game.
	static final int UFO_PASSES = 3;          // Number of passes for flying
												// saucer per appearance.

	// Ship's rotation and acceleration rates and maximum speed.
	static final double SHIP_ANGLE_STEP = Math.PI / FPS;
	static final double SHIP_SPEED_STEP = 15.0 / FPS;
	static final double MAX_SHIP_SPEED  = 1.25 * MAX_ROCK_SPEED;

	static final int FIRE_DELAY = 50;         // Minimum number of milliseconds
												// required between photon shots.

	// Probablility of flying saucer firing a missle during any given frame
	// (other conditions must be met).
	static final double MISSLE_PROBABILITY = 0.45 / FPS;

	static final int BIG_POINTS    =  25;     // Points scored for shooting
	static final int SMALL_POINTS  =  50;     // various objects.
	static final int UFO_POINTS    = 250;
	static final int MISSLE_POINTS = 500;

	// Number of points the must be scored to earn a new ship or to cause the
	// flying saucer to appear.
	static final int NEW_SHIP_POINTS = 5000;
	static final int NEW_UFO_POINTS  = 2750;


	// Background stars.
	int     numStars;
	Point[] stars;

	// Game data.
	int score;
	int highScore;
	int newShipScore;
	int newUfoScore;


	// Flags for game state and options.
	//boolean loaded = false;
	boolean paused;
	boolean playing;
	boolean detail;

	// Key flags.
	boolean left  = false;
	boolean right = false;
	boolean up    = false;
	boolean down  = false;


	// Sprite objects.
	AsteroidObject   ship;
	AsteroidObject   fwdThruster, revThruster;
	AsteroidObject   ufo;
	AsteroidObject   missle;
	AsteroidObject[] photons    = new AsteroidObject[MAX_SHOTS];
	AsteroidObject[] asteroids  = new AsteroidObject[MAX_ROCKS];
	AsteroidObject[] explosions = new AsteroidObject[MAX_SCRAP];


	// Ship data.
	int shipsLeft;       // Number of ships left in game, including current one.
	int shipCounter;     // Timer counter for ship explosion.
	int hyperCounter;    // Timer counter for hyperspace.


	// Photon data.
	int   photonIndex;    // Index to next available photon sprite.
	long  photonTime;     // Time value used to keep firing rate constant.


	// Flying saucer data.
	int ufoPassesLeft;    // Counter for number of flying saucer passes.
	int ufoCounter;       // Timer counter used to track each flying saucer pass.


	// Missle data.
	int missleCounter;    // Counter for life of missle.


	// Asteroid data.
	boolean[] asteroidIsSmall = new boolean[MAX_ROCKS];    // Asteroid size flag.
	int       asteroidsCounter;                            // Break-time counter.
	double    asteroidsSpeed;                              // Asteroid speed.
	int       asteroidsLeft;                               // Number of active asteroids.


	// Explosion data.
	int[] explosionCounter = new int[MAX_SCRAP];  // Time counters for explosions.
	int   explosionIndex;                         // Next available explosion sprite.

	// Off screen image.
	Dimension offDimension;
	Image     offImage;
	Graphics  offGraphics;


	// Data for the screen font.
	Font font      = new Font("Helvetica", Font.BOLD, 12);
	FontMetrics fm = getFontMetrics(font);
	int fontWidth  = fm.getMaxAdvance();
	int fontHeight = fm.getHeight();


	public void init() 
	{

		Dimension d = getSize();
		int i;

		// Set up key event handling and set focus to applet window.
		addKeyListener(this);
		requestFocus();

		// Save the screen size.

		AsteroidObject.width = d.width;
		AsteroidObject.height = d.height;

		// Generate the starry background.
		numStars = AsteroidObject.width * AsteroidObject.height / 5000;
		stars = new Point[numStars];
		for (i = 0; i < numStars; i++)
		stars[i] = new Point((int) (Math.random() * AsteroidObject.width), (int) (Math.random() * AsteroidObject.height));

		// Create shape for the ship sprite.
		ship = new AsteroidObject();
		ship.shape.addPoint(0, -10);
		ship.shape.addPoint(7, 10);
		ship.shape.addPoint(-7, 10);

		// Create shapes for the ship thrusters.
		fwdThruster = new AsteroidObject();
		fwdThruster.shape.addPoint(0, 12);
		fwdThruster.shape.addPoint(-3, 16);
		fwdThruster.shape.addPoint(0, 26);
		fwdThruster.shape.addPoint(3, 16);
		revThruster = new AsteroidObject();
		revThruster.shape.addPoint(-2, 12);
		revThruster.shape.addPoint(-4, 14);
		revThruster.shape.addPoint(-2, 20);
		revThruster.shape.addPoint(0, 14);
		revThruster.shape.addPoint(2, 12);
		revThruster.shape.addPoint(4, 14);
		revThruster.shape.addPoint(2, 20);
		revThruster.shape.addPoint(0, 14);

		// Create shape for each photon sprites.
		for (i = 0; i < MAX_SHOTS; i++) {
		photons[i] = new AsteroidObject();
		photons[i].shape.addPoint(1, 1);
		photons[i].shape.addPoint(1, -1);
		photons[i].shape.addPoint(-1, 1);
		photons[i].shape.addPoint(-1, -1);
		}

		// Create shape for the flying saucer.
		ufo = new AsteroidObject();
		ufo.shape.addPoint(-15, 0);
		ufo.shape.addPoint(-10, -5);
		ufo.shape.addPoint(-5, -5);
		ufo.shape.addPoint(-5, -8);
		ufo.shape.addPoint(5, -8);
		ufo.shape.addPoint(5, -5);
		ufo.shape.addPoint(10, -5);
		ufo.shape.addPoint(15, 0);
		ufo.shape.addPoint(10, 5);
		ufo.shape.addPoint(-10, 5);

		// Create shape for the guided missle.
		missle = new AsteroidObject();
		missle.shape.addPoint(0, -4);
		missle.shape.addPoint(1, -3);
		missle.shape.addPoint(1, 3);
		missle.shape.addPoint(2, 4);
		missle.shape.addPoint(-2, 4);
		missle.shape.addPoint(-1, 3);
		missle.shape.addPoint(-1, -3);

		// Create asteroid sprites.
		for (i = 0; i < MAX_ROCKS; i++)
		asteroids[i] = new AsteroidObject();

		// Create explosion sprites.

		for (i = 0; i < MAX_SCRAP; i++)
		explosions[i] = new AsteroidObject();

		// Initialize game data and put us in 'game over' mode.
		highScore = 0;
		detail = true;
		initGame();
		endGame();
	}

	public void initGame() 
	{

		// Initialize game data and sprites.

		score = 0;
		shipsLeft = MAX_SHIPS;
		asteroidsSpeed = MIN_ROCK_SPEED;
		newShipScore = NEW_SHIP_POINTS;
		newUfoScore = NEW_UFO_POINTS;
		initShip();
		initPhotons();
		stopUfo();
		stopMissle();
		initAsteroids();
		initExplosions();
		playing = true;
		paused = false;
		photonTime = System.currentTimeMillis();
	}

	public void endGame() {

		// Stop ship, flying saucer, guided missle and associated sounds.

		playing = false;
		stopShip();
		stopUfo();
		stopMissle();
	}

	public void start() 
	{
		if (loopThread == null) 
		{
			loopThread = new Thread(this);
			loopThread.start();
		}
	}

	public void stop() 
	{
		if (loopThread != null) 
		{
			loopThread.stop();
			loopThread = null;
		}
	}

	public void run() 
	{
		int i, j;
		long startTime;

		// Lower this thread's priority and get the current time.
		Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
		startTime = System.currentTimeMillis();

		// This is the main loop.
		while (Thread.currentThread() == loopThread) 
		{

		if (!paused) 
		{

			// Move and process all sprites.

			updateShip();
			updatePhotons();
			updateUfo();
			updateMissle();
			updateAsteroids();
			updateExplosions();

			// Check the score and advance high score, add a new ship or start the
			// flying saucer as necessary.

			if (score > highScore)
			highScore = score;
			if (score > newShipScore) {
			newShipScore += NEW_SHIP_POINTS;
			shipsLeft++;
			}
			if (playing && score > newUfoScore && !ufo.active) {
			newUfoScore += NEW_UFO_POINTS;
			ufoPassesLeft = UFO_PASSES;
			initUfo();
			}

			// If all asteroids have been destroyed create a new batch.

			if (asteroidsLeft <= 0)
				if (--asteroidsCounter <= 0)
				initAsteroids();
		}

		// Update the screen and set the timer for the next loop.

		repaint();
		try {
			startTime += DELAY;
			Thread.sleep(Math.max(0, startTime - System.currentTimeMillis()));
		}
		catch (InterruptedException e) {
			break;
		}
		}
	}


	public void initShip() 
	{
		// Reset the ship sprite at the center of the screen.
		ship.active = true;
		ship.angle = 0.0;
		ship.deltaAngle = 0.0;
		ship.x = 0.0;
		ship.y = 0.0;
		ship.deltaX = 0.0;
		ship.deltaY = 0.0;
		ship.render();

		// Initialize thruster sprites.
		fwdThruster.x = ship.x;
		fwdThruster.y = ship.y;
		fwdThruster.angle = ship.angle;
		fwdThruster.render();
		revThruster.x = ship.x;
		revThruster.y = ship.y;
		revThruster.angle = ship.angle;
		revThruster.render();

		hyperCounter = 0;
	}

	public void updateShip() 
	{
		double dx, dy, speed;

		if (!playing)
		return;

		// Rotate the ship if left or right cursor key is down.

		if (left) {
		ship.angle += SHIP_ANGLE_STEP;
		if (ship.angle > 2 * Math.PI)
			ship.angle -= 2 * Math.PI;
		}
		if (right) {
		ship.angle -= SHIP_ANGLE_STEP;
		if (ship.angle < 0)
			ship.angle += 2 * Math.PI;
		}

		// Fire thrusters if up or down cursor key is down.

		dx = SHIP_SPEED_STEP * -Math.sin(ship.angle);
		dy = SHIP_SPEED_STEP *  Math.cos(ship.angle);
		if (up) 
		{
			ship.deltaX += dx;
			ship.deltaY += dy;
		}
		if (down) 
		{
			ship.deltaX -= dx;
			ship.deltaY -= dy;
		}

		// Don't let ship go past the speed limit.

		if (up || down) 
		{
			speed = Math.sqrt(ship.deltaX * ship.deltaX + ship.deltaY * ship.deltaY);
			if (speed > MAX_SHIP_SPEED) 
			{
				dx = MAX_SHIP_SPEED * -Math.sin(ship.angle);
				dy = MAX_SHIP_SPEED *  Math.cos(ship.angle);
				if (up)
				ship.deltaX = dx;
				else
				ship.deltaX = -dx;
				if (up)
				ship.deltaY = dy;
				else
				ship.deltaY = -dy;
			}
		}

		// Move the ship. If it is currently in hyperspace, advance the countdown.
		if (ship.active) {
		ship.advance();
		ship.render();
		if (hyperCounter > 0)
			hyperCounter--;

		// Update the thruster sprites to match the ship sprite.
		fwdThruster.x = ship.x;
		fwdThruster.y = ship.y;
		fwdThruster.angle = ship.angle;
		fwdThruster.render();
		revThruster.x = ship.x;
		revThruster.y = ship.y;
		revThruster.angle = ship.angle;
		revThruster.render();
		}

		// Ship is exploding, advance the countdown or create a new ship if it is
		// done exploding. The new ship is added as though it were in hyperspace.
		// (This gives the player time to move the ship if it is in imminent
		// danger.) If that was the last ship, end the game.

		else
		if (--shipCounter <= 0)
			if (shipsLeft > 0) {
			initShip();
			hyperCounter = HYPER_COUNT;
			}
			else
			endGame();
	}

	public void stopShip() 
	{

		ship.active = false;
		shipCounter = SCRAP_COUNT;
		if (shipsLeft > 0)
			shipsLeft--;
	}

	public void initPhotons() 
	{
		int i;

		for (i = 0; i < MAX_SHOTS; i++)
			photons[i].active = false;
		photonIndex = 0;
	}

	public void updatePhotons() 
	{

		int i;

		// Move any active photons. Stop it when its counter has expired.

		for (i = 0; i < MAX_SHOTS; i++)
		if (photons[i].active) 
		{
			if (!photons[i].advance())
				photons[i].render();
			else
				photons[i].active = false;
		}
	}

	public void initUfo() 
	{

		double angle, speed;

		// Randomly set flying saucer at left or right edge of the screen.

		ufo.active = true;
		ufo.x = -AsteroidObject.width / 2;
		ufo.y = Math.random() * 2 * AsteroidObject.height - AsteroidObject.height;
		angle = Math.random() * Math.PI / 4 - Math.PI / 2;
		speed = MAX_ROCK_SPEED / 2 + Math.random() * (MAX_ROCK_SPEED / 2);
		ufo.deltaX = speed * -Math.sin(angle);
		ufo.deltaY = speed *  Math.cos(angle);
		if (Math.random() < 0.5) 
		{
			ufo.x = AsteroidObject.width / 2;
			ufo.deltaX = -ufo.deltaX;
		}
		if (ufo.y > 0)
			ufo.deltaY = ufo.deltaY;
		ufo.render();
		
		ufoCounter = (int) Math.abs(AsteroidObject.width / ufo.deltaX);
	}

	public void updateUfo() 
	{

		int i, d;
		boolean wrapped;

		// Move the flying saucer and check for collision with a photon. Stop it
		// when its counter has expired.

		if (ufo.active) {
		if (--ufoCounter <= 0) {
			if (--ufoPassesLeft > 0)
			initUfo();
			else
			stopUfo();
		}
		if (ufo.active) {
			ufo.advance();
			ufo.render();
			for (i = 0; i < MAX_SHOTS; i++)
			if (photons[i].active && ufo.isColliding(photons[i])) {
				//if (sound)
				//  crashSound.play();
				explode(ufo);
				stopUfo();
				score += UFO_POINTS;
			}

			// On occassion, fire a missle at the ship if the saucer is not too
			// close to it.
			d = (int) Math.max(Math.abs(ufo.x - ship.x), Math.abs(ufo.y - ship.y));
			if (ship.active && hyperCounter <= 0 &&
				ufo.active && !missle.active &&
				d > MAX_ROCK_SPEED * FPS / 2 &&
				Math.random() < MISSLE_PROBABILITY)
				initMissle();
		}
		}
	}

	public void stopUfo() 
	{

		ufo.active = false;
		ufoCounter = 0;
		ufoPassesLeft = 0;
	}

	public void initMissle() 
	{

		missle.active = true;
		missle.angle = 0.0;
		missle.deltaAngle = 0.0;
		missle.x = ufo.x;
		missle.y = ufo.y;
		missle.deltaX = 0.0;
		missle.deltaY = 0.0;
		missle.render();
		missleCounter = MISSLE_COUNT;
	}

	public void updateMissle() 
	{

		int i;

		// Move the guided missle and check for collision with ship or photon. Stop
		// it when its counter has expired.

		if (missle.active) {
		if (--missleCounter <= 0)
			stopMissle();
		else {
			guideMissle();
			missle.advance();
			missle.render();
			for (i = 0; i < MAX_SHOTS; i++)
			if (photons[i].active && missle.isColliding(photons[i])) 
			{
				explode(missle);
				stopMissle();
				score += MISSLE_POINTS;
			}
			if (missle.active && ship.active &&
				hyperCounter <= 0 && ship.isColliding(missle)) 
			{
				//System.out.println("ship collision");
				
				explode(ship);
				stopShip();
				stopUfo();
				stopMissle();
				
			}
			//System.out.println("");
		}
		}
	}

	public void guideMissle() 
	{

		double dx, dy, angle;

		if (!ship.active || hyperCounter > 0)
		return;

		// Find the angle needed to hit the ship.

		dx = ship.x - missle.x;
		dy = ship.y - missle.y;
		if (dx == 0 && dy == 0)
		angle = 0;
		if (dx == 0) {
		if (dy < 0)
			angle = -Math.PI / 2;
		else
			angle = Math.PI / 2;
		}
		else {
		angle = Math.atan(Math.abs(dy / dx));
		if (dy > 0)
			angle = -angle;
		if (dx < 0)
			angle = Math.PI - angle;
		}

		// Adjust angle for screen coordinates.
		missle.angle = angle - Math.PI / 2;

		// Change the missle's angle so that it points toward the ship.
		missle.deltaX = 0.75 * MAX_ROCK_SPEED * -Math.sin(missle.angle);
		missle.deltaY = 0.75 * MAX_ROCK_SPEED *  Math.cos(missle.angle);
	}

	public void stopMissle() 
	{

		missle.active = false;
		missleCounter = 0;
	}

	public void initAsteroids() 
	{

		int i, j;
		int s;
		double theta, r;
		int x, y;

		// Create random shapes, positions and movements for each asteroid.

		for (i = 0; i < MAX_ROCKS; i++) {

		// Create a jagged shape for the asteroid and give it a random rotation.

		asteroids[i].shape = new myShape();
		s = MIN_ROCK_SIDES + (int) (Math.random() * (MAX_ROCK_SIDES - MIN_ROCK_SIDES));
		for (j = 0; j < s; j ++) {
			theta = 2 * Math.PI / s * j;
			r = MIN_ROCK_SIZE + (int) (Math.random() * (MAX_ROCK_SIZE - MIN_ROCK_SIZE));
			x = (int) -Math.round(r * Math.sin(theta));
			y = (int)  Math.round(r * Math.cos(theta));
			asteroids[i].shape.addPoint(x, y);
		}
		asteroids[i].active = true;
		asteroids[i].angle = 0.0;
		asteroids[i].deltaAngle = Math.random() * 2 * MAX_ROCK_SPIN - MAX_ROCK_SPIN;

		// Place the asteroid at one edge of the screen.

		if (Math.random() < 0.5) {
			asteroids[i].x = -AsteroidObject.width / 2;
			if (Math.random() < 0.5)
			asteroids[i].x = AsteroidObject.width / 2;
			asteroids[i].y = Math.random() * AsteroidObject.height;
		}
		else {
			asteroids[i].x = Math.random() * AsteroidObject.width;
			asteroids[i].y = -AsteroidObject.height / 2;
			if (Math.random() < 0.5)
			asteroids[i].y = AsteroidObject.height / 2;
		}

		// Set a random motion for the asteroid.

		asteroids[i].deltaX = Math.random() * asteroidsSpeed;
		if (Math.random() < 0.5)
			asteroids[i].deltaX = -asteroids[i].deltaX;
		asteroids[i].deltaY = Math.random() * asteroidsSpeed;
		if (Math.random() < 0.5)
			asteroids[i].deltaY = -asteroids[i].deltaY;

		asteroids[i].render();
		asteroidIsSmall[i] = false;
		}

		asteroidsCounter = STORM_PAUSE;
		asteroidsLeft = MAX_ROCKS;
		if (asteroidsSpeed < MAX_ROCK_SPEED)
		asteroidsSpeed += 0.5;
	}

	public void initSmallAsteroids(int n) 
	{

		int count;
		int i, j;
		int s;
		double tempX, tempY;
		double theta, r;
		int x, y;

		// Create one or two smaller asteroids from a larger one using inactive
		// asteroids. The new asteroids will be placed in the same position as the
		// old one but will have a new, smaller shape and new, randomly generated
		// movements.

		count = 0;
		i = 0;
		tempX = asteroids[n].x;
		tempY = asteroids[n].y;
		do {
		if (!asteroids[i].active) {
			asteroids[i].shape = new myShape();
			s = MIN_ROCK_SIDES + (int) (Math.random() * (MAX_ROCK_SIDES - MIN_ROCK_SIDES));
			for (j = 0; j < s; j ++) {
			theta = 2 * Math.PI / s * j;
			r = (MIN_ROCK_SIZE + (int) (Math.random() * (MAX_ROCK_SIZE - MIN_ROCK_SIZE))) / 2;
			x = (int) -Math.round(r * Math.sin(theta));
			y = (int)  Math.round(r * Math.cos(theta));
			asteroids[i].shape.addPoint(x, y);
			}
			asteroids[i].active = true;
			asteroids[i].angle = 0.0;
			asteroids[i].deltaAngle = Math.random() * 2 * MAX_ROCK_SPIN - MAX_ROCK_SPIN;
			asteroids[i].x = tempX;
			asteroids[i].y = tempY;
			asteroids[i].deltaX = Math.random() * 2 * asteroidsSpeed - asteroidsSpeed;
			asteroids[i].deltaY = Math.random() * 2 * asteroidsSpeed - asteroidsSpeed;
			asteroids[i].render();
			asteroidIsSmall[i] = true;
			count++;
			asteroidsLeft++;
		}
		i++;
		} while (i < MAX_ROCKS && count < 2);
	}

	public void updateAsteroids() 
	{

		int i, j;

		// Move any active asteroids and check for collisions.
		for (i = 0; i < MAX_ROCKS; i++)
		if (asteroids[i].active) 
		{
			asteroids[i].advance();
			asteroids[i].render();

			// If hit by photon, kill asteroid and advance score. If asteroid is
			// large, make some smaller ones to replace it.
			for (j = 0; j < MAX_SHOTS; j++)
			if (photons[j].active && asteroids[i].active && asteroids[i].isColliding(photons[j])) {
				asteroidsLeft--;
				asteroids[i].active = false;
				photons[j].active = false;
				explode(asteroids[i]);
				if (!asteroidIsSmall[i]) {
				score += BIG_POINTS;
				initSmallAsteroids(i);
				}
				else
				score += SMALL_POINTS;
			}

			// If the ship is not in hyperspace, see if it is hit.

			if (ship.active && hyperCounter <= 0 &&
				asteroids[i].active && asteroids[i].isColliding(ship)) 
			//if (ship.active && hyperCounter <= 0 &&
			//		asteroids[i].active && ship.isColliding(asteroids[i])) 
			{
				//System.out.println("ship collision2");

				explode(ship);
				stopShip();
				stopUfo();
				stopMissle();

			}
			//System.out.println("");
		}
	}

	public void initExplosions() 
	{

		int i;

		for (i = 0; i < MAX_SCRAP; i++) {
		explosions[i].shape = new myShape();
		explosions[i].active = false;
		explosionCounter[i] = 0;
		}
		explosionIndex = 0;
	}

	public void explode(AsteroidObject s) 
	{

		int c, i, j;
		int cx, cy;

		// Create sprites for explosion animation. The each individual line segment
		// of the given sprite is used to create a new sprite that will move
		// outward  from the sprite's original position with a random rotation.

		s.render();
		c = 2;
		if (detail || s.sprite.npoints < 6)
		c = 1;
		for (i = 0; i < s.sprite.npoints; i += c) {
		explosionIndex++;
		if (explosionIndex >= MAX_SCRAP)
			explosionIndex = 0;
		explosions[explosionIndex].active = true;
		explosions[explosionIndex].shape = new myShape();
		j = i + 1;
		if (j >= s.sprite.npoints)
			j -= s.sprite.npoints;
		cx = (int) ((s.shape.xpoints[i] + s.shape.xpoints[j]) / 2);
		cy = (int) ((s.shape.ypoints[i] + s.shape.ypoints[j]) / 2);
		explosions[explosionIndex].shape.addPoint(
			s.shape.xpoints[i] - cx,
			s.shape.ypoints[i] - cy);
		explosions[explosionIndex].shape.addPoint(
			s.shape.xpoints[j] - cx,
			s.shape.ypoints[j] - cy);
		explosions[explosionIndex].x = s.x + cx;
		explosions[explosionIndex].y = s.y + cy;
		explosions[explosionIndex].angle = s.angle;
		explosions[explosionIndex].deltaAngle = 4 * (Math.random() * 2 * MAX_ROCK_SPIN - MAX_ROCK_SPIN);
		explosions[explosionIndex].deltaX = (Math.random() * 2 * MAX_ROCK_SPEED - MAX_ROCK_SPEED + s.deltaX) / 2;
		explosions[explosionIndex].deltaY = (Math.random() * 2 * MAX_ROCK_SPEED - MAX_ROCK_SPEED + s.deltaY) / 2;
		explosionCounter[explosionIndex] = SCRAP_COUNT;
		}
	}

	public void updateExplosions() 
	{

		int i;

		// Move any active explosion debris. Stop explosion when its counter has
		// expired.

		for (i = 0; i < MAX_SCRAP; i++)
		if (explosions[i].active) {
			explosions[i].advance();
			explosions[i].render();
			if (--explosionCounter[i] < 0)
			explosions[i].active = false;
		}
	}

	public void keyPressed(KeyEvent e) 
	{

		char c;

		// Check if any cursor keys have been pressed and set flags.

		if (e.getKeyCode() == KeyEvent.VK_LEFT)
		left = true;
		if (e.getKeyCode() == KeyEvent.VK_RIGHT)
		right = true;
		if (e.getKeyCode() == KeyEvent.VK_UP)
		up = true;
		if (e.getKeyCode() == KeyEvent.VK_DOWN)
		down = true;

		// Spacebar: fire a photon and start its counter.

		if (e.getKeyChar() == ' ' && ship.active) 
		{
		photonTime = System.currentTimeMillis();
		photonIndex++;
		if (photonIndex >= MAX_SHOTS)
			photonIndex = 0;
		photons[photonIndex].active = true;
		photons[photonIndex].x = ship.x;
		photons[photonIndex].y = ship.y;
		photons[photonIndex].deltaX = 2 * MAX_ROCK_SPEED * -Math.sin(ship.angle);
		photons[photonIndex].deltaY = 2 * MAX_ROCK_SPEED *  Math.cos(ship.angle);
		}

		// Allow upper or lower case characters for remaining keys.

		c = Character.toLowerCase(e.getKeyChar());

		// 'H' key: warp ship into hyperspace by moving to a random location and
		// starting counter.

		if (c == 'h' && ship.active && hyperCounter <= 0) 
		{
			ship.x = Math.random() * AsteroidObject.width;
			ship.y = Math.random() * AsteroidObject.height;
			hyperCounter = HYPER_COUNT;
		}
		// 'D' key: toggle graphics detail on or off.

		if (c == 'd')
		detail = !detail;

		if (c == 'p')
			paused = !paused;

		// 'S' key: start the game, if not already in progress.
		if (c == 's' && !playing)
		initGame();
	}

	public void keyReleased(KeyEvent e) 
	{

		// Check if any cursor keys where released and set flags.

		if (e.getKeyCode() == KeyEvent.VK_LEFT)
		left = false;
		if (e.getKeyCode() == KeyEvent.VK_RIGHT)
		right = false;
		if (e.getKeyCode() == KeyEvent.VK_UP)
		up = false;
		if (e.getKeyCode() == KeyEvent.VK_DOWN)
		down = false;
	}

	public void keyTyped(KeyEvent e) {}

	public void update(Graphics g) 
	{
		paint(g);
	}

	public void paint(Graphics g) 
	{
		Dimension d = getSize();
		int i;
		int c;
		String s;
		int w, h;
		int x, y;

		// Create the off screen graphics context, if no good one exists.
		if (offGraphics == null || d.width != offDimension.width || d.height != offDimension.height) 
		{
		offDimension = d;
		offImage = createImage(d.width, d.height);
		offGraphics = offImage.getGraphics();
		}

		// Fill in background and stars.
		offGraphics.setColor(Color.black);
		offGraphics.fillRect(0, 0, d.width, d.height);
		if (detail) {
		offGraphics.setColor(Color.white);
		for (i = 0; i < numStars; i++)
			offGraphics.drawLine(stars[i].x, stars[i].y, stars[i].x, stars[i].y);
		}

		// Draw photon bullets.
		offGraphics.setColor(Color.white);
		for (i = 0; i < MAX_SHOTS; i++)
			if (photons[i].active)
			{
				photons[i].drawPolyline(offGraphics, null);
			}



		// Draw the guided missle, counter is used to quickly fade color to black
		// when near expiration.
		c = Math.min(missleCounter * 24, 255);
		offGraphics.setColor(new Color(c, c, c));
		if (missle.active) 
		{
			missle.drawPolyline(offGraphics, null);

			offGraphics.drawLine(missle.sprite.xpoints[missle.sprite.npoints - 1], missle.sprite.ypoints[missle.sprite.npoints - 1],
							missle.sprite.xpoints[0], missle.sprite.ypoints[0]);
		}

		// Draw the asteroids.

		for (i = 0; i < MAX_ROCKS; i++)
		if (asteroids[i].active) {
			if (detail) 
			{
				/*
			offGraphics.setColor(Color.black);

			// Draws a sequence of connected lines defined by arrays of x and y coordinates.
			offGraphics.fillPolygon ( asteroids[i].sprite.xpoints, // int[] x points
					                  asteroids[i].sprite.ypoints, // int[] y points
					                  asteroids[i].sprite.npoints);// number points
									  */
				asteroids[i].fillPolygon(offGraphics, Color.black);

			}
			/*
			offGraphics.setColor(Color.white);

			// Draws a sequence of connected lines defined by arrays of x and y coordinates.
			offGraphics.drawPolyline( asteroids[i].sprite.xpoints, // int[] x points
				                      asteroids[i].sprite.ypoints, // int[] y points
				                      asteroids[i].sprite.npoints);// number points
			*/
			asteroids[i].drawPolyline(offGraphics, Color.white);

			offGraphics.drawLine(asteroids[i].sprite.xpoints[asteroids[i].sprite.npoints - 1], asteroids[i].sprite.ypoints[asteroids[i].sprite.npoints - 1],
								asteroids[i].sprite.xpoints[0], asteroids[i].sprite.ypoints[0]);
		}

		// Draw the flying saucer.
		if (ufo.active) 
		{
			if (detail) 
			{
				offGraphics.setColor(Color.black);

				// Draws a sequence of connected lines defined by arrays of x and y coordinates.
				offGraphics.fillPolygon ( ufo.sprite.xpoints, // int[] x points
										ufo.sprite.ypoints, // int[] y points
										ufo.sprite.npoints);// number points
			}

			ufo.drawPolyline(offGraphics, Color.white);

			offGraphics.drawLine(ufo.sprite.xpoints[ufo.sprite.npoints - 1], ufo.sprite.ypoints[ufo.sprite.npoints - 1],
								ufo.sprite.xpoints[0], ufo.sprite.ypoints[0]);
		}//End if(ufo..

		// Draw the ship, counter is used to fade color to white on hyperspace.

		c = 255 - (255 / HYPER_COUNT) * hyperCounter;
		if (ship.active) {
		if (detail && hyperCounter == 0) 
		{
			ship.drawPolyline(offGraphics, Color.black);
		}

		ship.drawPolyline(offGraphics, new Color(c, c, c));

		offGraphics.drawLine(ship.sprite.xpoints[ship.sprite.npoints - 1], ship.sprite.ypoints[ship.sprite.npoints - 1],
							ship.sprite.xpoints[0], ship.sprite.ypoints[0]);

		// Draw thruster exhaust if thrusters are on. Do it randomly to get a
		// flicker effect.

		if (!paused && detail && Math.random() < 0.5) 
		{
			if (up) 
			{
				fwdThruster.drawPolyline(offGraphics, null);

			    offGraphics.drawLine(fwdThruster.sprite.xpoints[fwdThruster.sprite.npoints - 1], fwdThruster.sprite.ypoints[fwdThruster.sprite.npoints - 1],
								     fwdThruster.sprite.xpoints[0], fwdThruster.sprite.ypoints[0]);
			}
			if (down) 
			{
				revThruster.drawPolyline(offGraphics, null);

				offGraphics.drawLine(revThruster.sprite.xpoints[revThruster.sprite.npoints - 1], revThruster.sprite.ypoints[revThruster.sprite.npoints - 1],
								revThruster.sprite.xpoints[0], revThruster.sprite.ypoints[0]);
			}
		}
		}

		// Draw any explosion debris, counters are used to fade color to black.

		for (i = 0; i < MAX_SCRAP; i++)
		if (explosions[i].active) 
		{
			c = (255 / SCRAP_COUNT) * explosionCounter [i];

			explosions[i].drawPolyline(offGraphics, new Color(c, c, c));
		}

		// Display status and messages.

		offGraphics.setFont(font);
		offGraphics.setColor(Color.white);

		offGraphics.drawString("Score: " + score, fontWidth, fontHeight);
		offGraphics.drawString("Ships: " + shipsLeft, fontWidth, d.height - fontHeight);
		s = "High: " + highScore;
		offGraphics.drawString(s, d.width - (fontWidth + fm.stringWidth(s)), fontHeight);


		// ** DEBUGGING ** //
		for (i = 0; i < MAX_ROCKS; i++)
			if (ship.active && asteroids[i].active)
			{
				ship.RenderisCollision(offGraphics, asteroids[i]);
					
			}


		int y_pos_text = d.height / 2;

		if (!playing) 
		{
			s = szName;
			offGraphics.drawString(s, (d.width - fm.stringWidth(s)) / 2, y_pos_text );
			y_pos_text += fontHeight;
			s = szVers;
			offGraphics.drawString(s, (d.width - fm.stringWidth(s)) / 2, y_pos_text);
			y_pos_text += fontHeight;
			s = "Game Over";
			offGraphics.drawString(s, (d.width - fm.stringWidth(s)) / 2, y_pos_text);
			y_pos_text += fontHeight;
			s = "'S' to Start";
			offGraphics.drawString(s, (d.width - fm.stringWidth(s)) / 2, d.height / 4 + y_pos_text);

		}
		else if (paused) 
		{
			s = "Game Paused";
			offGraphics.drawString(s, (d.width - fm.stringWidth(s)) / 2, d.height / 4);
		}

		// Copy the off screen buffer to the screen.
		g.drawImage(offImage, 0, 0, this);
	}

}//End applet class

