/*************************************
File:  TankGame.java
Name:  Matt Sharritt
Description:  Java Game that can
	be played as an applet on
	a webpage, with the objective
	to kill the computer tank
	with your tank.
Date:  March 24, 2000
*************************************/

import java.applet.*;
import java.awt.*;
import java.awt.event.*;


public class TankGame extends Applet implements Runnable
{
	TankSprite tank;
	TankSprite computer;
	SpriteObject scenery[];
	public boolean boxDebug = false;
	Image rockImage, treeImage, fireImage;
	Image burnImage, smokeImage;
	Image arrayTank[] = new Image[9];
	Thread anim;
	Image buffer;
	Graphics bufgr;
	Font font;

	Bullet tankBullet;
	Bullet computerBullet;
	public boolean tankBulletOn = false;
	public boolean computerBulletOn = false;

	// Global scale variable for bounding boxes
	public static float CBscale = 0.9f;

	public static final int FRAME_DELAY = 50;
	public static final int DYING = 80;

	public void init()
	{
		addKeyListener(new keyL());

		rockImage = getImage(getCodeBase(), "rock.gif");
		treeImage = getImage(getCodeBase(), "tree.gif");

		arrayTank[0] = getImage(getCodeBase(), "TankN.gif");
		arrayTank[1] = getImage(getCodeBase(), "TankNE.gif");
		arrayTank[2] = getImage(getCodeBase(), "TankE.gif");
		arrayTank[3] = getImage(getCodeBase(), "TankSE.gif");
		arrayTank[4] = getImage(getCodeBase(), "TankS.gif");
		arrayTank[5] = getImage(getCodeBase(), "TankSW.gif");
		arrayTank[6] = getImage(getCodeBase(), "TankW.gif");
		arrayTank[7] = getImage(getCodeBase(), "TankNW.gif");
		arrayTank[8] = getImage(getCodeBase(), "fire.gif");

		MediaTracker t = new MediaTracker(this);
		t.addImage(rockImage, 0);
		t.addImage(treeImage, 0);
		t.addImage(arrayTank[0], 0);
		t.addImage(arrayTank[1], 0);
		t.addImage(arrayTank[2], 0);
		t.addImage(arrayTank[3], 0);
		t.addImage(arrayTank[4], 0);
		t.addImage(arrayTank[5], 0);
		t.addImage(arrayTank[6], 0);
		t.addImage(arrayTank[7], 0);
		t.addImage(arrayTank[8], 0);


		try
		{
			t.waitForID(0);
		} catch (InterruptedException e)
			{}

		buffer = createImage(600,400);
		bufgr = buffer.getGraphics();
		font = new Font("TimesRoman",Font.ITALIC,30);
		setLevel1();
	}

	public void start()
	{
		anim = new Thread(this);
		anim.start();
	}

	public void stop()
	{
		anim.stop();
		anim = null;
	}

	public void run()
	{
		Rectangle cb;
		Rectangle tb;

		while(true)
		{
			if (computer.isActive())
			{
				if (computer.isAlive())
				{
					cb = computer.collisionBox();
					for (int k = 0; k < scenery.length; k++)
					{
						if (cb.intersects(scenery[k].collisionBox()))
						{
							computer.collidesWith(scenery[k]);
							computer.left();
							computer.left();
							computer.updateSprite();
						}

						if (cb.intersects(tank.collisionBox()))
						{
							computer.collidesWith(tank);
						}
					}
				}
			}
			computer.updateSprite();

			if (tank.isActive())
			{
				if (tank.isAlive())
				{
					tb = tank.collisionBox();
					for (int k = 0; k < scenery.length; k++)
					{
						if (tb.intersects(scenery[k].collisionBox()))
						{
							tank.collidesWith(scenery[k]);
						}

						if (tb.intersects(computer.collisionBox()))
						{
							tank.collidesWith(computer);
						}
					}
				}
				tank.updateSprite();
			}

			if(tank.getIndex() == 8)
			{
				tank.alive(false);
				try
				{
					Thread.sleep(3000);
				} catch (InterruptedException e)
				{}
			}

			if(computer.getIndex() == 8)
			{
				computer.alive(false);
				try
				{
					Thread.sleep(3000);
				} catch (InterruptedException e)
				{}
			}

			repaint();

			try
			{
				Thread.sleep(FRAME_DELAY);
			} catch (InterruptedException e)
				{}
		}
	}

	public void setLevel1()
	{

		scenery = new SpriteObject[20];
		scenery[0] = new SceneSprite(SceneSprite.ROCK,
						150,300,rockImage,this);
		scenery[1] = new SceneSprite(SceneSprite.ROCK,
						160,340,rockImage,this);
		scenery[2] = new SceneSprite(SceneSprite.ROCK,
						120,380,rockImage,this);
		scenery[3] = new SceneSprite(SceneSprite.TREE,
						560,300,treeImage,this);
		scenery[4] = new SceneSprite(SceneSprite.TREE,
						400,180,treeImage,this);
		scenery[5] = new SceneSprite(SceneSprite.TREE,
						50,200,treeImage,this);
		scenery[6] = new SceneSprite(SceneSprite.ROCK,
						470,40,rockImage,this);
		scenery[7] = new SceneSprite(SceneSprite.ROCK,
						450,80,rockImage,this);
		scenery[8] = new SceneSprite(SceneSprite.ROCK,
						460,120,rockImage,this);
		scenery[9] = new SceneSprite(SceneSprite.TREE,
						50,80,treeImage,this);
		scenery[10] = new SceneSprite(SceneSprite.TREE,
						200,180,treeImage,this);
		scenery[11] = new SceneSprite(SceneSprite.TREE,
						300,300,treeImage,this);
		scenery[12] = new SceneSprite(SceneSprite.TREE,
						250,50,treeImage,this);
		scenery[13] = new SceneSprite(SceneSprite.ROCK,
						200,20,rockImage,this);
		scenery[14] = new SceneSprite(SceneSprite.TREE,
						350,70,treeImage,this);
		scenery[15] = new SceneSprite(SceneSprite.ROCK,
						420,320,rockImage,this);
		scenery[16] = new WallSprite(0,0,1190,10,this);		//top
		scenery[17] = new WallSprite(0,0,10,790,this);		//lside
		scenery[18] = new WallSprite(594,0,10,790,this);	//rside
		scenery[19] = new WallSprite(0,394,1190,10,this);	//bottom
		tank = new TankSprite(40,350, arrayTank[0],arrayTank,0,this);
		computer = new TankSprite(570,50,arrayTank[4],arrayTank,4,this);

	}

	private boolean lostGame()
	{
		return !tank.isAlive();
	}

	private boolean wonGame()
	{
		return tank.isAlive() && !computer.isAlive();
	}

	public void update(Graphics g)
	{
		Rectangle cb;

		double tankX = tank.getX();
		double tankY = tank.getY();

		cb = computer.collisionBox();


		for (int i = 0; i < 5; i++)
		{
			computer.move();
		}

		if(computer.getDx() == 0 && computer.getDy() == 0)
		{
			computer.setIndex((computer.getIndex()) + 3);
		}

		for (int i = 0; i < scenery.length; i++)
		{
			if (cb.intersects(scenery[i].collisionBox()))
			{
				for(int j = 0; j < 15; j++)
				{
					computer.collidesWith(scenery[i]);
				}
			}

		}

		double x = computer.getX(); double y = computer.getY();
		int h = (int)(Math.random()*1000+1);
		if(h % 35 == 0)
		{
			computerBullet = new Bullet(x, y, tank, computer);
			computerBulletOn = true;
			computerBullet.setCompDir();
		}


		computer.updateSprite();
		if(tankBulletOn == true)
			tankBullet.move();
		if(computerBulletOn == true)
			computerBullet.move();
		paint(bufgr);
		g.drawImage(buffer,0,0,this);
	}

	public void paint(Graphics g)
	{
		Color bkgd = new Color(30, 128, 30);
		Color end = new Color(30, 30, 128);
		g.setColor(bkgd);
		g.fillRect(0,0,600,400);
		for (int i = 0; i < scenery.length; i++)
		{
			scenery[i].drawSprite(g);
		}
		computer.drawSprite(g);
		tank.drawSprite(g);

		if(tankBulletOn == true)
			tankBullet.draw(g);
		if(computerBulletOn == true)
			computerBullet.draw(g);


		if (wonGame())
		{
			g.setColor(end);
			g.fillRect(0,0,600,400);
			g.setColor(Color.magenta);
			g.setFont(font);
			g.drawString("Good Work - You WIN!",170,150);
		}

		if (lostGame())
		{
			g.setColor(end);
			g.fillRect(0,0,600,400);
			g.setColor(Color.magenta);
			g.setFont(font);
			g.drawString("You LOSE...Better Luck Next Time.",100,150);
		}
	}

	private class keyL extends KeyAdapter
	{
		public void keyPressed(KeyEvent e)
		{
			int key = e.getKeyCode();
			switch (key)
			{
				/*add control keys for tank*/
				case KeyEvent.VK_LEFT:		tank.left();
											break;

				case KeyEvent.VK_RIGHT:		tank.right();
											break;

				case KeyEvent.VK_UP:		tank.move();
											break;

				case KeyEvent.VK_SPACE:		double x = tank.getX(); double y = tank.getY();
											tankBullet = new Bullet(x, y, tank, computer);
											tankBulletOn = true;
											tankBullet.setTankDir();
											break;
			}
			update(bufgr);
		}

		public void keyReleased(KeyEvent e)
		{
			int key = e.getKeyCode();
		}
	}
}


class Bullet
{
	double    x, y, dx, dy;
	int       size = 10,
			  radius = 5;
	TankSprite tank;
	TankSprite computer;


	Bullet (double x, double y, TankSprite tank, TankSprite computer)
	{
		this.tank = tank;
		this.computer = computer;
		this.x = x;
		this.y = y;
	}


	public void setTankDir()
	{
		switch(tank.getIndex())
		{
			case 0:				y-=25;
								dx = 0;
								dy = -10;
								System.out.println("in 0");
								break;
			case 1: 		   	y-=20;
								x+=20;
								dx = 6;
								dy = -6;
								System.out.println("in 1");
								break;
			case 2: 		   	x+=25;
								dx = 10;
								dy = 0;
								System.out.println("in 2");
								break;
			case 3:    			x+=20;
								y+=20;
								dx = 6;
								dy = 6;
								System.out.println("in 3");
								break;
			case 4:    			y+=25;
								dx = 0;
								dy = 10;
								System.out.println("in 4");
								break;
			case 5:    			x-=20;
								y+=20;
								dx = -6;
								dy = 6;
								System.out.println("in 5");
								break;
			case 6:    			x-=25;
								dx = -10;
								dy = 0;
								System.out.println("in 6");
								break;
			case 7:    			x-=20;
								y-=20;
								dx = -6;
								dy = -6;
								System.out.println("in 7");
								break;
		}
		move();
	}


	public void setCompDir()
	{
		switch(computer.getIndex())
		{
			case 0:				y-=25;
								dx = 0;
								dy = -10;
								break;
			case 1: 		   	y-=20;
								x+=20;
								dx = 6;
								dy = -6;
								break;
			case 2: 		   	x+=25;
								dx = 10;
								dy = 0;
								break;
			case 3:    			x+=20;
								y+=20;
								dx = 6;
								dy = 6;
								break;
			case 4:    			y+=25;
								dx = 0;
								dy = 10;
								break;
			case 5:    			x-=20;
								y+=20;
								dx = -6;
								dy = 6;
								break;
			case 6:    			x-=25;
								dx = -10;
								dy = 0;
								break;
			case 7:    			x-=20;
								y-=20;
								dx = -6;
								dy = -6;
								break;
		}
	}

	public void move()
	{
		if((checkHit() == false) && (x<600)  && (y<420))
		{
			x += dx;
			y += dy;
		}
		if(checkHit() == true)
		{
			blowUp();
		}
	}

	public boolean checkHit ()
	{
		boolean hit;
		if (
		((((x) > (tank.getX() - (18))) && ((x) < (tank.getX() + (18))))
		&& (((y) > (tank.getY() - (18))) && ((y) < (tank.getY() + (18)))))
		||
		((((x) > (computer.getX() - (18))) && ((x) < (computer.getX() + (18))))
		&& (((y) > (computer.getY() - (18))) && ((y) < (computer.getY() + (18)))))
		)
		{
			hit = true;
		}
		else
		{
			hit = false;
		}

		return hit;
	}

	public void draw(Graphics g)
	{
		g.setColor(Color.black);
		g.fillOval(((int)this.x - radius), ((int)this.y - radius), size, size);
	}

	public void blowUp()
	{
		if((((x + size) > (tank.getX() - (18))) && ((x) < (tank.getX() + (18))))
		&& (((y + size) > (tank.getY() - (18))) && ((y) < (tank.getY() + (18)))))
		{
			tank.drawFire();
		}

		if((((x + size) > (computer.getX() - (18))) && ((x) < (computer.getX() + (18))))
		&& (((y + size) > (computer.getY() - (18))) && ((y) < (computer.getY() + (18)))))
		{
			computer.drawFire();
		}

	}

}


class WallSprite extends SpriteObject
{
	TankGame app;

	public WallSprite(double x, double y, int w, int h, TankGame a)
	{
		super(x, y, w, h);
		app = a;
	}

	public void drawSprite(Graphics g)
	{
		// Walls are usually invisible
		if (app.boxDebug)
		{
			g.setColor(Color.black);
			g.drawRect((int)(x - width/2.0),(int)(y - height/2.0),width,height);
		}
	}

}

class SceneSprite extends ImageSprite
{
	public int type;
	TankGame app;

	public static final int ROCK = 1;
	public static final int TREE = 2;

	public SceneSprite(int t, int x, int y, Image i, TankGame a)
	{
		super(i, x, y);
		app = a;
		type = t;
	}

	public void drawSprite(Graphics g)
	{
		super.drawSprite(g);
		if (app.boxDebug)
		{
			g.setColor(Color.black);
			Rectangle cb = collisionBox();
			g.drawRect(cb.x, cb.y, cb.width, cb.height);
		}
	}

	public Rectangle collisionBox()
	{
		int cbw = (int)(width*TankGame.CBscale);
		int cbh = (int)(height*TankGame.CBscale);
		return new Rectangle((int)(x - cbw/2.0), (int)(y - cbh/2.0), cbw, cbh);
	}
}

class TankSprite extends ImageSprite
{
	double dx = 0;
	double dy = 0;
	Image burnImage;
	boolean alive = true;
	boolean active = true;
	int counter;
	TankGame app;
	int index;
	Image[] imageArray = new Image[9];

	static final int SPEED = 6;

	TankSprite(int x, int y, Image i, Image[] arr, int index, TankGame a)
	{
		super(i, arr, x, y);
		this.index = index;
		imageArray = arr;
		app = a;
	}

	public void move()
	{
		if(index == 0)
		{
			dx = 0;
			dy = -2;
		}
		else if(index == 1)
		{
			dx = 1;
			dy = -1;
		}
		else if(index == 2)
		{
			dx = 2;
			dy = 0;
		}
		else if(index == 3)
		{
			dx = 1;
			dy = 1;
		}
		else if(index == 4)
		{
			dx = 0;
			dy = 2;
		}
		else if(index == 5)
		{
			dx = -1;
			dy = 1;
		}
		else if(index == 6)
		{
			dx = -2;
			dy = 0;
		}
		else if(index == 7)
		{
			dx = -1;
			dy = -1;
		}
		else
			System.out.println("Loop error");

	}

	public void right()
	{
		if(index < 7)
			index++;
		else
			index = 0;
	}

	public void left()
	{
		if(index > 0)
			index--;
		else
			index = 7;
	}

	public void drawSprite(Graphics g)
	{
	 	if (!isActive())
			return;
		super.drawSprite(g, index);
	}

	public Rectangle collisionBox()
	{
		int cbw = (int)(width*TankGame.CBscale);
		int cbh = (int)(height*TankGame.CBscale);
		return new Rectangle((int)(x - cbw/2.0), (int)(y - cbh/2.0), cbw, cbh);
	}

	public void backup()
		{
			if(index == 0)
			{
				dx = 0;
				dy = 2;
			}
			else if(index == 1)
			{
				dx = -1;
				dy = 1;
			}
			else if(index == 2)
			{
				dx = -2;
				dy = 0;
			}
			else if(index == 3)
			{
				dx = -1;
				dy = -1;
			}
			else if(index == 4)
			{
				dx = 0;
				dy = -2;
			}
			else if(index == 5)
			{
				dx = 1;
				dy = -1;
			}
			else if(index == 6)
			{
				dx = 2;
				dy = 0;
			}
			else if(index == 7)
			{
				dx = 1;
				dy = 1;
			}
			else
				System.out.println("Loop error");

			dx *=2;
			dy *=2;
	}

	public void drawFire()
	{
		setIndex(8);

	}

	public double getX()
	{
		return x;
	}

	public double getY()
	{
		return y;
	}

	public double getDx()
	{
		return dx;
	}

	public double getDy()
	{
		return dy;
	}

	public void alive(boolean alive)
	{
		this.alive = alive;
	}

	public boolean isAlive()
	{
		return alive;
	}

	public boolean isActive()
	{
		return active;
	}

	public void suspend()
	{
		active = false;
	}

	public void updateSprite()
	{
		if (alive)
		{
			x += dx; y += dy;
			dx = 0; dy = 0;
		}
		else
		{
			// We're on fire - wait a bit, then go away
			counter--;
			if (counter <= 0)
				suspend();
		}
	}

	public int getIndex()
	{
		return index;
	}

	public void setIndex(int index)
	{
		this.index = index;
	}


	public void collidesWith(Object obj)
	{
			backup();
	}
}