import java.awt.*;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.image.*;
import javax.imageio.*;
import java.awt.AWTEvent;
import javax.swing.JFrame;
import java.util.Random;

public class Robots extends JFrame
{
    // game state
    static final int TITLE = 0;
    static final int START = 1;
    static final int PLAYING = 2;
    static final int DYING = 3;
    static final int LEVEL_COMPLETE = 4;
    static final int WON = 5;
    
    // robot state
    static final int DEAD = 0;
    static final int ALIVE = 1;
    static final int ALIVE_AND_ANGRY = 2;

    static private int[] mouseAndKeys;

    public static void main(String[] args) throws Exception
    {
		// initialise frame
        Robots f = new Robots();
        f.setSize(800, 600);
        f.setResizable(false);
        //f.setLocation(50, 50);
        f.setVisible(true);

        mouseAndKeys = new int[256];

        f.enableEvents(AWTEvent.KEY_EVENT_MASK + AWTEvent.MOUSE_EVENT_MASK + AWTEvent.MOUSE_MOTION_EVENT_MASK + AWTEvent.WINDOW_EVENT_MASK);

        // create screen buffer
        BufferedImage image = new BufferedImage(200, 150, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();        

        // load sprite sheets
        BufferedImage robotImage = ImageIO.read(f.getClass().getResource("r.png"));
        BufferedImage stuffImage = ImageIO.read(f.getClass().getResource("s.png"));
        
       // create background 'map'
        BufferedImage map = new BufferedImage(600, 450, BufferedImage.TYPE_INT_RGB);
        Graphics2D bgGraphics = map.createGraphics();

        int playerX = 0;
        int playerY = 0;
        int playerDir = 0;
        
		double[] robotX = new double[200];
        double[] robotY = new double[200];
        double[] robotDir = new double[200];
        int[] robotState = new int[200];
        
        double[] particleX = new double[1000];
        double[] particleY = new double[1000];
        double[] particleDX = new double[1000];
        double[] particleDY = new double[1000];
        double[] particleH = new double[1000];
        double[] particleDH = new double[1000];
        Color[] particleColor = new Color[1000];
        int[] particleLife = new int[1000];
        
        int[] podX = new int[10];
        int[] podY = new int[10];
		
		String[] cities = {"London", "Nairobi", "New York", "Tokyo", "Sydney"};
		
		Random random = new Random();

        int frameCount = -1;
        int playerFrameCount = 0;

        int i = 0;
        int j = 0;
        int k = 0;
        int x = 0;
        int y = 0;
        int offset = 0;
		int laserTemp = 0;
		int pods = 0;
		int level = 1;
        int lives = 0;
		int state = 0;
		int countdown = 0;
        int spawn = 0;
        int numberOfRobots = 0;
        boolean newLevel = false;
        boolean killed = false;
		boolean firstTime = false;
		
        while(mouseAndKeys[KeyEvent.VK_ESCAPE] == 0)
        {
			if (state == TITLE && (mouseAndKeys[2] != 0 || firstTime))
			{
                // init stuff
                if (lives == 0 || newLevel) // new game
                {
                    if (lives == 0 || level == 6)
                    {
                        level = 1;
                        lives = 3;
                    }
                    newLevel = false;
                    // init robots
                    numberOfRobots = 50 + 30 * level;
                    for (i = 0; i < numberOfRobots; i++)
                    {
                        robotX[i] = 8 + random.nextInt(576);
                        robotY[i] = 6 + random.nextInt(424);
                        robotDir[i] = i;
                        robotState[i] = ALIVE;
                    }
                    // init pods
                    for (i = 0; i < 10; i++)
                    {
                        podX[i] = 24 + random.nextInt(552);
                        podY[i] = 18 + random.nextInt(406);
                    }
                    // draw walls / grass on background
                    for (i = 0; i < 600; i += 8)
                    {
                        for (j = 0; j < 450; j += 6)
                        {
                            offset = (level - 1) * 12 + random.nextInt(2) * 6; // grass
                            if (i % 592 == 0 || j % 444 <= 6)
                            {
                                offset += 12 * (6 - level); // wall
                            }
                            /* This is now redrawn every frame anyway for the flashing effect
                            if ((x == 176 || x == 416) && (y == 132 || y == 312))
                            {
                                offset = 54; // spawning point
                            }
                            */
                            bgGraphics.setClip(i, j, 8, 6);
                            bgGraphics.drawImage(stuffImage, i, j - offset, null);
                        }
                    }
                    // draw some flowers on background
                    for (i = 0; i < 1000; i++)
                    {
                        x = random.nextInt(580) + 8;
                        y = random.nextInt(428) + 12;
                        bgGraphics.setClip(x, y, 6, 4);
                        bgGraphics.drawImage(stuffImage, x, y - 102 - 4 * (i % 2 + level), null);
                    }
                    pods = 10;
                }
                firstTime = false;
                playerX = 300;
                playerY = 225;
                playerDir = 1;
                killed = false;
                state = START;
                countdown = 50;
			}
			if (state == START)
			{
				if (countdown-- == 0)
				{
					state = PLAYING;
				}
			}
			if (state == START || state == PLAYING)
			{				
				// move player
				if (mouseAndKeys[KeyEvent.VK_UP] != 0 || mouseAndKeys[KeyEvent.VK_W] != 0)
				{
					if (playerY > 6)
						playerY--;
					playerDir = 0;
					playerFrameCount++;
				}
				if (mouseAndKeys[KeyEvent.VK_DOWN] != 0 || mouseAndKeys[KeyEvent.VK_S] != 0)
				{
					if (playerY < 430)
						playerY++;
					playerDir = 2;
					playerFrameCount++;
				}
				if (mouseAndKeys[KeyEvent.VK_RIGHT] != 0 || mouseAndKeys[KeyEvent.VK_D] != 0)
				{
					if (playerX < 584)
						playerX++;
					playerDir = 1;
					playerFrameCount++;
				}
				if (mouseAndKeys[KeyEvent.VK_LEFT] != 0 || mouseAndKeys[KeyEvent.VK_A] != 0)
				{
					if (playerX > 8)
						playerX--;
					playerDir = 3;
					playerFrameCount++;
				}
			}
			if (state == DYING)
			{
				if (countdown-- == 0)
				{
					state = TITLE;
				}
			}
            if (state == LEVEL_COMPLETE && mouseAndKeys[2] != 0)
            {
                state = TITLE;
                firstTime = true;
            }
            if (state == WON && mouseAndKeys[2] != 0)
            {
                state = TITLE;
                firstTime = true;
                lives = 0;
            }
            
            frameCount++;

            // fill background
			int translateX = 100 - playerX;
			int translateY = 75 - playerY;
			if (translateX > 0) translateX = 0;
			if (translateX < -400) translateX = -400;
			if (translateY > 0) translateY = 0;
			if (translateY < -300) translateY = -300;
			g.translate(translateX, translateY);
			
            g.setClip(0, 0, 600, 450);
            g.drawImage(map, 0, 0, null);
            
            // draw spawning point flash
            if (state != TITLE)
            {
                for (i = 176; i <= 416; i += 240)
                {
                    for (j = 132; j <= 312; j += 180)
                    {
                        bgGraphics.setClip(i, j, 8, 6);
                        offset = 94;
                        if (spawn-- > 0)
                        {
                            offset = 100;
                        }
                        bgGraphics.drawImage(stuffImage, i, j - offset, null);
                    }
                }
            }
            
            // draw gene pods
            for (i = 0; i < 10; i++)
            {
                // draw
                g.setClip(podX[i], podY[i], 8, 4);
                g.drawImage(stuffImage, podX[i], podY[i] - 90, null);

                // check for player collecting pod
                x = podX[i] - playerX;
                y = podY[i] - playerY - 10;
                if (x * x + y * y < 16)
                {
                    pods--;
                    podX[i] = -100; // just move it off screen
                }
            }

            // draw player
            if (state == PLAYING || (state == START && frameCount % 2 == 0)) // blink if just started
			{
				g.setClip(playerX, playerY, 7, 13);
				g.drawImage(robotImage, playerX - 56  - 7 * (playerDir % 4), playerY - 13 * ((playerFrameCount) % 4), null);
			}
            
            // move & draw robots
            for (i = 0; i < numberOfRobots; i++)
            {
				// if alive
                if (robotState[i] != DEAD)
                {
                    if (robotState[i] == ALIVE_AND_ANGRY) // angry
                    {
                        // turn towards player
                        robotDir[i] = Math.atan2(playerY - robotY[i], playerX - robotX[i]) + 6.28319;
                    }
                    // move in current direction
                    robotX[i] += Math.cos(robotDir[i]);
                    robotY[i] += Math.sin(robotDir[i]);
                    
                     // turn around at edge of map
					if (robotX[i] < 8 || robotX[i] > 584 || robotY[i] < 6 || robotY[i] > 430)
					{
                        robotX[i] -= Math.cos(robotDir[i]);
                        robotY[i] -= Math.sin(robotDir[i]);
						robotDir[i] = random.nextDouble() * 6.283;
					}
                    
                    // draw
                    g.setClip((int)(robotX[i]), (int)(robotY[i]), 7, 13);
                    int normal = 28 * (2 - robotState[i]);
                    int dir = ((int)(1.5 + robotDir[i] * 2 / 3.14159)) % 4;
                    g.drawImage(robotImage, (int)(robotX[i]) - 7 * dir - normal, (int)(robotY[i]) - 13 * ((frameCount + i) % 4), null);

					// check for robot killing player
					x = (int)(robotX[i]) - playerX;
					y = (int)(robotY[i]) - playerY;
                    int distance = x * x + y * y;
					if (state == PLAYING && distance < 9) // dead
					{
						killed = true;
					}
                    // make robot angry if close to alive player
					if (state == PLAYING && distance < (28 + 7 * level) * (28 + 7 * level))
					{
						robotState[i] = ALIVE_AND_ANGRY;
					}
                    // robot is not angry if player is playing
                    if (state != PLAYING)
                    {
                        robotState[i] = ALIVE;
                    }
                }  
			}
            
            // respawn a robot every 40 frames (about 2 seconds)
            if (frameCount % 40 == 0)
            {
                spawn = 10;
                for (i = 0; i < numberOfRobots; i++)
                {
                    if (robotState[i] == DEAD)
                    {
                        robotState[i] = ALIVE;
                        robotX[i] = 176 + random.nextInt(2) * 240;
                        robotY[i] = 122 + random.nextInt(2) * 180;
                        robotDir[i] = random.nextDouble() * 6.283;
                        break;
                    }
                }
            }
			
            g.setClip(0, 0, 600, 450);
            bgGraphics.setClip(8, 12, 784, 582);

            // particles
            for (i = 0; i < 1000; i++)
            {
                if (particleLife[i]-- > 0)
                {
                    // move
                    particleH[i] += particleDH[i]--;
                    if (particleH[i] > 0)
                    {
                        particleX[i] += particleDX[i];
                        particleY[i] += particleDY[i];
                        // draw
                        g.setColor(particleColor[i]);
                        g.fillRect((int)(particleX[i]), (int)(particleY[i] - particleH[i]), 1, 1);
                    }
                    else
                    {
                        // draw to background
                        particleH[i] = 0;
                        bgGraphics.setColor(particleColor[i]);
                        bgGraphics.fillRect((int)(particleX[i]), (int)(particleY[i] - particleH[i]), 1, 1);
                    }
                }
            }
			
            // draw laser
            if (state < DYING && mouseAndKeys[2] != 0 && laserTemp < 46)
            {
				laserTemp += 3;
                int mouseX = mouseAndKeys[0] / 4 - translateX;
                int mouseY = mouseAndKeys[1] / 4 - translateY;
                // check whether hitting player
                if (state == PLAYING && (playerX + 2 - mouseX) * (playerX + 2 - mouseX) + (playerY + 12 - mouseY) * (playerY + 12 - mouseY) < 9)
                {
                    killed = true;
                }
                
                // draw beam
                g.setColor(Color.RED);
                g.fillRect(mouseX - 1, 0, 3, mouseY + 1);
                // draw flame
                g.setClip(mouseX - 4, mouseY - 4, 8, 5);
                g.drawImage(stuffImage, mouseX - 4, mouseY - 86 - 5 * (frameCount % 2), null);
                // draw scorch / mud onto background map
                bgGraphics.setClip(mouseX - 4, mouseY - 2, 8, 5);
                bgGraphics.drawImage(stuffImage, mouseX - 4, mouseY - 74 - 5 * random.nextInt(2), null);
                
                // check for hit
                for (i = 0; i < numberOfRobots; i++)
                {
                    int dx = (int)(robotX[i]) + 3 - mouseX;
                    int dy = (int)(robotY[i]) + 12 - mouseY;
                    if (robotState[i] != DEAD && dx * dx + dy * dy < 16)
                    {
						// kill robot
                        robotState[i] = DEAD;
                        // add shrapnel particles
                        for (j = 0; j < 500; j+= 100)
                        {
                            //find unused part of array
                            if (particleLife[j] <= 0)
                            {
                                for (k = j; k < j + 100; k++)
                                {
                                    particleX[k] = robotX[i] + 3;
                                    particleY[k] = robotY[i] + 12;
                                    particleDX[k] =  (random.nextDouble() - 0.5) * (random.nextDouble() - 0.5) * 12; 
                                    particleDY[k] =  (random.nextDouble() - 0.5) * (random.nextDouble() - 0.5) * 12; 
                                    particleH[k] = random.nextDouble() * 13;
                                    particleDH[k] = random.nextDouble() * 3;
                                    particleColor[k] = new Color(0x878787 - 0x070707 * random.nextInt(10));
                                    particleLife[k] = 40;
                                }
                                break;
                            }
                        }
                    }
                    // make robot angry if laser is close
                    if (state == PLAYING && robotState[i] != DEAD && dx * dx + dy * dy < (28 + 7 * level) * (28 + 7 * level))
                    {
                        robotState[i] = ALIVE_AND_ANGRY;
                    }
                }
            }
			if (laserTemp > 0)
				laserTemp--;
                
            if (killed)
            {
                lives--;
                killed = false;
                countdown = 25;
                state = DYING;
                // add plenty of blood particles
                for (k = 500; k < 1000; k++)
                {
                    particleX[k] = playerX + 3;
                    particleY[k] = playerY + 12;
                    particleDX[k] = (random.nextDouble() - 0.5) * (random.nextDouble() - 0.5) * 6; 
                    particleDY[k] = (random.nextDouble() - 0.5) * (random.nextDouble() - 0.5) * 6; 
                    particleH[k] = random.nextDouble() * 13;
                    particleDH[k] = random.nextDouble() * 5;
                    particleColor[k] = new Color(0xdd0000 - 0x010000 * random.nextInt(70));
                    particleLife[k] = 40;
                }

            }
            
            if (state == PLAYING && pods == 0)
            {
                level++;
                newLevel = true;
                state = LEVEL_COMPLETE;
                if (level > 5)
                {
                    state = WON;
                }
            }
			
            g.setClip(0, 0, 600, 450);

			g.translate(-translateX, -translateY);
			
			g.setColor(Color.WHITE);
            if (state == LEVEL_COMPLETE)
            {
                g.drawString(cities[level - 2] + " complete", 53, 70);
                g.drawString("Click to start", 66, 90);
            }
            if (state == WON)
            {
                g.drawString("Mission complete", 53, 50);
                g.drawString("Humanity is saved!", 49, 70);
                g.drawString("Click to start", 66, 90);
            }
			if (state == TITLE)
			{
                if (lives == 0)
                {
                    // draw title screen
                    g.drawString("Robo Rampage", 57, 70);
                }
                else
                {
                    g.drawString(lives + " lives left", 73, 70);
                }
                g.drawString("Click to start", 66, 90);
			}
			else
			{
				// draw city name
				if (state < LEVEL_COMPLETE)
                {
                    g.drawString(cities[level - 1], 2, 17);
                }
				// draw lives
				g.drawString("Lives " + lives, 151, 17);
				// draw score (pods remaining)
				g.drawString("Pods " + pods, 151, 148);
				// draw laser temperature
				g.drawString("Laser", 2, 148);
				if (laserTemp > 45)
				{
					g.setColor(Color.RED);
				}
				g.drawRect(42, 141, 50, 6);
				g.fillRect(42, 141, laserTemp, 6);
			}
            
            // copy buffer to screen
			Graphics g2 = f.getGraphics();
            g2.drawImage(image, 0, 0, 800, 600, f);
            g2.dispose();
			
            // sleep between frames
            try
            {
                Thread.sleep(50);
            }
            catch (Exception e)
            {
            }
        }
		
		System.exit(0);
    }

    public void processEvent(AWTEvent e)
    {
        int id = e.getID();
        if (id == KeyEvent.KEY_PRESSED || id == KeyEvent.KEY_RELEASED) // 401 or 402
        {
            mouseAndKeys[((KeyEvent) e).getKeyCode()] = 402 - id;
        }
        if (id == MouseEvent.MOUSE_PRESSED || id == MouseEvent.MOUSE_RELEASED) // 501 or 502
        {
            mouseAndKeys[2] = 502 - id;
        }
        if (id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED)  // 503, 506
        {
            mouseAndKeys[0] = ((MouseEvent) e).getX();
            mouseAndKeys[1] = ((MouseEvent) e).getY();
        }
        if (id == WindowEvent.WINDOW_CLOSING) // 201
        {
            mouseAndKeys[KeyEvent.VK_ESCAPE] = 1;
        }
    }
}

