// Java Wormy
// Version 1.1

// Import files...
import java.awt.*;
import java.lang.*;
import java.awt.event.*;
import java.applet.Applet;

// Main Class
public class Wormy extends Applet implements Runnable {
    int maxLength = 500;				//Max worm length
    int highest, delay, number, current, slength;
    int direction, length, aNumber, numx, numy = 0;
	int options, oldx, oldy;
	int mystery, hundreds = 0;
    int[] xpos = new int[maxLength];	//Cell xpos
    int[] ypos = new int[maxLength];	//Cell ypos
    String[] hiname = new String[5];	//Hiscore names
    int[] hiscr = new int[5];			//Hiscore scores
    String newName;						//Temp hiscore name
    Thread theThread;				//The thread				
    boolean newHiScr, paused, frozen = false;
    boolean pressedButt = false;
    boolean firstRun, running, dying = false;
	//Keyboard keys
    char kLeft, kRight, kUp, kDown, kPause;

	// Graphics stuff
    Dimension offDimension;
    Image offImage, iwormy, icell, ilogo, ipresents, iclick,
		ikeys, ioptions, ihiScore, ihead;
    Graphics offGraphics;

	//Start new game...
    public void startUp() {
		firstRun = true;
		length = slength;
		hundreds = 0;
		mystery = 0;  
		for (int loop = 0; loop < length; loop++) {
			xpos[loop] = 200 + (10*loop);
			ypos[loop] = 200;
		}
		for (int loop = length; loop < maxLength; loop++) {
			xpos[loop] = 400;
			ypos[loop] = 400;
		}	
		direction = 3;
		newNumber();
	
		number = 0;	
		paused = false;
		newHiScr = false;
		newName = "";
    }

	// Initialization
    public void init() {
        String str;
        int fps = 10;

        //How many milliseconds between frames?
        str = getParameter("fps");
        try {
            if (str != null) {
                fps = Integer.parseInt(str);
            }
        } catch (Exception e) {}
		delay=150;

		// Load graphic files
        iwormy = getImage(getCodeBase(), "data/wormy.gif");
        ilogo = getImage(getCodeBase(), "data/logo.jpg");
        icell = getImage(getCodeBase(), "data/wormy1.gif");
        ihead = getImage(getCodeBase(), "data/wormy2.gif");
        ipresents = getImage(getCodeBase(), "data/presents.gif");
        iclick = getImage(getCodeBase(), "data/click.gif");
        ihiScore = getImage(getCodeBase(), "data/hiscore.gif");
        ikeys = getImage(getCodeBase(), "data/keys.gif");
		ioptions = getImage(getCodeBase(), "data/options.gif");

		number = 0;
		current = 0;
		options = 0;
		slength = 10;
		
		//Setup keys
		kLeft = 'a';
		kRight = 's';
		kUp = 'j';
		kDown = 'n';
		kPause = 'p';
		
		//Set hiscores
		hiname[0] = "Tois";
		hiscr[0] = 400;
		hiname[1] = "Tois";
		hiscr[1] = 300;
		hiname[2] = "Tois";
		hiscr[2] = 250;
		hiname[3] = "Tois";
		hiscr[3] = 200;
		hiname[4] = "Tois";
		hiscr[4] = 150;
		//Call startup
		startUp();
    }

	// Start the thread
    public void start() {
        if (frozen) {
            //Do nothing.  The user has requested that we
            //stop changing the image.
        } else {
            //Start animating!
            if (theThread == null) {
                theThread = new Thread(this);
            }
            theThread.start();
        }
    }

 	// stops the Thread
    public void stop() {
        //Stop the animating thread.
        theThread = null;
        //Get rid of the objects necessary for double buffering.
        offGraphics = null;
        offImage = null;
    }

	// Running the thread
    public void run() {

		//Set the thread priority
	    Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
 
        //Remember the starting time.
        long startTime = System.currentTimeMillis();

        //This is the animation loop.
        while (Thread.currentThread() == theThread) {

		//If worm moving
	    if ((running) && (!paused)) {
			delay = 50;
			oldx = xpos[length-1];
			oldy = ypos[length-1];
			if (direction==0) {
			    for (int loop = length-1; loop > 0; loop--) {
					ypos[loop] = ypos[loop-1];
					xpos[loop] = xpos[loop-1];
			    }
			    ypos[0] -= 10;
			}
			if (direction==1) {
			    for (int loop = length-1; loop > 0; loop--) {
					xpos[loop] = xpos[loop-1];
					ypos[loop] = ypos[loop-1];
			    }
			    xpos[0] += 10;
			}
			if (direction==2) {
			    for (int loop = length-1; loop > 0; loop--) {
					ypos[loop] = ypos[loop-1];
					xpos[loop] = xpos[loop-1];
			    }
			    ypos[0] += 10;
			}
			if (direction==3) {
			    for (int loop = length-1; loop > 0; loop--) {
					xpos[loop] = xpos[loop-1];
					ypos[loop] = ypos[loop-1];
			    }
			    xpos[0] -= 10;
			}
		    if ((xpos[0] < 0) || (xpos[0] > 390) || (ypos[0] < 0) || (ypos[0] > 280)) {
			    running = false;
			    dying = true;
		    }
			for (int loop = 1; loop < length; loop++) 
				if ((xpos[loop] == xpos[0]) && (ypos[loop] == ypos[0])) {
			   		running = false;
					dying = true;
				}
	    	if ((xpos[0] == numx) && (ypos[0] == (numy-10))) {
			    length += aNumber;
			    newNumber();
			    if (length >= maxLength) {
			    	running = false;
					dying = true;
			    }
	     	}			
	    }
	    else { //If paused, dead or in menu
			delay = 500;
			if (dying) {
			    if (!newHiScr)
			        number++;
			    if (number==10) {
					dying = false;
					number = 0;
			    }
			}
			else {	//incrememnt menu loop
			    number++;
				//Change menu screen
			    if (number==10) {
			    	number = 0;
			    	current++;
			    	if (current==2)
					    current = 0;
			    }
			}
	    }

	    //Display it.
        repaint();
        
        //Delay depending on how far we are behind.
        try {
            startTime += delay;
            Thread.sleep(Math.max(0, 
                    startTime-System.currentTimeMillis()));
        } catch (InterruptedException e) {
            break;
          }
        }
    }

	// Create a new number onscreen
    public void newNumber() {
		boolean OK = false;
		if (mystery == 1)
			mystery = 0;
		if (((hundreds + 1)*100) < length) {
			mystery = 1;
			hundreds++;
			aNumber = (int)(Math.random() * 39) + 11;
		}
		else	
			aNumber = (int)(Math.random() * 9) + 1;
		//Must check the it's not on existing cell
		while (!OK) {
		    numx = (int)(Math.random() * 39+1) * 10;
		    numy = (int)(Math.random() * 28+1) * 10;
		    OK = true;
	  	    for (int loop = 0; loop < length; loop++) 
				if ((xpos[loop] == numx) && (ypos[loop] == (numy-10)))
				    OK = false;
		}
    }

 /*******************************EVENTS****************************************/  

	// Catch a mouse down event
	public boolean mouseDown(Event evt, int x, int y) {
		//Start new game
		if ((!running) && (!dying)) {
			if ((current != 0) &&
				(x > 150) && (x < 230) &&
				(y > 240) && (y < 280)) {
				options = 1;
			}
			else {
				running = true;
				startUp();
				repaint();
			}
		}
		//Resume paused game
        if (paused)
		    paused = false;
			
		return true;
    }

	// Catch a key typed event (in-game)
	public boolean keyDown(Event e,int key) {
    	//Start new game
		char pressedKey = (char)key;
		if ((!running) && (!dying)) {
			running = true;
			startUp();
			repaint();
		}
		else { //If in game
			//Check keypressed and does something
		    if (paused)
				paused = false;
		    if (pressedKey==kUp)
				direction = 0;
		    if (pressedKey==kRight)
				direction = 1;
		    if (pressedKey==kDown)
				direction = 2;
		    if (pressedKey==kLeft)
				direction = 3;
		    if (pressedKey==kPause)
				paused = true;
		}
			
		return true;
    }

	// Catch a key released event (high score)
	public boolean keyUp(Event e, int key) {
		//If it's a new high score...
		if (newHiScr) {
			//If return
		    if (key == 10) {
				//Move high scores down
				int loop;
	 	    	for (loop = 4; loop > highest; loop--) {
				    hiscr[loop] = hiscr[loop-1];
				    hiname[loop] = hiname[loop-1];
				}
				//Set new high score
				hiscr[loop] = length-10;
				hiname[loop] = newName;
				//Reset high score var's
				newHiScr = false;
				dying = false;	
				running = false;
		    }
		    else if (key == 8) { //If delete key
				newName = newName.substring(0, (newName.length()-1));
		    }
		    else if ((key >= 65) && (key <= 122)) //If character add to name
				newName += (char)key;
		}
			
		return true;
	}

/*******************************END EVENTS*************************************/

	// Paint the graphics
    public void paint(Graphics g) {
        Dimension d = size();

		//Create the offscreen graphics context, if no good one exists.
        if ( (offGraphics == null)
              || (d.width != offDimension.width)
              || (d.height != offDimension.height) ) {
                offDimension = d;
                offImage = createImage(402, 302);
                offGraphics = offImage.getGraphics();
		}
        //Clear the screen.
        offGraphics.setColor(Color.black);
		offGraphics.fillRect(0, 0, 402, 302);

		firstRun = true;

		//Paint the image onto the screen.
		g.drawImage(offImage, 0, 0, this);        
    }

	//Update the graphics
    public void update(Graphics g) {
    	            	  //Pause the thread while doing graphics
  	  if (theThread != null) theThread.suspend();
  	  
	  if (offGraphics != null) {
		//Get font details
		Font font = offGraphics.getFont();
    	int size = font.getSize();
    	String name = font.getName();
    	int style = font.getStyle();

		//Draw rectangle around screen
        offGraphics.setColor(new Color(85, 68, 58));
		offGraphics.drawRect(0, 0, 401, 301); 
		
		//Display worm on screen
		if ((running) || (dying)) {
			//If first run
			if (firstRun){
		        //Clear the screen.
		        offGraphics.setColor(Color.black);
				offGraphics.fillRect(1, 1, 400, 300);
				for (int loop = 1; loop <  length; loop++)
					offGraphics.drawImage(icell, xpos[loop], ypos[loop], this);
				firstRun = false;
			}
			//Draw score seperator
		    offGraphics.drawLine(0, 290, 401, 290); 
			//Draw head
	 	    offGraphics.drawImage(ihead, xpos[0], ypos[0], this);
	        offGraphics.setColor(Color.black);
			offGraphics.fillRect(xpos[1], ypos[1], 10, 10);
			//Draw other cells
			offGraphics.drawImage(icell, xpos[1], ypos[1], this);
	        //Erase the last cell.
	        offGraphics.setColor(Color.black);
			offGraphics.fillRect(oldx, oldy, 10, 10);

			offGraphics.fillRect(1, 291, 399, 10);
            offGraphics.setColor(new Color(255, 255, 255));
		    if (mystery == 0)
				offGraphics.drawString("" + aNumber, numx, numy);
			else
				offGraphics.drawString("?", numx, numy);
			//Draw score
            offGraphics.setColor(new Color(116, 129, 128));
            offGraphics.drawString("Score : " + (length-10), 80, 300);
			//Do high score bit in score section
    	    highest = 5;
		    for (int loop = 4; loop >= 0; loop--) 
				if ((length-10) > hiscr[loop])
					highest--;
		    if (highest != 0)
		        offGraphics.drawString("" + highest + "   " + hiname[highest-1] + "   " + hiscr[highest-1], 300, 300);
		    else
		        offGraphics.drawString("Highest Score", 300, 300);		
			//If the worm is dead...
		    if (dying) {
				//Draws score on screen
	        	offGraphics.setColor(new Color(116, 129, 128));
		    	size = size + 4;
		    	offGraphics.setFont(font = new Font(name, style, size));
		        offGraphics.drawString("Score : " + (length-10), 160, 150);
				if (newHiScr)
		            offGraphics.drawString("New High Score : " + newName, 140, 180);
		    	size = size - 4;
		    	offGraphics.setFont(font = new Font(name, style, size));
    	    	highest = 5;
		    	for (int loop = 4; loop >= 0; loop--) 
				    if ((length-slength) > hiscr[loop])
						highest--;
		    	if (highest != 5) 
				    newHiScr = true;
		    }
		}
		else { //else if in menu
	        //Clear the screen.
	        offGraphics.setColor(Color.black);
			offGraphics.fillRect(1, 1, 400, 300);
			//Draw score on screen
		    size = size + 2;
		    offGraphics.setFont(font = new Font(name, style, size));
	        offGraphics.drawString("Score : " + (length-slength), 170, 265);
		    size = size - 2;
	  	    offGraphics.setFont(font = new Font(name, style, size));
			//Draw graphics on screen (1)
		    if (current==0) {
		        offGraphics.drawImage(ilogo, 125, 2, this);
	 	        offGraphics.drawImage(ipresents, 75, 120, this);
	 	        offGraphics.drawImage(iwormy, 110, 165, this);
	 	        offGraphics.drawImage(iclick, 75, 270, this);
		    }
		    else { 	//Draw graphics on screen (2)
	 	        offGraphics.drawImage(iwormy, 110, 2, this);
	 	        offGraphics.drawImage(ihiScore, 20, 100, this);
	 	        offGraphics.drawImage(ikeys, 220, 100, this);
	 	        offGraphics.drawImage(iclick, 75, 270, this);
	 	        offGraphics.drawImage(ioptions, 150, 240, this);
	        	offGraphics.setColor(new Color(116, 129, 128));
				size = size + 4;
				//Draw high scores
				offGraphics.setFont(font = new Font(name, style, size));
	 	    	for (int loop = 0; loop < 5; loop++) {
		            offGraphics.drawString(hiname[loop], 80, 140 + loop*20);
		            offGraphics.drawString("" + hiscr[loop], 150, 140 + loop*20);
				}
				//Draw keys
				offGraphics.drawString("" + kUp, 350, 140);
				offGraphics.drawString("" + kDown, 350, 160);
				offGraphics.drawString("" + kLeft, 350, 180);
				offGraphics.drawString("" + kRight, 350, 200);
				offGraphics.drawString("" + kPause, 350, 220);
				size = size - 4;
				offGraphics.setFont(font = new Font(name, style, size));
		    }
		}
	
		//Paint the image onto the screen.
		g.drawImage(offImage, 0, 0, this);        

  	  }

		//Restart the thread
		if (theThread != null) 
			theThread.resume();
    }
}
