// 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;

	// create the bucket: only one bucket exists so no messing about
	if (buckets!=document.getElementById('Bucket').getElementsByTagName('p'))
	{
		// if this is the time the game has run, make buckets[0] the template bucket
		buckets = document.getElementById('Bucket').getElementsByTagName('p');	
	}
	InitBucket(buckets[0]);
	PlaceBucketBackground(buckets[0]);
	
	if (balls!=document.getElementById('TheBalls').getElementsByTagName('img'))
	{
		// if this is the time the game has run, make balls[0] the template ball
		balls = document.getElementById('TheBalls').getElementsByTagName('img');
	}
	
	if (sources!=document.getElementById('Sources').getElementsByTagName('img'))
	{
		// if this is the time the game has run, make sources[0] the template source
		sources = document.getElementById('Sources').getElementsByTagName('img');
	}
	
	if (scoreCards!=document.getElementById('ScoreCards').getElementsByTagName('p'))
	{
		// if this is the time the game has run, make scoreCards[0] the template score card
		scoreCards = document.getElementById('ScoreCards').getElementsByTagName('p');
	}
	
	//  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, make powerups[0] the template powerup
		powerups = document.getElementById('Powerups').getElementsByTagName('img');
	}
	
	InitLevel();
	NewLevelInfo();
}

function InitUI()
{
	setButtonStatus("PauseButton", "disable");
	setPauseButtonIcon();
	setButtonStatus("SettingsButton", "enable");
	setButtonStatus("NewGameButton", "disable");
	
	// set the correct positions for the ball sources based on the source width	
	initSourceX();
	
	// set the game values to match the defaults
	setSeed=defaultSetSeed;
	startSeed=defaultStartSeed;
	startLevel=defaultStartLevel;
	currentNumberLives = defaultNumberLives;
		
	// show the default game info
	document.getElementById("helpinfo").className = "default";
	
	// 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("start_screen"); // don't let the player start the game until all the files have loaded!
}

/////////////////////////////////////
// create and configure game objects

/////////////////////////////////////
// initialise objects
function InitObject(oObject, object_type, x_pos, y_pos, visibility, strategy)
{
	// general purpose object initialisation setting common properties
	// use this for all game objects
	AssignID(oObject); //give it a unique ID
	
	oObject._objectType =  object_type; // valid types are 'bucket', 'ball', 'powerup', 'scorecard', 'source'
	
	TeleportObject(oObject, x_pos, y_pos); // set the start position
	
	oObject._strategyCounter = 0;
	SetStrategy(oObject, strategy);
	
	oObject.style.visibility = visibility; // should the object be visible or hidden?
	return;
}

function InitObjectSize(oObject, width, height)
{
	oObject._width = width;
	oObject._height = height;
	oObject.style.width = Math.round(width) +'px';
	oObject.style.height = Math.round(height) +'px';
}

function InitVectorMovement(oObject, vx, vy, speed, maxSpeed)
{
	// initialises objects that move with vx, vy and speed
	oObject._vx = vx;
	oObject._vy = vy;
	oObject._maxSpeed = maxSpeed;
	oObject._speed = speed;
}

function InitOrthogonalMovement(oObject, xSpeed, ySpeed, maxSpeed)
{
	oObject._xSpeed = xSpeed;
	oObject._ySpeed = ySpeed;
	oObject._maxSpeed = maxSpeed;
}

/////////////////////////////////////
// create the bucket

function NewBucket()
{
	// this function isn't called at present because there is only 1 bucket
	var oBucket;
	
	// create a new Bucket as a copy of the first one
	var oParent = document.getElementById('Bucket');
	oParent.appendChild(buckets[0].cloneNode(true));
	
	// the new bucket is automatically added to the array of bucket objects
	oBucket = buckets[buckets.length-1];
	oBucket._index = buckets.length-1;
	
	return oBucket;
}

function InitBucket(oBucket)
{
	// if there's ever more than 1 bucket, move this into a function
	InitObject(oBucket, "bucket", bucketStartX, bucketStartY, "visible", "default");
	InitObjectSize(oBucket, bucketWidth, bucketHeight);
	InitOrthogonalMovement(oBucket, 0, 0, maxBucketSpeed);
	oBucket._thickness = bucketThickness;
	oBucket._fly = bucketDefaultFly;
	oBucket._catchYellows = bucketDefaultCatchYellows;
	oBucket._shake = false;
}

function PlaceBucketBackground(oBucket)
{
	// displays the correct slice of the bucket background depending on whether it can fly and/or catch red balls
	var x_pos = (-(bucketWidth * 2) * (oBucket._fly == "fly")) - (bucketWidth * (oBucket._catchYellows == "catch"));
	oBucket.style.backgroundPosition=x_pos + "px 0px";
	// var x_pos = (-64 * (oBucket._fly == "fly")) - (32 * (oBucket._catchYellows == "catch"));
	oBucket.style.backgroundPosition=x_pos + "px 0px";
}

/////////////////////////////////////
// create balls

function CreateBall(colour, x_pos, y_pos, speed, angle, strategy)
{
	// create a new ball
	var oBall = NewBall();
	
	// now convert this to x, y components of velocity
	var unitVector = GetUnitVector(angle);
				
	// choose a random rank between 1 and 10
	// rank helps ensure consistent z-order
	var rank = RandomInteger(10); 

	InitBall(oBall, colour, rank, "fall"); // initialise properties that are specific to balls
	InitObject(oBall,"ball", x_pos, y_pos, "visible", strategy); // general object initialisation
	InitObjectSize(oBall, ball_width[oBall._rank], ball_height[oBall._rank]);
	InitVectorMovement(oBall, unitVector._x, unitVector._y, speed, ballMaxSpeed);
}

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);
	
	// 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, colour, rank, strategy)
{	
	// assuming that balls may vary in size in future, keep rank for now
	oBall._rank = rank;
	
	if (colour == "red") { oBall._countMe = false; } // so if a red ball is caught, it won't count when it explodes
	else {oBall._countMe = true; }
	
	oBall._explodedBucket = false; // this ball hasn't exploded inside the bucket
	
	SetBallColour(oBall, colour);
	oBall._radius = ball_radius[oBall._rank];
	
	// set each ball to be their own starting leader, which probably won't be needed in Bucket
	oBall._leaderID = oBall._ID;
	
	// mark the ball as having not been captuyellow
	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();
	
	oSource.src="icons/game/" + sourcesIconRoot + "_" + colour + ".gif";
	oSource._numberCreated = 0;
	
	InitObject(oSource,"source", x_pos, y_pos, "visible", "stop");
	InitObjectSize(oSource, sourceWidth, sourceHeight);
	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);
	
	// 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 array
	// record the location in the 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 = eval("scorePowerup_" + type);
	if(speed==0) {
		oPowerup.src="icons/game/" + powerupsIconRoot + "_" + type + ".gif";
	}
	else {
		oPowerup.src="icons/game/" + powerupsIconRoot + "_" + type + "_moving.gif";
	}
	
	// set text that will be displayed when the powerup is triggered
	if (eval("powerup_text_" + type) != null)
	{
		oPowerup._text = eval("powerup_text_" + type);
	}
	else
	{
		// this should only happen if I've forgotten to define the text for a type of powerup
		oPowerup._text = "no text defined";
	}
	
	InitObject(oPowerup, "powerup", x_pos, y_pos, "visible", "active");
	InitObjectSize(oPowerup, powerupWidth, powerupHeight);
	InitVectorMovement(oPowerup, vx, vy, speed, powerupMaxSpeed);
	
	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);
	
	// 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;
}

/////////////////////////////////////
// object behaviour

function SetStrategy(oObject, strategy)
{
	switch(oObject._objectType)
	{
		case "bucket": // no strategy engine
			break;
		case "ball":
			SetBallStrategy(oObject, strategy);
			break;
		case "source":
			SetSourceStrategy(oObject, strategy);
			break;
		case "scorecard": // no strategy engine
			break;
		case "powerup":
			SetPowerupStrategy(oObject, strategy);
			break;
	}
}

/////////////////////////////////////
// bucket behaviour

// the bucket doesn't have a proper strategy engine, just a couple of functions for its behaviours (because it has a very limited number of behaviours, and they can overlap)

function BucketCatchYellows(oBucket)
{
	if (oBucket._strategyCounter <= 0)
	{
		document.getElementById("helpinfo").className = "default";
		oBucket._catchYellows = "nocatch";
		PlaceBucketBackground(buckets[0]);
	}
	else
	{
		oBucket._strategyCounter -= 1;
	}
	
	// control the countdown and flashing at end of powerup
	if (oBucket._strategyCounter <= flashStart - (numberFlashes * flashInterval))
	{
		var tempCatch;
		if (1 == numberFlashes % 2) { tempCatch = "catch"; }
		else {tempCatch = "nocatch"; }

		
//		(-(bucketWidth * 2) * (oBucket._fly == "fly")) - (bucketWidth * (oBucket._catchYellows == "catch"))
		
//		var x_pos = (-64 * (oBucket._fly == "fly")) - (32 * (tempCatch == "catch"));
		var x_pos = (-(bucketWidth * 2) * (oBucket._fly == "fly")) - (bucketWidth * (tempCatch == "catch"));
		oBucket.style.backgroundPosition=x_pos + "px 0px";
		numberFlashes += 1;
	}
}

function ShakeRectangle(oRectangle)
{
	if (oRectangle._shakeCounter <= 0)
	{
		oRectangle._shake = false
	}
	else
	{
		oRectangle._shakeCounter -= 1;
	}
	if (oRectangle._shakeCounter <= shakeDuration - (numberShakes * shakeInterval))
	{
		if (Math.abs(shakeOffset) >= maxOffset)
		{
			shakeShift *= -1;
		}
		shakeOffset += shakeShift;
		oRectangle._x += shakeShift;
		
		PlaceObject(oRectangle);
		numberShakes += 1;
	}
}

/////////////////////////////////////
// ball behaviour

function RunBallStrategy(oBall)
{
	switch (oBall._strategy)
	{
		case "drag": StrategyDrag(oBall);
			break;
		case "wait": StrategyBallWait(oBall);
			break;
		case "disperse": StrategyBallDisperse(oBall);
			break;
		case "preFall": StrategyPreFall(oBall);
			break;
		case "fall": StrategyFall(oBall);
			break;
		case "wait": StrategyWait(oBall);
			break;
		case "justCaptuyellow": StrategyJustCaptuyellow(oBall);
			break;
		case "longFuse": StrategyLongFuse(oBall);
			break;
		case "explode": StrategyExplode(oBall);
			break;
		case "die": StrategyDie(oBall);
			break;
	}
	return true;
}

function SetBallStrategy(oBall, strategy)
{
	// sets each strategy and its initial conditions
	switch (strategy)
	{
		case "drag": oBall._strategy = "drag";
			break;
		case "wait":
			oBall._strategyCounter = 750 / gameTick;
			oBall._strategy = "wait";
			break;
		case "disperse":
			oBall._strategyCounter = 1000 / gameTick;
			oBall._strategy = "disperse";
			break;
		case "preFall":
			oBall._strategyCounter = 10000 / gameTick;
			oBall._strategy = "preFall";
			break;
		case "fall": oBall._strategy = "fall";
			break;
		case "justCaptuyellow": oBall._strategy = "justCaptuyellow";
			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;
	}
	return true;
}

function StrategyDrag(oBall)
{
	// don't do anything while being dragged!
	oBall._speed = 0;
	return true;
}

function StrategyBallWait(oBall)
{
	// release the ball after a time delay
	if (oBall._strategyCounter <= 0)
	{
		SetStrategy(oBall, "disperse");
	}
	else
	{
		oBall._strategyCounter -= 1;
	}
	return;
}

function StrategyBallDisperse(oBall)
{
	// balls cannot be exploded during this time
	if (oBall._strategyCounter <= 0)
	{
		SetStrategy(oBall, "fall");
	}
	else
	{
		oBall._strategyCounter -= 1;
	}
	MoveObject(oBall, topEdge);
}

function StrategyPreFall(oBall)
{
	// newly-created balls move at constant velocity until they cross the Gravity Line
	if ((oBall._y >= gravityLine) || (oBall._strategyCounter <= 0))
	{
		SetStrategy(oBall, "fall"); // ball is now in the main map area
	}
	else
	{
		oBall._strategyCounter -= 1;
		MoveObject(oBall, topEdge);
	}
	return;
}

function StrategyFall(oBall)
{
	// change velocity due to gravity
	AccelerateUnderGravity(oBall);
	
	// update position
	MoveObject(oBall, gravityLine);
	
	// check for bucket collision
	var outcome = BallRectangleBounce(oBall, buckets[0], false, true, true, true);
	
	if ((outcome == "inside") && (oBall._strategy != "capture"))
	{
		SetStrategy(oBall, "justCaptuyellow");
	}
	
	return true;
}

function StrategyJustCaptuyellow(oBall)
{
	// when you catch a red ball, you lose the game, unless powerup is active
	if ((oBall._colour == "red") && (levelOver == false))
	{
		if (buckets[0]._catchYellows == "catch")
		{
			ShowScore(scoreYellow, oBall._x, oBall._y);
			// prime the ball to explode
			SetStrategy(oBall, "longFuse");
		}
		else
		{
			currentNumberLives -= 1;
			ShowLives();
			if (currentNumberLives == 0) {GameOver("lose"); }
			else { LevelFail(); }
		}
	}
	// if it wasn't red, it turns purple and prepares to explode
	else if (oBall._colour != "red")
	{
		// score for the capture
		var score;
		switch (oBall._colour)
		{
			case "yellow": 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, gravityLine);			
	}
	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 StrategyExplode(oBall)
{
	// delete the ball when the explosion animation finishes
	if (oBall._strategyCounter <= 0)
	{
		// count the ball at the end of the explosion
		if (true == oBall._countMe) { ballsToCatch -= 1; } // a red caught while powerup is active doesn't count towards level end
		numberExplosions -= 1; // note there is one fewer explosion on screen
		if (numberExplosions < 0) { numberExplosions = 0; } // can happen because only secondary explosions increment the count (to give a bonus for cascades)
		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 the bucket?
		if (oBall._explodedBucket == false)
		{
			var outcome = BallRectangleCollision(oBall, buckets[0]);
			if (outcome._horizontal != "nocollision")
			{
				oBall._explodedBucket = true;
				if (buckets[0]._shake == false)
				{
					ShowScore(scoreBucketExplosion, oBall._centreX, oBall._centreY);
					buckets[0]._shakeCounter = shakeDuration;
					numberShakes = 0;
					buckets[0]._shake = true;
				}
			}
		}
		
		// 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 "yellow":
								numberExplosions += 1; // bonus for catching balls in an explosion
								ShowScore(scoreRedExp + (numberExplosions * cascadeBonus), balls[i]._x, balls[i]._y);
								SetStrategy(balls[i], "explode");
								break;
							case "blue":
								numberExplosions += 1; // bonus for catching balls in an explosion
								ShowScore(scoreBlueExp + (numberExplosions * cascadeBonus), balls[i]._x, balls[i]._y);
								SetStrategy(balls[i], "explode");
								break;
							case "red":
								SetBallColour(balls[i], "yellow");
								balls[i]._countMe = true;
								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 SetSourceStrategy(oSource)
{
	switch (oSource._strategy)
	{
		case "stop": oSource._strategy = "stop";
			break;
		case "go": oSource._strategy =  "go";
			break;
	}
	return true;
}

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 requiyellow 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)
		{
			// give the balls some variation in initial speed
			var speed = 50 + RandomInteger(10);
		
			// choose a random direction pointing either left or right
			var angle = (RandomInteger(2) == 1)? -0.4 : -2.74;
			
			CreateBall(oSource._colour, oSource._x + (oSource._width/2) - ball_width[0]/2, oSource._y + ballStartHeight, speed, angle, "preFall");
			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();
	InitObject(oObject,"scorecard", x_pos - 10, y_pos - 20, "visible", "default");
	// score cards don't actually have a strategy engine so just use "default"
	InitVectorMovement(oObject, 0, -1, 10, 10);
	
	// set how long the score card will show
	oObject._strategyCounter = scoreCardTime;
	oObject.innerHTML = score;
	
	PlaceObject(oObject);
	return;
}

function UpdateScoreCard(oScoreCard)
{
	if (oScoreCard._strategyCounter <= 0)
	{
		DeleteScoreCard(oScoreCard);
		return;
	}
	oScoreCard._strategyCounter -= 1;
	MoveObject(oScoreCard, topEdge);
	return;
}

/////////////////////////////////////
// powerup behaviour

function RunPowerupStrategy(oPowerup)
{
	switch (oPowerup._strategy)
	{
		case "active":
			StrategyPowerupActive(oPowerup);
			break;
		case "Triggered":
			StrategyPowerupTriggered(oPowerup);
			break;
		case "die":
			StrategyPowerupDie(oPowerup);
			break;
	}
	return true;
}

function SetPowerupStrategy(oPowerup, strategy)
{
	switch (strategy)
	{
		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);
			}
		}	
	}
	var outcome = false;
	var direction_h;
	var direction_v;
	var bounced = false;
	
	if (oPowerup._speed != 0) // if this is a moving powerup
	{
		if (oPowerup._vx > 0) {direction_h = "right";} // horizontal collision
		else {direction_h = "left";}
		
		if (oPowerup._vy > 0) {direction_v = "down";} // horizontal collision
		else {direction_v = "up";}
				
		// check for collision with powerups
		for (i = 1; i <= (powerups.length-1); i++)
		{
			if (oPowerup._index != i) // don't collide with yourself
			{
				outcome = MovingVFixedRectangleCollision(oPowerup, powerups[i], direction_h);
				if (outcome == true)
				{
					oPowerup._vx *= -1; // bounce off!
					bounced = true;
				}
				outcome = MovingVFixedRectangleCollision(oPowerup, powerups[i], direction_v);
				if (outcome == true)
				{
					oPowerup._vy *= -1; // bounce off!
					bounced = true;
				}
			}
		}
		
		// check for collision with bucket
		outcome = MovingVFixedRectangleCollision(oPowerup, buckets[0], direction_h);
		if (outcome == true)
		{
			oPowerup._vx *= -1; // bounce off!
			bounced = true;
		}
		outcome = MovingVFixedRectangleCollision(oPowerup, buckets[0], direction_v);
		if (outcome == true)
		{
			oPowerup._vy *= -1; // bounce off!
			bounced = true;
		}
	}
	
	// if block to right and bucket to left
	// block to left, bucket to right
	// block above, bucket below
	// block below, bucket above
	// then don't move
	
	// update position
	if (bounced == false)
	{
		MoveObject(oPowerup,gravityLine);
	}
	return;
}

function PowerupEffect(oPowerup)
{
	// what happens when the powerup is Triggered?
	switch (oPowerup._type)
	{
		case ("block"):
			// it's just a block!
			break;
		case ("points"):
			ShowScore(oPowerup._score, balls[i]._x, balls[i]._y);
			PowerupAnnouncement(oPowerup._text);
			SetPowerupStrategy(oPowerup, "Triggered");
			break;
		case ("catchreds"):
			// temporarily lets the bucket catch red balls
			ShowScore(oPowerup._score, balls[i]._x, balls[i]._y);
			PowerupAnnouncement(oPowerup._text);
			SetPowerupStrategy(oPowerup, "Triggered");
			buckets[0]._catchYellows = "catch";
			document.getElementById("helpinfo").className = "catchreds";
			numberFlashes = 0;
			PlaceBucketBackground(buckets[0]);
			buckets[0]._strategyCounter = catchYellowDuration; //  around 10 seconds?
			break;
		case ("antigravity"):
			ShowScore(oPowerup._score, balls[i]._x, balls[i]._y);
			PowerupAnnouncement(oPowerup._text);
			SetPowerupStrategy(oPowerup, "Triggered");
			buckets[0]._fly ="fly";
			PlaceBucketBackground(buckets[0]);
			GravityAcceleration *= -1;
			break;
		case ("freefall"):
			ShowScore(oPowerup._score, balls[i]._x, balls[i]._y);
			PowerupAnnouncement(oPowerup._text);
			SetPowerupStrategy(oPowerup, "Triggered");
			buckets[0]._fly ="fly";
			PlaceBucketBackground(buckets[0]);
			GravityAcceleration = 0;
			break;
		case ("fly"):
			// bucket can fly for the rest of the level
			ShowScore(oPowerup._score, balls[i]._x, balls[i]._y);
			PowerupAnnouncement(oPowerup._text);
			SetPowerupStrategy(oPowerup, "Triggered");
			buckets[0]._fly ="fly";
			PlaceBucketBackground(buckets[0]);
			break;
		case ("balls"):
			// creates new balls
			ShowScore(oPowerup._score, balls[i]._x, balls[i]._y);
			PowerupAnnouncement(oPowerup._text);
			SetPowerupStrategy(oPowerup, "Triggered");
			PowerupNewBalls(3, 3, oPowerup._x + (powerupWidth / 2), oPowerup._y + (powerupHeight / 2));
			break;
	}
}

var numberOfPowerupAnnouncements = 0;

function PowerupAnnouncement(powerup_text)
// tell the player what kind of powerup they have just triggered
{
	WriteText("powerup_announcement", powerup_text);

		
	timerPowerupAnnouncement = setTimeout("CancelPowerupAnnouncement()", 4000);
	numberOfPowerupAnnouncements += 1;
}

function CancelPowerupAnnouncement()
{
	numberOfPowerupAnnouncements -= 1;
	if (numberOfPowerupAnnouncements < 1)
	{
		WriteText("powerup_announcement", "");
	}
}

function PowerupNewBalls(yellow_number, red_number, x_pos, y_pos)
{
	ballsToCatch += yellow_number;
	var speed;
	var angle;
	var i;
	for (i = 1; i <= yellow_number; i++)
	{
		// give the balls some variation in initial speed
		speed = 20 + RandomInteger(20);
		
		// choose a random direction pointing either left or right
		angle = RandomAngle();
		
		CreateBall("yellow", x_pos, y_pos, speed, angle, "wait");
	}
	for (i = 1; i <= red_number; i++)
	{
		// give the balls some variation in initial speed
		speed = 30 + RandomInteger(10);
		
		// choose a random direction pointing either left or right
		angle = RandomAngle();
		
		CreateBall("red", x_pos, y_pos, speed, angle,"wait");
	}
	return;
}

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;
	ShowLives();
	document.getElementById("helpinfo").className = "default";
	GravityAcceleration = defaultGravityAcceleration;
	buckets[0]._fly = bucketDefaultFly;
	numberExplosions = 0;
	CreateLevels();
	
	return;
}

function GameOver(outcome)
{
	levelOver = true;
	
	if (outcome == "win")
	{
		GameWin();
	}
	else if (outcome == "lose")
	{
		GameLose();
	}
	// 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]);
	if (buckets[0]._catchYellows == "catch")
	{
		BucketCatchYellows(buckets[0]);
	}
	
	if (buckets[0]._shake == true)
	{
		ShakeRectangle(buckets[0]);
	}
}

function ShowLives()
{
	switch (currentNumberLives)
	{
		case (3):
			Show("life_01");
			Show("life_02");
			Hide("life_03");
			break;
		case (2):
			Show("life_01");
			Hide("life_02");
			Hide("life_03");
			break;
		case (1):
			Hide("life_01");
			Hide("life_02");
			Hide("life_03");
			break;
	}
}

function ShowGameScore()
{
	document.getElementById("score").innerHTML=gameScore;
	document.getElementById("currentLevel").innerHTML=currentLevel;
	document.getElementById("ballsLeft").innerHTML=ballsToCatch;
}

window.onload = InitUI;

//-->




