// REFERENCE
/////////////////////////////////////
// functions to run the game

/////////////////////////////////////
// start the game

function InitGame()
{
	// choose a new random seed, or use the fixed one from the Settings dialog
	if (setSeed==true)
	{
		randomSeed = startSeed;
	}
	else
	{
		randomSeed = Math.round(Math.random() * 100000);
	}
	
	// reset the ID numbers
	nextID=0;

	// initialise the bucket
	// create the bucket: only one bucket exists so no messing about
	if (buckets!=document.getElementById('Bucket').getElementsByTagName('img'))
	{
		// only needs to be done when the game is first played
		var oBucket = NewBucket();		
	}

	// position the bucket
	TeleportObject(buckets[0], bucketStartX, bucketStartY);
	buckets[0]._width = bucketWidth;
	buckets[0]._height = bucketHeight;
	
	// set the bucket speed to 0
	buckets[0]._xSpeed = 0;
	buckets[0]._ySpeed = 0;
	buckets[0]._thickness = bucketThickness;
	
	// set the template ball as the first element in the array and hide it
	if (balls!=document.getElementById('TheBalls').getElementsByTagName('img'))
	{
		// if this is the time the game has run, set the balls array to contain the "template" ball
		balls = document.getElementById('TheBalls').getElementsByTagName('img');
		SetStrategy(balls[0], "hide");
	}
	
	//  set the template source as the first element in the array and hide it
	if (sources!=document.getElementById('Sources').getElementsByTagName('img'))
	{
		// if this is the time the game has run, set the sources array to contain the "template" source
		sources = document.getElementById('Sources').getElementsByTagName('img');
		sources[0].style.top = "100px";
		sources[0].style.visibility = "hidden";
	}
	
	// set the template score card as the first element in the array and hide it
	if (scoreCards!=document.getElementById('ScoreCards').getElementsByTagName('p'))
	{
		// if this is the time the game has run, set the score cards array to contain the "template" score cards
		scoreCards = document.getElementById('ScoreCards').getElementsByTagName('p');
		
		// if more initialization is required, put it in a function
		scoreCards[0]._x = 0;
		scoreCards[0]._y = 0;
		scoreCards[0]._vx = 0;
		scoreCards[0]._vy = -1;
		scoreCards[0]._speed = 0;
	}
	
	//  set the template powerup as the first element in the array and hide it
	if (powerups!=document.getElementById('Powerups').getElementsByTagName('img'))
	{
		// if this is the time the game has run, set the powerups array to contain the "template" powerup
		powerups = document.getElementById('Powerups').getElementsByTagName('img');
	}
		
	InitLevel();
	NewLevelInfo();
}

function InitUI()
{
	// set the game values to match the defaults
	setSeed=defaultSetSeed;
	startSeed=defaultStartSeed;
	startLevel=defaultStartLevel;
	
	// set the default values of the Settings dialog
	document.getElementById("number_of_balls").value=startLevel;
	document.getElementById("seed").value=startSeed;
	document.getElementById("set_seed").checked=setSeed;
	SettingsDialogEnableSeed();
	Show("PlayGame"); // don't let the player start the game until all the files have loaded!
	
	//InitGame();
}

/////////////////////////////////////
// create and configure game objects

/////////////////////////////////////
// create the bucket

function NewBucket()
{
	// this is somewhat overkill if there is only 1 bucket, but it works...
	var oBucket;
	
	if (buckets!=document.getElementById('Bucket').getElementsByTagName('img'))
	{
		// if this is the first Bucket created
		// Connect the array of Buckets to the 'template' image
		buckets = document.getElementById('Bucket').getElementsByTagName('img');
	}
	
	else
	{
		// create a new Bucket as a copy of the first one
		var oParent = document.getElementById('Bucket');
		oParent.appendChild(buckets[0].cloneNode(true));
	}
	
	// add them to the array of Buckets
	oBucket=buckets[buckets.length-1];
	oBucket._index = buckets.length-1;
	
	return oBucket;
}

/////////////////////////////////////
// create balls

function CreateBall(colour, x_pos, y_pos)
{
	// create a new ball
	var oBall = NewBall();
	
		// generate the ball's starting properties
		// choose a starting position
		x = x_pos;
		y = y_pos;
				
		// give the balls some variation in initial speed
		var new_speed = 20 + RandomInteger(5);
		
		// choose a random direction pointing either left or right
		var new_angle = (RandomInteger(2) == 1)? 0 : Math.PI;

		// now convert this to x, y components of velocity
		var unitVector = GetUnitVector(new_angle);
				
		// choose a random rank between 1 and 10
		// rank helps ensure consistent z-order
		var rank = RandomInteger(10); 
		
		AssignID(oBall);

		InitBall(oBall, x_pos, y_pos, unitVector._x, unitVector._y, ballMaxSpeed, new_speed, colour, rank, "fall");
		oBall.style.visibility = "visible";
}

function NewBall()
{
	// create a new ball as a copy of the first one
	var oBall = balls[0].cloneNode(true);
	
	var oParent = document.getElementById('TheBalls');
	oParent.appendChild(oBall);
	
	// give the new object a unique ID
	AssignID(oBall);
	
	// the clone is automatically added to the end of the balls array
	// record the ball's location in the balls array for easy deletion
	oBall=balls[balls.length-1];
	oBall._index = balls.length-1;
	return oBall;
}	

function InitBall(oBall, x, y, velocity_x, velocity_y, maxSpeed, speed, colour, rank, strategy)
{	
	// initialize their properties
	oBall._vx = velocity_x;
	oBall._vy = velocity_y;
	oBall._maxSpeed = maxSpeed;
	oBall._speed = speed;
	oBall.style.visibility = "visible";
	
	// assuming that balls may vary in size in future, keep rank for now
	oBall._rank=rank;

	SetBallColour(oBall, colour);
	
	// x, y & z co-ordinates should be stored as integers
	oBall._x = x;
	oBall._y = y;

	PlaceObject(oBall);
	
	// collision area/visible icon size comes from a fixed array
	oBall._width = ball_width[oBall._rank];
	oBall._height = ball_height[oBall._rank];
	oBall._radius = ball_radius[oBall._rank];
		
	oBall.style.width = ball_width[oBall._rank] +'px';
	oBall.style.height = ball_height[oBall._rank] +'px';
	
	// set their starting strategy
	SetStrategy(oBall, strategy);

	// initialise the strategy counter to 0
	oBall._counter = 0;
	
	// and the counter for choosing a strategy, but out of step with everybody else
	oBall._chooseStrategyCounter = RandomInteger(10);
	
	// set each ball to be their own starting leader, which should bump them into solo strategy
	oBall._leaderID = oBall._ID;
	
	// mark the ball as having not been captured
	oBall._capture = false;
}

function SetBallColour(oBall, colour)
{
	// set the colour property
	oBall._colour=colour;
	
	// set the image
	oBall.src="icons/game/" + ballsIconRoot + "_" + oBall._colour + ".gif";
	
	return;
}

/////////////////////////////////////
// create sources

function CreateSource(colour, x_pos, y_pos)
{
	var oSource = NewSource();
	
	// give it a unique ID
	AssignID(oSource);
	
	// initialise its properties
	oSource._x = x_pos;
	oSource._y = y_pos;
	
	TeleportObject(oSource, x_pos, y_pos);
	oSource.style.visibility = "visible";
	oSource.src="icons/game/" + sourcesIconRoot + "_" + colour + ".gif";
	
	oSource._height = sourceHeight;
	oSource._width = sourceWidth;
	oSource.style.height = oSource._height + "px";
	oSource.style.width = oSource._width + "px";
	oSource._strategy = "stop";
	oSource._numberCreated = 0;
	oSource._strategyCounter = 0;
	
	return oSource;
}

function NewSource()
{
	// create a new source as a copy of the first one
	var oSource = sources[0].cloneNode(true);
	
	// append it to the same parent node as the first one
	var oParent = document.getElementById('Sources');
	oParent.appendChild(oSource);
	
	// give the new object a unique ID
	AssignID(oSource);
	
	// the clone is automatically added to the end of the sources array
	// record the source's location in the balls array for easy deletion
	oSource=sources[sources.length-1];
	oSource._index = sources.length-1;
	
	return oSource;
}

/////////////////////////////////////
// create score cards

function NewScoreCard()
{
	// create a new score card as a copy of the first one
	var oScoreCard = scoreCards[0].cloneNode(true);
	
	var oParent = document.getElementById('ScoreCards');
	oParent.appendChild(oScoreCard);
	
	// give the new object a unique ID
	AssignID(oScoreCard);
	
	// the clone is automatically added to the end of the balls array
	// record the ball's location in the balls array for easy deletion
	oScoreCard=scoreCards[scoreCards.length-1];
	oScoreCard._index = scoreCards.length-1;
	
	return oScoreCard;
}

/////////////////////////////////////
// create powerups

function CreatePowerup(type, x_pos, y_pos, vx, vy, speed)
{
	var oPowerup = NewPowerup();
	
	// initialise its properties
	oPowerup._type = type;
	oPowerup._score = scorePowerupPoints; // when more types of powerup, switch by type
	
	oPowerup._x = x_pos;
	oPowerup._y = y_pos;
	PlaceObject(oPowerup);
	
	oPowerup._vx = vx;
	oPowerup._vy = vy;
	oPowerup._speed = speed;
	oPowerup._maxSpeed = powerupMaxSpeed;
	oPowerup.style.visibility = "visible";
	oPowerup.src="icons/game/" + powerupsIconRoot + "_" + type + ".gif";
	
	oPowerup._height = powerupHeight;
	oPowerup._width = powerupWidth;
	oPowerup.style.height = oPowerup._height + "px";
	oPowerup.style.width = oPowerup._width + "px";
	oPowerup._strategy = "active";
	oPowerup._strategyCounter = 0;
	
	return oPowerup;
}

function NewPowerup()
{
	// create a new powerup as a copy of the first one
	var oPowerup = powerups[0].cloneNode(true);
	
	// append it to the same parent node as the first one
	var oParent = document.getElementById('Powerups');
	oParent.appendChild(oPowerup);
	
	// give the new object a unique ID
	AssignID(oPowerup);
	
	// the clone is automatically added to the end of the powerups array
	// record the powerup's location in the balls array for easy deletion
	oPowerup=powerups[powerups.length-1];
	oPowerup._index = powerups.length-1;
	
	return oPowerup;
}

/////////////////////////////////////
// ball behaviour

function RunBallStrategy(oBall)
{
	switch (oBall._strategy)
	{
		case "evaluate": StrategyEvaluate(oBall);
			break;
		case "drag": StrategyDrag(oBall);
			break;
		case "fall": StrategyFall(oBall);
			break;
		case "wait": StrategyWait(oBall);
			break;
		case "justCaptured": StrategyJustCaptured(oBall);
			break;
		case "longFuse": StrategyLongFuse(oBall);
			break;
		case "explode": StrategyExplode(oBall);
			break;
		case "die": StrategyDie(oBall);
			break;
		case "hide": StrategyHide(oBall);
			break;
	}
	return true;
}

function CheckStrategyCounter(oBall)
{
	//	check strategy every second
	if (oBall._chooseStrategyCounter <= 0)
	{
		ChooseBallStrategy(oBall);
	}
	else
	{
		oBall._chooseStrategyCounter -= 1;
	}
	return true;
}

function ChooseBallStrategy(oBall)
{
	SetStrategy(oBall, "fall");
	
	// reset the strategy counter
	oBall._chooseStrategyCounter = 1000 / gameTick;
	return true;
}

function SetStrategy(oBall, strategy)
{
	// sets each strategy and its initial conditions
	switch (strategy)
	{
		case "evaluate": oBall._strategy = "evaluate";
			break;
		case "drag": oBall._strategy = "drag";
			break;
		case "fall": oBall._strategy = "fall";
			break;
		case "wait":
		oBall._strategyCounter = 1000 / gameTick;
		oBall._strategy = "wait";
			break;
		case "justCaptured": oBall._strategy = "justCaptured";
			break;
		case "longFuse":
			SetBallColour(oBall, "purple");
			oBall._strategyCounter = 1500 / gameTick;
			if ((oBall._centreX >= buckets[0]._x + bucketEdge) && (oBall._centreX <= buckets[0]._x + buckets[0]._width - bucketEdge))
			// ball hits "sweet area" of bucket top
			{
				// sweet spot bounces ball and accelerates based on bucket speed
				oBall._vy = -1 * Math.abs(oBall._vy); // ball must move upwards
				AbsoluteAcceleration(oBall, buckets[0]._xSpeed, longFuseSpeed);
			}
			else
			{
				// edge shot sends ball straight up
				oBall._vx = 0;
				oBall._vy = -1;
				AbsoluteAcceleration(oBall, 0, longFuseSpeed);
			}
			oBall._strategy = "longFuse";
			break;
		case "explode":
			oBall._strategyCounter = explosionNoFrames * explosionFrameDelay / gameTick;
			oBall._ballsExploded = [];
			
			// resize to match new animation
			var oldWidth = oBall._width;
			var oldHeight = oBall._height;
			oBall._width = explodingBallWidth;
			oBall._height = explodingBallHeight;
			oBall.style.width = oBall._width + 'px';
			oBall.style.height = oBall._height + 'px';
			
			// reposition to keep centre constant
			oBall._x = oBall._x + (oldWidth / 2) - (oBall._width / 2);
			oBall._y = oBall._y + (oldHeight / 2) - (oBall._height / 2);
			TeleportObject(oBall, oBall._x, oBall._y);
			oBall.src="icons/game/" + ballsIconRoot + "_" + "exploding" + ".gif";	
			oBall._strategy = "explode";
			break;
		case "die":
			oBall._strategy = "die";
			break;
		case "hide":
			oBall.style.visibility = "hidden";
			oBall._strategy = "hide";
			break;
	}
	return true;
}

function StrategyEvaluate(oBall)
{
	ChooseBallStrategy(oBall);
	return true;
}

function StrategyDrag(oBall)
{
	// don't do anything while being dragged!
	oBall._speed = 0;
	return true;
}

function StrategyWait(oBall)
{
	// release the ball after a time delay based on its index position
	oBall._strategyCounter -= 1;
	if (oBall._strategyCounter <= 0)
	{
		SetStrategy(oBall, "fall");
	}	
}	

function StrategyFall(oBall)
{
	// change velocity due to gravity
	AccelerateUnderGravity(oBall);
	
	// update position
	MoveObject(oBall);
	
	// check for bucket collision
	var outcome = BallRectangleBounce(oBall, buckets[0], false, true, true, true);
	
	if ((outcome == "inside") && (oBall._strategy != "capture"))
	{
		SetStrategy(oBall, "justCaptured");
	}
	
	return true;
}

function StrategyJustCaptured(oBall)
{
	// when you first catch a yellow ball, you lose the game
	if ((oBall._colour == "yellow") && (levelOver == false))
	{
		GameOver("lose");
	}
	// if it wasn't yellow, it turns purple and prepares to explode
	else if (oBall._colour != "yellow")
	{
		// score for the capture
		var score;
		switch (oBall._colour)
		{
			case "red": score = scoreRed;
				break;
			case "blue": score = scoreBlue;
				break;
		}
		ShowScore(score, oBall._x, oBall._y);
		// prime the ball to explode
		SetStrategy(oBall, "longFuse");
	}
	// balls caught after game end will disappear
	else
	{
		SetStrategy(oBall, "die");
	}
}

function StrategyLongFuse(oBall)
{
	if (oBall._strategyCounter > 0)
	{
		oBall._strategyCounter -= 1;

		// change velocity due to gravity
		AccelerateUnderGravity(oBall);
		
		// update position
		MoveObject(oBall);			
	}
	else
	{
		SetStrategy(oBall, "explode");
	}
	return;
}

function StrategyDie(oBall)
{
	// don't delete the last ball - we need it to start a new game
	if (balls.length > 1)
	{
		DeleteBall(oBall);
	}
	else
	{
		oBall.style.visibility = "hidden";
	}
	return;
}

function StrategyHide(oBall)
{
	// ball hides and stays still, used for first (dummy and undeletable) ball
	oBall._speed = 0; // I think one browser had trouble with empty functions
}

function StrategyExplode(oBall)
{
	// delete the ball when the explosion animation finishes
	if (oBall._strategyCounter <= 0)
	{
		// count the ball at the end of the explosion
		ballsToCatch -= 1;
		SetStrategy(oBall, "die");
	}
	else
	{
		oBall._strategyCounter -= 1;
		
		// find the explosion's collision radius
		oBall._radius += (oBall._width / 2 - originalBallWidth / 2) * gameTick / (explosionNoFrames * explosionFrameDelay);
		
		// has the explosion caught another ball?
		// iterate through the balls
		var i;
		for (i = 0; i <= (balls.length-1); i++)
		{
			// don't test the exploding ball against itself
			if (balls[i]._index != oBall._index)
			{
				var result = CircleCircleCollision(balls[i], oBall);
				if (result._outcome == true)
				{
					// check whether this ball has been caught by this explosion before
					var alreadyExploded = findInArray(oBall._ballsExploded, balls[i]._ID);
					if ((alreadyExploded == -1) && (balls[i]._strategy == "fall")) // this is the first time this ball has been caught by this explosion, and the ball is falling
					{
						// record that this ball has been exploded by this explosion
						oBall._ballsExploded[oBall._ballsExploded.length] = balls[i]._ID;
						
						// score for the capture and handle the results
						switch (balls[i]._colour)
						{
							case "red":
								ShowScore(scoreRedExp, balls[i]._x, balls[i]._y);
								SetStrategy(balls[i], "explode");
								break;
							case "blue":
								ShowScore(scoreBlueExp, balls[i]._x, balls[i]._y);
								SetStrategy(balls[i], "explode");
								break;
							case "yellow":
								SetBallColour(balls[i], "red");
								ballsToCatch += 1; // this is now a ball to be caught
								ShowScore(scoreYellowExp, balls[i]._x, balls[i]._y);
								
								// accelerate ball away from centre of explosion
								explosionForce(oBall, balls[i], result);
								break;
							case "purple":
								SetStrategy(balls[i], "explode");
								break;
						}
					}
				}
			}
		}
	}
	return;
}

function explosionForce(oExplosion, oBall, result)
{
	if (result._distance < 0.001)
	{
		result._distance = 0.001; // prevent division by 0
	}
	var acceleration_factor = -20 * (originalBallWidth / (oExplosion._radius * result._distance)); // normalise the distance so accn is independent of distance (sorts out next calculation)
	//alert(result._distance + " " + acceleration_factor);
	var x_acc = acceleration_factor * result._x_distance;
	var y_acc = acceleration_factor * result._y_distance;
	AbsoluteAcceleration(oBall, x_acc, y_acc);
	return;	
}

/////////////////////////////////////
// source behaviour

function StartSource(oSource, number, colour, interval, delay)
{
	// create a ball immediately
	oSource._strategyCounter = delay * 1000 / gameTick;
	oSource._interval = interval;
	oSource._strategy = "go";
	oSource._targetNumber = number;
	oSource._actualNumber = 0;
	oSource._colour = colour;
}

function RunSourceStrategy(oSource)
{
	switch (oSource._strategy)
	{
		case "stop": StrategyStop(oSource);
			break;
		case "go": StrategyGo(oSource);
			break;
	}
	return true;
}

function StrategyStop(oSource)
{
	// don't do anything!
}

function StrategyGo(oSource)
{
	// if required number of balls created, turn off
	if (oSource._actualNumber >= oSource._targetNumber)
	{
		oSource._strategy = "stop";
	}
	else
		{
		// count a timer and then create a ball
		if (oSource._strategyCounter <= 0)
		{
			CreateBall(oSource._colour, oSource._x, oSource._y + oSource._height);
			oSource._actualNumber += 1;
			oSource._strategyCounter = oSource._interval * 1000 / gameTick;
		}
		else
		{
			oSource._strategyCounter -= 1;
		}
	}
	return;
}

/////////////////////////////////////
// score card behaviour

function ShowScore(score, x_pos, y_pos)
{
	if (levelOver == true) { return false; }
	//create a score card
	gameScore += score;
	var oObject = NewScoreCard();
	
	// set how long the score card will show
	oObject._counter = scoreCardTime;
	
	oObject.style.visibility = "visible";
	oObject.innerHTML = score;
	
	oObject._x = x_pos - 10;
	oObject._y = y_pos - 20;
	oObject._vx = 0;
	oObject._vy = -1;
	oObject._speed = 10;
	oObject._maxSpeed = 10;
	
	PlaceObject(oObject);
	return;
}

function UpdateScoreCard(oScoreCard)
{
	if (oScoreCard._counter <= 0)
	{
		DeleteScoreCard(oScoreCard);
		return;
	}
	oScoreCard._counter -= 1;
	// score cards rise up
	MoveObject(oScoreCard);
}

/////////////////////////////////////
// powerup behaviour

function RunPowerupStrategy(oPowerup)
{
	switch (oPowerup._strategy)
	{
//		case "wait":
//			StrategyPowerupWait(oPowerup);
//			break;
		case "active":
			StrategyPowerupActive(oPowerup);
			break;
		case "triggered":
			StrategyPowerupTriggered(oPowerup);
			break;
		case "die":
			StrategyPowerupDie(oPowerup);
			break;
	}
	return true;
}

function SetPowerupStrategy(oPowerup, strategy)
{
	switch (strategy)
	{
	//	case "wait":
	//		oPowerup._strategyCounter = 50 / gameTick;
	//		oPowerup._strategy = "wait";
	//		break;
		case "active": oPowerup._strategy = "active";
			break;
		case "triggered":
			oPowerup._strategyCounter = powerupTriggeredNoFrames * explosionFrameDelay / gameTick;
			oPowerup.src="icons/game/" + powerupsIconRoot + "_" + oPowerup._type + "_" + "triggered" + ".gif";
			oPowerup._strategy = "triggered";
			break;
		case "die": oPowerup._strategy = "die";
			break;
	}
	return true;
}

function StrategyPowerupWait(oPowerup)
{
	oPowerup._strategyCounter -= 1;
	if (oPowerup._strategyCounter<= 0)
	{
		SetPowerupStrategy(oPowerup, "active");
	}
	// do nothing
}

function StrategyPowerupActive(oPowerup)
{
	var outcome; 
	// check for collision with balls
	for (i = 1; i <= (balls.length-1); i++)
	{
		if (balls[i]._strategy == "explode")
		{
			outcome = BallRectangleCollision(balls[i], oPowerup);
			if (outcome._horizontal != "nocollision")
			{
				// explosion triggers powerup
				PowerupEffect(oPowerup);
			}
		}
		else
		{
			// ordinary balls bounce off the box
			outcome = BallRectangleBounce(balls[i], oPowerup, true, true, true, true);
			if ((outcome != "nocollision") && (balls[i]._colour == "purple"))
			{
				// purple ball triggers powerup
				PowerupEffect(oPowerup);
			}
		}	
	}
	// update position
	MoveObject(oPowerup);
	return;
}

function PowerupEffect(oPowerup)
{
	// what happens when the powerup is triggered?
	switch (oPowerup._type)
	{
		case ("points"):
			ShowScore(oPowerup._score, balls[i]._x, balls[i]._y);
			SetPowerupStrategy(oPowerup, "triggered");
			break;
		case ("block"):
			// it's just a block!
			break;
	}
}
	

function StrategyPowerupTriggered(oPowerup)
{
	// delete the powerup when the animation finishes
	if (oPowerup._strategyCounter <= 0)
	{
		SetPowerupStrategy(oPowerup, "die");
	}
	else
	{
		oPowerup._strategyCounter -= 1;
	}
}

function StrategyPowerupDie(oPowerup)
{
	DeletePowerup(oPowerup);
}

/////////////////////////////////////
// game management

function InitLevel()
{
	levelOver = false;
	
	switch (currentLevel)
	{
		case 1:
			// create one source
			var oSource2 = CreateSource("red", source2_x, source2_y);
				
			// create the first wave of red balls to catch
			// if ballsToCatch isn't set, the player will win the level immediately!
			ballsToCatch = 5;
			StartSource(oSource2, ballsToCatch, "red", 2, 0); 

			/*var oSource2 = CreateSource("red", 0, (bottomEdge - 32));
	
			StartSource(oSource2, 1, "red", 2, 0);
			
			var oSource3 = CreateSource("red", (rightEdge - 16), (bottomEdge - 32));
			ballsToCatch = 2;
			StartSource(oSource3, 1, "red", 2, 0);*/
			break;
		case 2:
			// create two sources
			var oSource1 = CreateSource("yellow", source1_x, source1_y);
			var oSource2 = CreateSource("red", source2_x, source2_y);
				
			// create the first wave of red balls to catch
			// if ballsToCatch isn't set, the player will win the level immediately!
			ballsToCatch = 5;
			StartSource(oSource2, ballsToCatch, "red", 2, 0);
			
			// start some yellow balls with a delay
			StartSource(oSource1, 5, "yellow", 8, 5);
			break;
		case 3:
			// create three sources
			var oSource1 = CreateSource("yellow", source1_x, source1_y);
			var oSource2 = CreateSource("red", source2_x, source2_y);
			var oSource3 = CreateSource("yellow", source3_x, source3_y);
				
			// create the first wave of red balls to catch
			// if ballsToCatch isn't set, the player will win the level immediately!
			ballsToCatch = 10;
			StartSource(oSource2, ballsToCatch, "red", 2, 0);
			
			// start some yellow balls with a delay
			StartSource(oSource1, 10, "yellow", 8, 5);
			StartSource(oSource3, 10, "yellow", 8, 7);
			break;
		case 4:
			// create two sources
			var oSource1 = CreateSource("yellow", source1_x, source1_y);
			var oSource2 = CreateSource("red", source2_x, source2_y);
			
			// create a points powerup
			var oPowerup1 = CreatePowerup("points", Math.round((rightEdge - leftEdge)/2 - (powerupWidth/2)), 200, 1, 0, 0);
			
			// create the first wave of red balls to catch
			// if ballsToCatch isn't set, the player will win the level immediately!
			ballsToCatch = 10;
			StartSource(oSource2, ballsToCatch, "red", 2, 0);
			
			// start some yellow balls with a delay
			StartSource(oSource1, 5, "yellow", 8, 5);
			
			break;
		case 5:
			// create three sources
			var oSource1 = CreateSource("yellow", source1_x, source1_y);
			var oSource2 = CreateSource("red", source2_x, source2_y);
			var oSource3 = CreateSource("yellow", source3_x, source3_y);
			
			// create a block
			var oPowerup1 = CreatePowerup("block", 0, 90, 1, 0, 0);
			
			//create a points powerup
			var oPowerup1 = CreatePowerup("points", Math.round((rightEdge - leftEdge)/2 - (powerupWidth/2) - 60), 130, 1, 0, 10);
			
			//create a points powerup
			var oPowerup1 = CreatePowerup("points", Math.round((rightEdge - leftEdge)/2 - (powerupWidth/2)), 180, 1, 0, 10);
			
			//create a points powerup
			var oPowerup1 = CreatePowerup("points", Math.round((rightEdge - leftEdge)/2 - (powerupWidth/2) + 60), 230, 1, 0, 10);
			
			// create a block
			var oPowerup1 = CreatePowerup("block", (rightEdge - powerupWidth), 270, 1, 0, 0);
				
			// create the first wave of red balls to catch
			// if ballsToCatch isn't set, the player will win the level immediately!
			ballsToCatch = 10;
			StartSource(oSource2, ballsToCatch, "red", 2, 0);
			
			// start some yellow balls with a delay
			StartSource(oSource1, 10, "yellow", 8, 5);
			StartSource(oSource3, 10, "yellow", 8, 7);
			
			break;
	}
	return;
}

function GameOver(outcome)
{
	if (outcome == "win")
	{
		GameWin();
	}
	else if (outcome == "lose")
	{
		GameLose();
	}
	levelOver = true;
	// stop the game timer
	clearTimeout(gameInterval);
	
	StopKeys();
	return;
}

function GameUpdate()
{
	// count how long the game has been running
	timeElapsed += gameTick/1000;
	
	// convert this to a string and format it for display
	gameTime = String(Math.round(timeElapsed * 10) / 10);

	// if time elapsed is a whole number, add .0 to ensure consistent length string
	if (gameTime == Math.round(gameTime))
	{
		gameTime = gameTime + ".0";
	} 
	
	if ((ballsToCatch > 0) && (levelOver == false))
	{
		ShowGameScore();
	}
	else if (levelOver == false)
	{
		LevelOver();
	}
	
	// execute ball source strategies
	for (i = 1; i <= (sources.length-1); i++)
	{
		oSource=sources[i];
		RunSourceStrategy(oSource);
	}

	// execute each ball's strategy
	var oBall;
	
	var i;
	for (i = 1; i <= (balls.length-1); i++)
	{
		oBall=balls[i];
		RunBallStrategy(oBall);
		
	}
	
	// move each score card
	var oScoreCard;

	for (i = 1; i <= (scoreCards.length-1); i++)
	{
		oScoreCard=scoreCards[i];
		UpdateScoreCard(oScoreCard);
	}

	// move each powerup
	var oPowerup;

	for (i = 1; i <= (powerups.length-1); i++)
	{
		oPowerup=powerups[i];	
		RunPowerupStrategy(oPowerup);
	}
	
	// move the bucket
	moveBucket(buckets[0]);
}

function ShowGameScore()
{
	WriteText("game_info", "Score: " + gameScore + "<br /><br />Level: " +  currentLevel + "<br /><br />Balls Left: " + ballsToCatch + "<br /><br />Time: " + gameTime + " secs");
}

window.onload = InitUI;

//-->




