SOURCE OF MelfBlock.java
This is the entire file needed for the MelfBlock applet


/*
 * MELFBLOCK (based on Breakout)
 * Code Written by Melissa Fedak
 * October 2005
 *
 *
 * THIS GAME IS ESSENTIALLY "BREAKOUT"    
 * PLAYER TRIES TO HIT ALL THE BLOCKS WITH THE BALL
 *
 * THERE ARE FOUR CLASSES IN THIS GAME:
 * 		BLOCK - EACH RECTANGULAR BLOCK THAT THE BALL HITS
 *		BLOCKCANVAS - THE CENTRAL AREA WHERE MOST OF THE ACTION TAKES PLACE (MOUSEEVENTS)
 *		SCOREPANEL - WHERE THE SCORE AND SPEED ARE KEPT TRACK OF (BUTTON ACTIONEVENTS)
 *		MELFBLOCK - THE MAIN APPLET THAT EVERYTHING ELSE IS BASED OFF OF
 *
 *
 *
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*; 	//NEEDED FOR MOUSE
import java.awt.font.*; 	//NEEDED FOR FONT
import java.awt.geom.*; 	//NEEDED FOR BALL

import java.lang.Integer.*;


//====================================================================
//====================================================================
//THIS IS A SMALL CLASS FOR THE RECTANGULAR BLOCKS PLAYER TRIED TO HIT

class Block extends Rectangle {

	Boolean on;
	Boolean solid;	//IF IT'S SOLID, IT CAN'T BE BROKEN
	Color blockColor;
	
	public Block(int xval, int yval, int w, int h) {  //*NOT* VOID; CONSTRUCTOR RETURNS A BLOCK!
		on = true;
		solid = false;
		x = xval;
		y = yval;
		width = w;
		height = h;
		
		blockColor = Color.black;		
	}
	
	public void turnOn() {
		on = true;
	}
	public Boolean turnOff() {
		if (solid == false) { //"SOLID" NOT USED YET; ALWAYS FALSE FOR NOW
			on = false;
			blockColor = Color.black;
			return true;
		}
		return false;
	}
	
	//NOT USED YET - FOR FUTURE FUNCTIONALITY
	public void makeSolid () {
		solid = true;
		setSpecificBlockColor(0);
	}

	//NOT USED YET - FOR FUTURE FUNCTIONALITY
	public void setSpecificBlockColor(int temp) {
		if (temp == 0)
			blockColor = Color.darkGray;
		else if (temp == 1)
			blockColor = new Color(64, 128, 128);
		else if (temp == 2)
			blockColor = new Color(128, 64, 64);
		else if (temp == 3)
			blockColor = new Color(128, 64, 128);
		else 
			blockColor = new Color(128, 128, 64);
	}
}


//====================================================================
//====================================================================
//THIS IS THE MAIN CANVAS WHRE INPUT IS RECEIVED AND GAME TAKES PLACE

class BlockCanvas extends Canvas implements MouseListener, MouseMotionListener {

	//VARIABLES
	//WIDTH AND HEIGHT OF ALL RECTANGLES IN GAME (BLOCKS AND PLAYER)
	int rectWidth, rectHeight;
	
	//NUMBER OF COLUMNS AND ROWS IN DOUBLE ARRAY OF BLOCKS
	int blockRows, blockColumns;
	
	//CURRENT X AND Y CORDINATE OF BALL (UPPER LEFT HAND CORNER)
	int happyBallX, happyBallY;
	
	//STATE VARIABLES FOR STATUS OF BALL AND GAME
	Boolean gameStarted;
	Boolean gameOver;
	Boolean ballFree;
	Boolean lifeOver;

	//KEY OBJECTS IN GAME
	Rectangle playerRect; 
	Block[][] blockArray;
	Image happyBall;
	
	//NUMBER OF BLOCKS (SO GAME KNOWS WHEN WE'VE WON WITHOUT ITERATING)
	int numBlocks;
	
	//VARIABLES FOR PAINTING ONSCREEN AND OFFSCREEN (TO AVOID FLICKERING)
	Color currColor;	//MAIN COLOR OF BORDER AND PLAYER RECTANGLE
	Font currFont;
	Image offScreenImage;
	Graphics offScreenGraphics;
	
	//LINK BACK TO PARENT
	MelfBlock theApplet;

	public BlockCanvas(Color c, MelfBlock parent) { 
		
		playerRect = new Rectangle(rectWidth, rectHeight);
		
		theApplet = parent;
		
		numBlocks = 0;
		
		//INITIALIZE GAME STATUS
		gameStarted = false;
		gameOver = false;
		ballFree = false;
		lifeOver = false;
		
		//SET BALL IMAGES
		happyBall = theApplet.happyFace;
		
		//BALL LOCATION (INIT TO 0 FOR NOW)
		happyBallX = 0;
		happyBallY = 0;

		//SIZE OF THE MAIN PLAYER RECTANGLE (INIT TO 0 FOR NOW)
		rectWidth = 0; 
		rectHeight = 0;  
		
		//FOR MOUSE CLICKING AND MOVING INPUT
		addMouseListener(this);
		addMouseMotionListener(this);

		currColor = c;
		currFont = new Font("Arial", 1, 14);	//INEXPLICABLY DOESN'T KNOW "BOLD" OR Color.BOLD

		setBackground(new Color(0,0,0));

		//NOTE: CANVAS DOES NOT KNOW ITS OWN WIDTH OR HEIGHT IN THE CONSTRUCTOR
		//THEREFORE, MANY VARIABLES ARE INITIALIZED LATER, IN setConstants() BELOW
		
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THIS FUNCTION INITIALIZES KEY VARIABLES AND OBJECTS 
	//WHEN GAME IS STARTING AND CANVAS SIZE IS KNOWN
	
	public void setConstants() {
		rectWidth = (int)getWidth()/10;
		rectHeight = (int)getHeight()/25;
		blockRows = (int)getWidth()/rectWidth;
		blockColumns = (int)getHeight()/rectHeight/2;
		
		theApplet.statPanel.score.setText("00000");
		
		theApplet.currFace = happyBall;
		
		blockArray = new Block[blockRows][blockColumns];
		
		numBlocks = 0;
		//INITIALIZE ALL BLOCKS
		for (int i = 0; i < blockRows; i++)
			for (int j = 0; j < blockColumns; j++) {
				blockArray[i][j] = new Block(i*rectWidth, j*rectHeight, rectWidth, rectHeight);
				//if not solid:
				blockArray[i][j].blockColor = theApplet.chooseColor();
				numBlocks++;
			}
						
	 	gameOver = false;
	 	gameStarted = true;
	 	theApplet.youWin = false;
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THIS FUNCTION MOVES PLAYER RECTANGLE WHEN MOUSE IS MOVED

  	public void mouseMoved(MouseEvent e) {
		
  		if (gameStarted) {
  		
			//IF MOUSE IS WITHIN CANVAS AREA, MOVE RECTANGLE, OR MOVE IT TO NEAREST EDGE
			if (e.getX() < rectWidth/2) {
				playerRect.setLocation(0, getHeight() - rectHeight*2);
			}
			else if (e.getX() > getWidth() - rectWidth/2) {
				playerRect.setLocation(getWidth() - rectWidth,getHeight() - rectHeight*2);
			}
			else {  // if (e.getX() >= rectWidth/2 && e.getX() <= getWidth() - rectWidth/2) 		
	  			playerRect.setLocation(e.getX() - rectWidth/2, getHeight() - rectHeight*2);
	  		}
			
			//IF BALL ISN'T 'FREED' KEEP BALL FOLLOWING ABOVE RECTANGLE UNTIL CLICKED
			if (!ballFree) {
				happyBallX = (int)playerRect.getX() + rectWidth/2 - happyBall.getWidth(this)/2;
				happyBallY = (int)playerRect.getY() - happyBall.getHeight(this);
			}
						
			repaint();
		}
  	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THIS FUNCTION CONTROLS WHEN A NEW GAME HAS STARTED 
	//AND STARTS BALL MOVING ("FREES BALL") IF IT HAS NOT YET
	
  	public void mouseReleased(MouseEvent e) {
	 	
	 	//IF WE ARE IN LIMBO BETWEEN THE GAME STARTING AND THE GAME ENDING
	 	if (!gameStarted || gameOver) {
	 		
	 		//RESET VARIABLES
	 		setConstants();

	 		//SET BALL AND RECTANGLE IN INITIAL POSITIONS
	 		playerRect.setLocation(getWidth()/2 - rectWidth/2, getHeight() - rectHeight*2);
	 		
	 		happyBallX = (int)(playerRect.getX() + rectWidth/2 - happyBall.getWidth(this)/2);
			happyBallY = (int)playerRect.getY() - happyBall.getHeight(this);

	 	}
	 	
	 	//IF MOUSE IS CLICKED BUT GAME HAS ALREADY STARTED, FREE THE BALL
	 	else if (gameStarted && !ballFree) {
	 		ballFree = true;
	 	}
	 	
	 	//OTHERWISE, DO NOTHING; CLICKING IS IRRELEVANT DURING THE GAME
	 	
	 	repaint();
  	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THESE FUNCTIONS OVERRIDE ALL MOUSE EVENTS, EVEN THAT I DON'T USE, AS REQUIRED BY JAVA
	
	public void mouseClicked(MouseEvent e) {
  	}
  	public void mouseDragged(MouseEvent e) {
  	}
  	public void mousePressed(MouseEvent e) {
  	}
  	public void mouseEntered(MouseEvent e) {
  	}
  	public void mouseExited(MouseEvent e) {
  	}


	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THE THREE BELOW FUNCTIONS PAINT THE CURRENT POSITION OF BLOCKS, 
	//PLAYER RECTANGLE, AND BALL TO SCREEN USING OFFSCREEN GRAPHICS TO AVOID FLICKERING

	//OVERRIDING UPDATE TO AVOID FLICKERING
	public void update(Graphics g) {
		paint(g);
	}

	public void doOffScreenPainting() {
	
		if (offScreenImage == null) {		// || graphicsWidth != getWidth() || graphicsHeight != getHeight()) {
			offScreenImage = createImage((int)getWidth(), (int)getHeight());
			offScreenGraphics = offScreenImage.getGraphics();
		}
		
		offScreenGraphics.setColor(Color.black);
		offScreenGraphics.fillRect(0, 0, getWidth(), getHeight());
	
		offScreenGraphics.setColor(getBackground());
		offScreenGraphics.fillRect(0,0,getWidth(),getHeight());
		offScreenGraphics.setColor(currColor);
		
		//PAINT "ON" BLOCKS WITH BLOCK COLOR
		for (int i = 0; i < blockRows; i++) {
			for (int j = 0; j < blockColumns; j++) {
				if (blockArray[i][j].on) {
					offScreenGraphics.setColor(blockArray[i][j].blockColor);
					offScreenGraphics.fill3DRect(i*rectWidth, j*rectHeight, rectWidth, rectHeight, true);
				}
			}
		}

		//SET BACK TO MAIN COLOR
		offScreenGraphics.setColor(currColor);

		if (theApplet.youWin) {
		
			String s =    "YOU WIN! YAY!!";
			String t =         "Score: ";
			String u = "* Click to Start Again *";
			
			offScreenGraphics.setFont(currFont);
			
			offScreenGraphics.fillRect(getWidth()/2 - 100, getHeight()/2 - 100, 200, 200);			
			offScreenGraphics.drawImage(happyBall, getWidth()/2 - happyBall.getWidth(this), getHeight()/2 - 100 + 40, this);
			
			offScreenGraphics.setColor(new Color(255,255,255));
			offScreenGraphics.drawString(s, getWidth()/2 - 100 + 48, 200);
			offScreenGraphics.drawString(t, getWidth()/2 - 100 + 53, 230);
			offScreenGraphics.drawString(theApplet.statPanel.score.getText(), getWidth()/2 - 100 + 130, 230);
			offScreenGraphics.drawString(u, getWidth()/2 - 100 + 40, 260);

		}
		else if (gameOver) {
		
			String s =       "GAME OVER";
			String t =         "Score: ";
			String u = "* Click to Start Again *";
			
			offScreenGraphics.setFont(currFont);
			
			offScreenGraphics.fillRect(getWidth()/2 - 100, getHeight()/2 - 100, 200, 200);				
			offScreenGraphics.drawImage(theApplet.currFace, getWidth()/2 - happyBall.getWidth(this)/2, getHeight()/2 - 100 + 40, this);
			
			offScreenGraphics.setColor(new Color(255,255,255));
			offScreenGraphics.drawString(s, getWidth()/2 - 100 + 54, 200);
			offScreenGraphics.drawString(t, getWidth()/2 - 100 + 56, 230);
			offScreenGraphics.drawString(theApplet.statPanel.score.getText(), getWidth()/2 - 100 + 111, 230);
			offScreenGraphics.drawString(u, getWidth()/2 - 100 + 25, 260);

		}
		else if (gameStarted) {
						
			//PAINTS BALL AND PLAYER RECTANGLE IN THEIR CURRENT POSITONS
			offScreenGraphics.fill3DRect((int)playerRect.getX(), (int)playerRect.getY(), rectWidth, rectHeight, true);
			offScreenGraphics.drawImage(theApplet.currFace, happyBallX, happyBallY, this);

		}
		else { //IF IT IS THE FIRST SCREEN OF THE GAME
		
			String s = "Bounce the Ball";
			String t = "and Break the Blocks!";
			String u = "* Click to Begin *";
			
			offScreenGraphics.setFont(currFont);
			
			offScreenGraphics.fillRect(getWidth()/2 - 100, getHeight()/2 - 100, 200, 200);	
			offScreenGraphics.drawImage(theApplet.bounceFace, getWidth()/2- happyBall.getWidth(this)/2, getHeight()/2 - 100 + 40, this);
			offScreenGraphics.drawImage(theApplet.oopsFace, getWidth()/2- happyBall.getWidth(this)/2, getHeight()/2 - 100 + 40, this);
			offScreenGraphics.drawImage(theApplet.currFace, getWidth()/2 - happyBall.getWidth(this)/2, getHeight()/2 - 100 + 40, this);
				
			
			offScreenGraphics.setColor(new Color(255,255,255));
			offScreenGraphics.drawString(s, getWidth()/2 - 100 + 43, 200);
			offScreenGraphics.drawString(t, getWidth()/2 - 100 + 22, 230);
			offScreenGraphics.drawString(u, getWidth()/2 - 100 + 40, 260);	
		}			
	}

	public void paint(Graphics g) {

		doOffScreenPainting();
		g.drawImage(offScreenImage, 0, 0, this);

	}
	

}

//====================================================================
//====================================================================
// SCORE PANEL CLASS (THE LOWEST PANEL WHERE SCORE AND SPEED (AND LATER, TIME) ARE LOCATED

class ScorePanel extends Panel implements ActionListener {

	Button speedUpButton;
	Button speedDownButton;
	
	Label scoreLabel;
	Label score;
	Label time;
	Label speedLabel;
	Label speed;
	
	int scoreInc;
	int pauseTime;
	
	Font mainFnt;
	Font smallFnt;
	
	MelfBlock theApplet;

	ScorePanel(MelfBlock parent, Font f) {
	
		theApplet = parent;
		
		scoreInc = 10;
		pauseTime = 8; //SPEED = 2;
		
		mainFnt = f;
		smallFnt = new Font("Arial", 0, 10);
		
		Panel p =  new Panel();
		p.setLayout(new FlowLayout(FlowLayout.CENTER));
		
		/* WILL IMPLEMENT LATER		
		time = new Label("0:00");
		time.setFont(smallFnt);
		time.setBackground(theApplet.mainColor);
		p.add(time);
		*/
		
		Label spacer = new Label("       ");
		spacer.setBackground(theApplet.mainColor);
		p.add(spacer);
		
		scoreLabel = new Label("Score:");
		scoreLabel.setFont(smallFnt);
		scoreLabel.setBackground(theApplet.mainColor);
		p.add(scoreLabel);
		
		//GET SCORE VALUE FROM VALUE SENT IN BY APPLET
		score = new Label("00000");
		score.setFont(mainFnt);
		score.setBackground(theApplet.mainColor);
		p.add(score);
		
		Label spacer2 = new Label("       ");
		spacer2.setBackground(theApplet.mainColor);
		p.add(spacer2); 
		
		speedDownButton = new Button(" - ");
		speedDownButton.setFont(mainFnt);
		p.add(speedDownButton);
		speedDownButton.addActionListener(this);
		
		speedUpButton = new Button(" + ");  //INCREASE SPEED = DECREASE PAUSE TIME
		speedUpButton.setFont(mainFnt);
		p.add(speedUpButton);
		speedUpButton.addActionListener(this);
		
		Label spacer3 = new Label("       ");
		spacer3.setBackground(theApplet.mainColor);
		p.add(spacer3); 
		
		speedLabel = new Label("SPEED:");
		speedLabel.setFont(mainFnt);
		speedLabel.setBackground(theApplet.mainColor);
		p.add(speedLabel);
		
		speed = new Label(Integer.toString(10 - pauseTime));
		speed.setFont(smallFnt);
		speed.setBackground(theApplet.mainColor);
		p.add(speed);
		
		p.add(spacer); //SPACER
		
		add(p);
		
		//Integer.parseInt(hoursWorked.getText());
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THIS STANDARD FUNCTION CATCHES WHEN THE SPEED BUTTONS ARE PRESSED
	
	public void actionPerformed(ActionEvent e) {
		//String arg = e.getActionCommand();
		if (e.getSource() == speedUpButton) {
			if (pauseTime > 0)  {
				pauseTime--;
				speed.setText(Integer.toString(10 - pauseTime));
				speedDownButton.setEnabled(true);
			}
			else
				speedUpButton.setEnabled(false);
		}
		else if (e.getSource() == speedDownButton) {
			if (pauseTime < 10)  {
				pauseTime++;
				speed.setText(Integer.toString(10 - pauseTime));
				speedUpButton.setEnabled(true);
			}
			else
				speedDownButton.setEnabled(false);
		}		
	}
	

}

//================================================================
// MAIN APPLET CLASS

public class MelfBlock extends java.applet.Applet implements Runnable {

	private Thread runner;
	
	int xDir;  		//DIRECTION OF X
	int yDir;
	int xInc;  		//INCREMENT AMOUNT OF X
	int yInc;
	
	Boolean youWin;
	
	//LAYOUT
	Panel eastPanel;
	Panel westPanel;
	Panel topPanel;
	ScorePanel statPanel;
	//TitleCanvas topCanvas; //NOT USED AT THE MOMENT
	BlockCanvas mainCanvas;
	
	//FOR topPanel:
	Label title;
	Label livesLeftLabel;
	Label livesLeft;
	int maxLives;
	int lives;
		
	Color mainColor;
	Font mainFont;
	
	Image happyFace, bounceFace, oopsFace;
	Image currFace;
	
	public void init() {

		happyFace = getImage(getCodeBase(), "happyface.gif");
		bounceFace = getImage(getCodeBase(), "happyfaceO.gif");
		oopsFace = getImage(getCodeBase(), "happyfaceX.gif");
		
		currFace = happyFace;
		
		youWin = false;
		
		//DIRECTION AND INCREMENTAL VALUE OF BALL
		xDir = 1;	//RIGHT
		yDir = -1;	//UP
		xInc = 1;	//ALWAYS A POSITIVE INTEGER
		yInc = 1;	//ALWAYS A POSITIVE INTEGER

		//score = 0;
		maxLives = 4;
		lives = maxLives;

		//MAKE MAIN COLOR RANDOM
		//mainColor = new Color((int)Math.floor(Math.random()*256),(int)Math.floor(Math.random()*256),(int)Math.floor(Math.random()*256));
		mainColor = chooseColor();

		mainFont = new Font("Arial", 1, 10);

		//CREATE MAIN CANVAS!
		mainCanvas = new BlockCanvas(mainColor, this);
		
		//CREATE PANEL WITH SCORE AND TIMER
		statPanel = new ScorePanel(this, mainFont);
		
		//CREATE TOP CANVAS WHERE TITLE AND LIVES LEFT ARE RECORDED
		//topCanvas = new TitleCanvas(mainColor, mainFont, happyFace);
		
		setLayout(new BorderLayout());

		add(eastPanel = new Panel(), BorderLayout.EAST);
		add(westPanel = new Panel(), BorderLayout.WEST);
		add(topPanel = new Panel(), BorderLayout.NORTH);
		add(statPanel, BorderLayout.SOUTH);
		add(mainCanvas, BorderLayout.CENTER);
		
		topPanel.setBackground(mainColor);
		eastPanel.setBackground(mainColor);
		westPanel.setBackground(mainColor);	
		statPanel.setBackground(mainColor);
		
		livesLeftLabel = new Label("Lives Left: ");
		livesLeftLabel.setFont(mainFont);
		livesLeft= new Label(Integer.toString(lives));
		livesLeft.setFont(mainFont);
		title = new Label("WELCOME TO MELFBLOCK!");
		title.setFont(mainFont);
		
		topPanel.setLayout(new FlowLayout(FlowLayout.RIGHT, 1,1));
		
		topPanel.add(title);
		topPanel.add(new Label("                   "));
		topPanel.add(livesLeftLabel);
		topPanel.add(livesLeft);
	}

	
	public void start() {
		if (runner == null) {
			runner = new Thread(this);
			runner.start();
		}
	}


	public void stop() {
			runner = null;
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THE BELOW FUNCTION ("RUN") AND ITS THREE HELPER FUNCTIONS DECIDE IF BALL NEEDS TO BOUNCE,
	//BOUNCE IT, CHANGE THE IMAGE TO REFLECT THAT, AND GET RID OF ANY BLOCKS IT BOUNCES AGAINST 

	public void run() {
	
		while (true) {

			currFace = happyFace;  //TO START
			
			//gameStarted - MOUSELISTENERS IN THE mainCanvas CLASS TAKE CARE OF THAT

			if (mainCanvas.ballFree == true && !youWin) {
			
				//WHILE BALL DOESN'T HIT BOTTOM
				while (mainCanvas.happyBallY < mainCanvas.getHeight()) { 
				
					//IF NOT FALLING DOWN THE BOTTOM, MAKE IT A HAPPY FACE
					if (currFace != oopsFace)
						currFace = happyFace;
				
					//IF PLAYER RECTANGLE CATCHES BALL, BOUNCE BALL WITH BOUNCE FACE
					if (yDir > 0 ) {
						if (mainCanvas.happyBallY + happyFace.getHeight(this) == mainCanvas.getHeight() - mainCanvas.rectHeight*2) { //DO "BALL.GETHEIGHT" BECAUSE BALL'S "Y" MEASURES FROM THE TOP OF THE BALL
							if (ballCaughtCheck())
								currFace = bounceFace;
							else {
								currFace = oopsFace;
							}
						}
					}
					
					//IF BALL BOUNCES AGAINST EDGE, MAKE IT A BOUNCE FACE
					if (ballHitsEdgeCheck())
						if (currFace != oopsFace) 
						 currFace = bounceFace;
						 
					//IF BALL HITS BLOCK (FUNCTION REMOVES IT AND INCREMENTS SCORE), MAKE IT A BOUNCE FACE
					if (ballHitsBlockCheck())
						currFace = bounceFace;
						
					//UPDATE SCREEN WITH NEW BALL FACE
					mainCanvas.repaint();
					
					//THE "SPEED"  (AMOUNT OF PAUSE BETWEEN REPAINTS)
					pause(statPanel.pauseTime);
					
					//PAUSE EXTRA TO SHOW DIFF FACE
					if (currFace == bounceFace)
						pause(50);
									
					//MOVE THE BALL DIAGONALLY
					mainCanvas.happyBallX = mainCanvas.happyBallX + xInc*xDir;
					mainCanvas.happyBallY = mainCanvas.happyBallY + yInc*yDir;

				} //END INNER WHILE LOOP
				
				//THIS WHILE LOOP EXITS WHEN BALL HITS BOTTOM, SO BALL-LIFE IS OVER
				mainCanvas.lifeOver = true;
				lives--;
				livesLeft.setText(Integer.toString(lives));
				
				if (lives < 0)
					gameOverReset();
				else
					lifeOverReset();
				
				mainCanvas.repaint();
								
			} //END IF
		} //END OUTER WHILE(TRUE) LOOP
	} //END RUN()

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THIS HELPER FUNCTION CHECKS IF PLAYER CAUGHT BALL AS IT COMES DOWN
		
	public Boolean ballCaughtCheck() {
	
		int ballLeft = mainCanvas.happyBallX + (happyFace.getWidth(this)*3/4);
		int ballRight = mainCanvas.happyBallX + (happyFace.getWidth(this)/4);
		int rectLeft = (int)mainCanvas.playerRect.getX();
		int rectRight = rectLeft + mainCanvas.rectWidth;
		
		//IF RECTANGLE 'CATCHES' BALL
		if (ballLeft >= rectLeft && ballRight <= rectRight) {
			
			if (yDir > 0) { //IF BALL IS GOING DOWN (IN THE POSITIVE DIRECTION), MAKE IT GO UP (IF IT IS FIRST SHOT, BALL IS ALREADY GOING UP, SO WE DON'T WANT TO MAKE IT GO DOWN)
				yDir = -1 * yDir;
				
				// IF EXACTLY IN CORNER AND BALL IS COMING IN AT FAR ANGLE, BOUNCE OFF OTHER SIDE
				if (ballLeft == rectLeft || ballLeft == rectLeft + 2 || ballLeft == rectLeft - 1)
					if (xDir > 0)
						xDir = xDir * -1;
				if (ballRight == rectRight || ballRight == rectRight + 1 || ballRight == rectRight - 2)
					if (xDir < 0)
						xDir = xDir * -1;
						
				return true;
			}
		}
		else	
			return false;
		return false;
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THIS HELPER FUNCTION CHECKS IF BALL HAS HIT EDGE AND IF SO, BOUNCES IT
	
	public Boolean ballHitsEdgeCheck() {
	
		//IF BALL IS ON LEFT OR RIGHT SIDE, SWITCH X DIRECTION
		if (mainCanvas.happyBallX == mainCanvas.getWidth() - happyFace.getWidth(this) || mainCanvas.happyBallX == 0) {
			xDir = -1 * xDir;
			return true;
		}
			
		//IF BALL IS AT TOP, SWITCH Y DIRECTION (NOT "ELSE" IN CASE BALL IS HITTING CORNER)
		if (mainCanvas.happyBallY == 0) {
			yDir = -1 * yDir;
			return true;
		}
		
		return false;
	}
	
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THIS HELPER FUNCTION CHECKS IF BALL HAS HIT ANY VISIBLE BLOCK FROM ABOVE OR BELOW
	//IF SO, IT GETS RID OF THE BLOCK (TURNS IT OFF) AND BOUNCES
	
	public Boolean ballHitsBlockCheck() {
	
		Boolean returnVal = false;
		String place[] = {"top", "bottom","left","right"};
		int x = 0;
		int y = 0;
		
		//THIS FOR LOOP CHECKS WHETHER ANY OF THE FOUR POINTS ON THE SIDES OF
		//THE BALL CONNECT WITH ANY RECTANGLE THAT IS ON.  IF SO, DELETE THAT RECTANGLE
		
		for (int i = 0; i < 4; i++) {	
			if (place[i] == "top") {
				x = (int)Math.floor( (mainCanvas.happyBallX + happyFace.getWidth(this)/2) / mainCanvas.rectWidth );
				y = (int)Math.floor(mainCanvas.happyBallY/mainCanvas.rectHeight);
			}
			else if (place[i] == "bottom") {
				x = (int)Math.floor( (mainCanvas.happyBallX + happyFace.getWidth(this)/2) / mainCanvas.rectWidth );
				y = (int)Math.floor(mainCanvas.happyBallY + happyFace.getHeight(this)/mainCanvas.rectHeight);
			}
			else if (place[i] == "left") {
				x = (int)Math.floor( (mainCanvas.happyBallX + happyFace.getWidth(this)/2) / mainCanvas.rectWidth );
				y = (int)Math.floor(mainCanvas.happyBallY + happyFace.getHeight(this)/2/mainCanvas.rectHeight);
			}
			else if (place[i] == "right") {
				x = (int)Math.floor( (mainCanvas.happyBallX + happyFace.getWidth(this)/2) / mainCanvas.rectWidth );
				y = (int)Math.floor(mainCanvas.happyBallY + happyFace.getHeight(this)/2/mainCanvas.rectHeight);
			}

			//VALIDATE X AND Y (SO NO BAD POINTER ERRORS)
			if (x >= 0 && x < mainCanvas.blockRows && y >= 0 && y < mainCanvas.blockColumns) {
			
				//IF BLOCK IS ON, TURN IT OFF AND BOUNCE
				if (mainCanvas.blockArray[x][y].on == true) {
					if (mainCanvas.blockArray[x][y].turnOff()) {
					    
					    int temp = Integer.valueOf(statPanel.score.getText());
						temp = temp + (10 - statPanel.pauseTime) * statPanel.scoreInc;
						statPanel.score.setText(Integer.toString(temp));
						
						mainCanvas.numBlocks--;
						if (mainCanvas.numBlocks <= 0) {
							youWin = true;
							int temp2 = Integer.valueOf(statPanel.score.getText());
							temp2 = temp2 + 10000;  //BONUS
							statPanel.score.setText(Integer.toString(temp2));
							gameOverReset();
						}
						
					}
					if (place[i] == "top" || place[i] == "bottom")
						yDir = -1 * yDir;
					else 
						xDir = -1 * xDir;
					returnVal = true;
				}
				else
					returnVal = false;
			}
		}
		return returnVal;
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THIS FUNCTION RESETS CERTAIN VARIABLES WHEN A BALL LIFE IS OVER
		
	public void lifeOverReset() {
		currFace = happyFace;
		mainCanvas.ballFree = false;
		//PUT BALL BACK INTO STARTING LOCATION
		mainCanvas.happyBallX = (int)mainCanvas.playerRect.getX() + mainCanvas.rectWidth/2 - happyFace.getWidth(this)/2;
		mainCanvas.happyBallY = (int)mainCanvas.playerRect.getY() - happyFace.getHeight(this);
		mainCanvas.lifeOver = false;

		//RESET SPEED		
		statPanel.pauseTime = 8;
		statPanel.speed.setText(Integer.toString(10 - statPanel.pauseTime));
		
		statPanel.speedUpButton.setEnabled(true);
		statPanel.speedDownButton.setEnabled(true);
	}


	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THIS FUNCTION RESETS CERTAIN VARIABLES WHEN THE TOTAL GAME IS OVER

	public void gameOverReset() {
	
		mainCanvas.gameOver = true;	
		lives = maxLives;
		livesLeft.setText(Integer.toString(lives));
		statPanel.pauseTime = 8;
		statPanel.speed.setText(Integer.toString(10 - statPanel.pauseTime));
		statPanel.speedUpButton.setEnabled(true);
		statPanel.speedDownButton.setEnabled(true);
		mainCanvas.ballFree = false;
		mainCanvas.gameStarted = false;
		mainCanvas.lifeOver = false;
			
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//THIS FUNCTION IS CALLED TO GET A RANDOM COLOR FROM MY COLOR SCHEME
	
	public Color chooseColor() {
		//RANDOMLY PICK ONE OF FOUR COLORS
		int temp = (int)(Math.floor(Math.random()*4));
		if (temp == 0)
			return new Color(64, 128, 64);
		else if (temp == 1)
			return new Color(64, 128, 128);
		else if (temp == 2)
			return new Color(128, 64, 64);
		else //if (temp == 3)
			return new Color(128, 64, 128);
	}
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//PAUSE THE THREAD
	
	public void pause(int t) {
	
		try {Thread.sleep(t); }
		catch (InterruptedException e) {}
						
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//A NECESSARY MAIN FUNCTION TO GET THE APPLET GOING?

	public static void main(String args[]) {
		MelfBlock MainMelfBlock = new MelfBlock();
		MainMelfBlock.init();
		MainMelfBlock.start();
 
		//AppletFrame.startApplet("MelfBlock", "Melf Block", args);
	}
	
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	//GET RID OF MEMORY-USING OFFSCREEN GRAPHICS 
	
	public void destroy() {
	
		mainCanvas.offScreenGraphics.dispose();
	
	}
}