// Shots.java:  Defines Projectile like classes for Stoneroids...

// Classes Defined:		stLaser, stPowerup

import java.awt.*;
import Stoneroids;   import GameActor;
import stShip;       import stAlienShip;   import stAsteroid;


// Laser objects derived from GameActor.  This is what you shoot...
class stLaser extends GameActor
{
	int countdown;		// Life of laser (in # of frames)

	// Initializes object to stLaser defaults
	stLaser()
	{
		super();
		status = 1;
		active = onscreen = movable = true;
		MakePolyShape(3, 15);
		color = new Color(150, 128, 0);//Color(0, 0, 128);
		solidFill = true;
		countdown = 16;
	}

	// Creates a stLaser object with location, angle & speed (no ownership)
	stLaser(double x, double y, double a, double spd, int length)
	{
		this();    reset(x, y, a, spd, 0, 0);
		countdown = length;    RotatePoly();
	}

	// Creates a stLaser object with object ownership, and Laser angle/speed
	stLaser(GameActor owner, double LAngle, double LSpeed, int length)
	{
		this();    pOwner = owner;    pOwner.NumChildren++;
		reset(owner.cx, owner.cy, owner.angle, owner.speed, LAngle, LSpeed);
		countdown = length;    RotatePoly();
	}

	// Sets the Laser in motion.  x/y is location, ang/spd are "parent's" angle/speed.
	// LAng/LSpeed are offsets from parent's angle and speed.
	void reset(double x, double y, double ang, double spd, double LAng, double LSpd)
	{
		cx = px = x;       cy = py = y;
		angle = ang + LAng;
		if (pOwner != null)
		{
			dx = pOwner.dx  +  LSpd * Math.cos(angle);
			dy = pOwner.dy  +  LSpd * Math.sin(angle);
		}
		else
		{
			dx = spd * Math.cos(ang)  +  LSpd * Math.cos(angle);
			dy = spd * Math.sin(ang)  +  LSpd * Math.sin(angle);
		}
		speed = Math.sqrt(dx*dx + dy*dy);
	}

	// Initialize the initial shape and bounding box for Laser objects.
	void MakePolyShape()
	{
		MakePolyShape(3, 7);		// Default shot size;
	}

	// The shape of the Laser is a small line if width <= 2.
	void MakePolyShape(int w, int h)
	{
		poly = new Polygon();
		if (w == 1)
		{
			poly.addPoint(0, -h/2);       poly.addPoint(0, 0);
			poly.addPoint(0, (-h/2)+h);   poly.addPoint(0, -h/2);
		}
		else
		{
			int x0 = (int)(-w/2);        int y0 = (int)(-h/2);
			poly.addPoint(x0, y0);       poly.addPoint(x0+w-1, y0);
			poly.addPoint(x0+w-1, 0);    poly.addPoint(x0+w-1, y0+h-1);
			poly.addPoint(x0, y0+h-1);   poly.addPoint(x0, 0);
			poly.addPoint(x0, y0);
		}
		bbox = poly.getBoundingBox();
		polyO = new Polygon(poly.xpoints, poly.ypoints, poly.npoints);
	}

	void Move()
	{
		super.Move();
		if (lifetime > countdown)    Death();
	}

	// Ensures that a shot doesn't explode on contact and dies immediately.
//	void Explode()
//	{
//		Death();
//	}

	// Player loses 5 points if Laser dies without hitting a target.
//	void Death()
//	{
//		if (lifetime > countdown  &&  active == true)
//		{
//			if (pOwner != null  &&  pOwner instanceof stShip)
//			{
//				pOwner.ScoreVal -= 5;
//				if (pOwner.ScoreVal < 0)    pOwner.ScoreVal = 0;
//			}
//		}
//		super.Death();
//	}

	// What object classes should collide with Lasers?
	boolean CanCollide(GameActor objY)
	{
		// ...Any but the source object the Laser came from.
		if (pOwner == objY)            return false;
		if (objY instanceof stLaser)   return false;
		return true;
	}

	// What should a Laser do if it hits something?
	void OnHit(GameActor objY)
	{
		if (!invincible)    objY.OnHit(this);
//			if (objY instanceof stAsteroid  ||  objY instanceof stAlienShip
//				||  objY instanceof stPowerup || objY instanceof stShip)
	}

	// This paint caters to specific painting needs of Lasers.
	void paint(Graphics g)
	{
		if (active == false)    return;
		if (status <= 0)
		{	super.paint(g);    return;   }

		// Fade colors as the laser gets older...
		double amtLife = (double)(countdown-lifetime) / countdown;
		if (amtLife > .5)        g.setColor(color);
		else if (amtLife > .25)  g.setColor(color.darker());
		else                     g.setColor(color.darker().darker());
		
		if (poly.npoints == 4)		// Laser with width of 1 pixel
		{	// Draw it as a line, not a filled polygon
			g.drawLine(poly.xpoints[0], poly.ypoints[0],
				poly.xpoints[2], poly.ypoints[2]);
		}
		else						// Laser is wider than 1 pixel
		{	// Use only the corners of poly, leaving out midpoints.
			Polygon laserbox = new Polygon();
			laserbox.addPoint(poly.xpoints[0], poly.ypoints[0]);
			laserbox.addPoint(poly.xpoints[1], poly.ypoints[1]);
			laserbox.addPoint(poly.xpoints[3], poly.ypoints[3]);
			laserbox.addPoint(poly.xpoints[4], poly.ypoints[4]);
			laserbox.addPoint(poly.xpoints[0], poly.ypoints[0]);
			if (solidFill)    g.fillPolygon(laserbox);	// Fill solid if true
			else              g.drawPolygon(laserbox);	// Draw outline if false
		}
	}
}



// stPowerup objects derived from GameActor.  They can appear when targets are shot...
class stPowerup extends GameActor
{
	int countdown;			// Life of the Powerup (in # of frames)
	int PowerupType;		// Type of powerup
	double angleSpeed;		// Angular Rotation

	// Initializes object to Powerup defaults
	stPowerup()
	{
		super();
		status = 1;    active = onscreen = movable = true;
		countdown = 50;    angleSpeed = PowerupType = 0;
		angle = Math.random()*2*Math.PI;
		angleSpeed = 4 + (Math.random()*8);			// Rotates 4-12 deg/frame
		angleSpeed *= ((Math.random()<.5) ?(-1) :1) * Math.PI/180;
		MakePolyShape();
		solidFill = true;    color = new Color(192, 192, 0);
	}

	// Creates a Powerup object.  Initial speed/direction is inherited by its parent...
	// Unique colors for each type of powerup.
	stPowerup(GameActor owner, int type, int length)
	{
		this();
		cx = px = owner.cx;    cy = py = owner.cy;
		angle = owner.angle;    speed = owner.speed;
		if (speed < thisApp.level/2.0)    speed = thisApp.level/2.0;
		dx = speed * Math.cos(angle);    dy = speed * Math.sin(angle);
		PowerupType = type;
		MakePolyShape();    RotatePoly();

		countdown = (int)(length*(.80+Math.random()*.40));	// 80-120% lifetime range
		if (type == 8  ||  type == 9)    countdown *= 2;	// x2 for super Powerups

		// Define colors for particular type of powerup
		if (type >= 1  &&  type <= 5)		// Yellow for Laser Powerups
			color = new Color(192, 192, 0);
		else if (type == 6)					// Blue for Shield Powerups
			color = new Color(0, 0, 160);
		else if (type == 7)					// Green for Energy/Temp Powerups
			color = new Color(0, 160, 0);
		else if (type == 8)					// Magenta for Invulnerability Powerup
			color = Color.magenta;
		else if (type == 9)					// Red for SubZero Lasers Powerup
			color = new Color(200, 50, 50);
		else								// Gray for unknown/mystery powerups
			color = Color.lightGray;
	}

	// A star-like shape for powerups.
	void MakePolyShape()
	{
		poly = new Polygon();
		poly.addPoint( 0, 7);   poly.addPoint( 2, 2);   poly.addPoint( 5, 0);
		poly.addPoint( 2,-2);   poly.addPoint( 0,-7);   poly.addPoint(-2,-2);
		poly.addPoint(-5, 0);   poly.addPoint(-2, 2);   poly.addPoint( 0, 7);
		bbox = poly.getBoundingBox();
		polyO = new Polygon(poly.xpoints, poly.ypoints, poly.npoints);
	}

	// Moves Powerup (and angular velocity)...
	void Move()
	{
		angle += angleSpeed;			// Rotate Powerup...
		super.Move();					// ...Then move it
		if (lifetime >= countdown)    Death();
	}

	// Static function to return a random powerup type, but based upon level...
	static int RandType()
	{
		int type = 0;

		// 15-40% chance it will be a "status" related powerup...
		double chance = .10 + .05*thisApp.level;
		if (chance > .40)    chance = 0.40;
		if (Math.random() < chance)
		{
			if (Math.random() < 0.15)				// 15% chance super P-Ups 8/9
				type = 8 + (int)(Math.random()*2);
			else									// Else choose Type 6 or 7
				type = 6 + (int)(Math.random()*2);
		}
		// Otherwise it will be a laser powerup...
		else
		{
			int MaxType = ((thisApp.level<5) ?thisApp.level :5);
			type = 1 + (int)(Math.random()*MaxType);	// Choose Types 1-5
		}
		return type;
	}

	// What object classes should collide with Powerups?
	boolean CanCollide(GameActor objY)
	{
		// ...Any objects except for Powerups and Asteroids.
		if (objY instanceof stPowerup)    return false;
		if (objY instanceof stAsteroid)   return false;
		return true;
	}

	// What should a Powerup do if it hits something?
	void OnHit(GameActor objY)
	{
		if (objY instanceof stLaser  &&  lifetime > 5)
		{	
			Explode();
			if (objY.pOwner != null  &&  objY.pOwner instanceof stShip)
			{
				objY.pOwner.ScoreVal -= 25;					// Lose 25 points if shot
				if (objY.pOwner.ScoreVal < 0)    objY.pOwner.ScoreVal = 0;
				objY.Death();
			}
		}
		// Hitting a Powerup is good news for you!
		if (objY instanceof stShip  ||  objY instanceof stAlienShip)
		{
			Damage(objY);    Death();
		}
	}

	// What to do when a Powerup is picked up...
	void Damage(GameActor objY)
	{
		// Powerups that work for both Ships and AlienShips...
		if (objY instanceof stShip  ||  objY instanceof stAlienShip)
		{
			// The "Weapon Powerup" sfx is in common...
			if (PowerupType >=1  &&  PowerupType <= 5)   SFX[sfxSwitch].play();

			// Powerup Type 6 adds 25-60 Shield Restoration Points...
			if (PowerupType == 6)
			{	objY.shield += 25+(Math.random()*35);    SFX[sfxPwrup1].play();   }

			// The "Shield/Energy" sfx is in common...
			if (PowerupType == 7)    SFX[sfxPwrup1].play();

			// Powerup Type 8 offers Invulnerability for 200 ticks...
			if (PowerupType == 8)
			{
				objY.shield += 20;     objY.invincible = true;
				objY.ticker += 200;    if (objY.ticker < 200)   objY.ticker = 200;
			}

			// The "Big/Other Pwrup" sfx is in common...
			if (PowerupType >= 8)    SFX[sfxPwrup2].play();
		}

		// Powerups for AlienShips only...
		if (objY instanceof stAlienShip)
		{
			// Powerup Types 1-5 to advance Alien's laser power...
			if (PowerupType >= 1  &&  PowerupType <= 5)    objY.energy++;

			// Powerup 7 for Aliens has same effect as Powerup 6 (shields)...
			if (PowerupType == 7)    objY.shield += 25+(Math.random()*35);

			// Add this powerup to the Alien's ItemList...
			((stAlienShip)objY).szItemList += String.valueOf((char)('0'+PowerupType));
//			thisApp.showStatus("ItemList: "+((stAlienShip)objY).szItemList);
		}

		// Powerups for Ships only
		if (objY instanceof stShip)
		{
			// Powerup Types 1-5 are Level 2-6 Lasers...
			if (PowerupType >= 1  &&  PowerupType <= 5)
			{
				((stShip)objY).WeaponsStack(PowerupType + 1);
				((stShip)objY).LaserPower = PowerupType + 1;
				objY.shield += 5;   objY.energy -= 5;	// Spare change 4U  :)
			}

			// Powerup Type 7 reduces Laser Temp by 25-40 Points...
			else if (PowerupType == 7)    objY.energy -= 25+(Math.random()*15);
			
			// Invulnerability Powerup for Ship also drops energy by 20...
			else if (PowerupType == 8)
			{	objY.energy -= 20;    SFX[sfxBuzz].loop();   }

			// Powerup Type 9 offers No Heat Buildup for 200 ticks...
			// Cooling rate doubles -- but collisions still build heat...
			else if (PowerupType == 9)
			{
				objY.shield += 20;    objY.energy -= 20;
				((stShip)objY).SubZero = true;
				((stShip)objY).CoolRate *= 2;	// Double Cooling Rate
				objY.ticker2 += 200;    if (objY.ticker2 < 200)   objY.ticker2 = 200;
			}
		}	// EndIf (Ship only Powerups)

		// Excess Shields/Energy from powerups are converted to points!
		if (objY instanceof stShip  ||  objY instanceof stAlienShip)
		{
			if (objY.shield > 100)
			{
				objY.ScoreVal += (int)(objY.shield-100)*5;		// Up to 300 pts
				objY.shield = 100;
			}
			if (objY.energy < 0)				// Energy not a factor for Aliens
			{
				objY.ScoreVal += (int)(-objY.energy)*5;			// Up to 200 pts
				objY.energy = 0;
			}
		}
	}

	// Special version to paint external ring around powerup...
	// Note: The ring is not part of collision detection.
	void paint(Graphics g)
	{
		// Fade colors as the powerup gets older...
		Color oldColor = new Color(color.getRGB());
		double amtLife = (double)(countdown-lifetime) / countdown;
		if (amtLife <= .5)   color = color.darker();
		if (amtLife <= .25)  color = color.darker();

		if (active  &&  status == 1)
		{
			g.setColor(color.darker());
			g.drawOval(bbox.x, bbox.y, bbox.width, bbox.height);
		}
		super.paint(g);
		color = oldColor;
	}
}
