Source: gameManager.js

/** A game manager that will run any updates that you need to keep track of. Also it keeps track of levels.
 * @class GameManager
 * @property {Boolean} isCollidable=false - value if the object can be collided with. Defaults false.
 * @property {Number} level=0 - the current level of the game. Defaults zero. 
 * @property {Array} events - the array of events to be carried out by the game manager.
 */
function GameManager(){
	var self = this;
	/** Creates the game manager object
	 * @memberof GameManager
	 * @function GameManager#constructor
	 */
	self.constructor = function() {
		GameObject.call(self, "GameManager", null, 0, 0);
		self.isCollidable=false;
		self.level = 0;
		self.events = [];
	}
	/** Creates GameManager */
	self.constructor();
	/** Override this function to put in your custom Updates for your game. Called at start of update.
	  * @memberof GameManager
	  * @function GameManager#customUpdate
	  * @param {Game} game - The game object
	  */
	self.customUpdate = function(game){}
	/** The update function of the object. Do not override this function. Handles all events
	  * @memberof GameManager
	  * @function GameManager#update
	  * @param {Game} game - The game object
	  */
	self.update = function(game) {
		self.customUpdate(game);
		for (var i = self.events.length - 1; i >= 0; --i) {
			//console.log(self.events[i]);
			if (self.events[i].type == "timed") {
				//console.log(self.events[i].steps);
				if (--self.events[i].steps <= 0) {
					//console.log("success!");
					self.events[i].func();
					if (self.events[i].repeat) {
						if (self.events[i].random) {
							self.events[i].steps = self.events[i].orig();
						} else {
							self.events[i].steps = self.events[i].orig;
						}
						//console.log("schedule")
					} else {
						self.events.splice(i, 1);
						//console.log("deleted event")
					}
				}
			} else if (self.events[i].type == "condition" && !self.events[i].coolingDown) {
				//console.log("condition");
				if (self.events[i].cond()) {
					//console.log("success!");
					if (!(self.events[i].runFuncAtEndOfCooldown && (self.events[i].random || self.events[i].cooldown))) {
						self.events[i].func();
						//console.log("called function");
					}
					if ((self.events[i].repeat || self.events[i].runFuncAtEndOfCooldown) && (self.events[i].random || self.events[i].cooldown)) {
						//console.log("schedule")
						self.events[i].coolingDown = true;
						var event = self.events[i];
						if (!self.events[i].random) {
							self.addTimedEvent(self.events[i].cooldown, function() {if (event.runFuncAtEndOfCooldown) {event.func();} event.coolingDown = false;}, false);
						} else {
							self.addRandomTimedEvent(self.events[i].cooldownStart, self.events[i].cooldownEnd, function() {if (event.runFuncAtEndOfCooldown) {event.func();} event.coolingDown = false;}, false)
						}
					}
					if (!self.events[i].repeat) {
						self.events.splice(i, 1);
						//console.log("deleted event");
					}
				}
			}
		}
	}
	/**
	 * Calls the events that run at the end of the update
	 * @memberof GameManager
	 * @function GameMember#postUpdate
	 */
	self.postUpdate = function() {
		for (var i = self.postEvents.length - 1; i >= 0; --i) {
			self.postEvents[i].func();
			if (!self.postEvents[i].repeat) {
				self.postEvents.splice(i, 1);
			}
		}
	}
	/** 
	  * Adds a timed event to the game manager. Can trigger just once or repeatedly.
	  * @memberof GameManager
	  * @function GameManager#addTimedEvent
	  * @param {Number} steps - how often to run the event
	  * @param {function} func - the function to call when the event occurs
	  * @param {Boolean} repeat - whether or not to repeat the event after it occurs
	  */
	self.addTimedEvent = function(steps, func, repeat) {
		self.events.push({type: "timed", random: false, steps: steps, func: func, repeat: repeat, orig: steps});
	}
	/** 
	 * Adds a randomly timed event to the game manager. Can trigger just once or repeatedly.
	 * @memberof GameManager
	 * @function GameManager#addRandomTimedEvent
	 * @param {Number} randomStart - how often to run the event at minimum
	 * @param {Number} randomEnd - how often to run the event at maximum
	 * @param {function} func - the function to call when the event occurs
	 * @param {Boolean} repeat - whether or not to repeat the event after it occurs
	 */
	self.addRandomTimedEvent = function(randomStart, randomEnd, func, repeat) {
		var s = function() {return randomStart + Math.random()*(randomEnd-randomStart)};
		self.events.push({type: "timed", random: true, steps: s(), func: func, repeat: repeat, orig: s});
	}
	/** 
	  * Adds a condition-based event to the game manager. Can trigger just once or repeatedly.
	  * @memberof GameManager
		* @function GameManager#addConditionEvent
	  * @param {function} cond - the condition to check
	  * @param {function} func - the function to run when the event is triggered
	  * @param {Boolean} repeat - whether or not to repeat the event after it occurs
	  * @param {Number} cooldown - if repeating event, cooldown can be optionally used to signify how long to wait until the event can be trigerred again
	  * @param {Boolean} [runFuncAtEndOfCooldown=false] - Whether or not to run the function at the end of the cooldown when an event is triggered instead of the beginning
	  */
	self.addConditionEvent = function(cond, func, repeat, cooldown = 0, runFuncAtEndOfCooldown = false) {
		self.events.push({type: "condition", cond: cond, func: func, repeat: repeat, cooldown: cooldown, random: false, coolingDown: false, runFuncAtEndOfCooldown: runFuncAtEndOfCooldown});
	}
	/** 
	  * Adds a condition-based event to the game manager with a random cooldown. Can trigger just once or repeatedly, and can have cooldown between checking if triggered
	  * @memberof GameManager
		* @function GameManager#addRandomCooldownConditionEvent
	  * @param {function} cond - the condition to check
	  * @param {function} func - the function to run when the event is triggered
	  * @param {Boolean} repeat - whether or not to repeat the event after it occurs
	  * @param {Number} cooldownStart - minimum value to use for cooldown
	  * @param {Number} cooldownEnd - maximum value to use for cooldown
	  * @param {Boolean} [runFuncAtEndOfCooldown=false] - Whether or not to run the function at the end of the cooldown when an event is triggered instead of the beginning
	  */
	self.addRandomCooldownConditionEvent = function(cond, func, repeat, cooldownStart, cooldownEnd, runFuncAtEndOfCooldown = false) {
		self.events.push({type: "condition", cond: cond, func: func, repeat: repeat, cooldownStart: cooldownStart, cooldownEnd: cooldownEnd, random: true, coolingDown: false, runFuncAtEndOfCooldown: runFuncAtEndOfCooldown});
	}
	
	/**
	 * @memberof GameManager
	 * events that are ran after game updates (instead of before, like the others)
	 */
	self.postEvents = [];
	
	/**
	 * Adds a event that triggers at the end of the update, before any objects are updated. Does not have any timer or condition
	 * @memberof GameManager
	 * @function GameManager#addPostUpdateEvent
	 * @param {function} func - the function to run when the event is triggered
	 * @param {Boolean} repeat - whether or not to repeat the event
	 */
	self.addPostUpdateEvent = function(func, repeat) {
		self.postEvents.push({func: func, repeat: repeat});
	}
}

/** Represents Score
 * @class Score
 * @param {Game} game - the game to associate with the score
 * @property {Boolean} isCollidable=false - value if the object can be collided with. Defaults false. 
 * @property {Number} score - the value that holds the score number
 * @property {Boolean} displayScore=true - value if the score should be shown or not. Defaults to true.
 * @property {String} scoreColor="black" - value of the color that score wil be draw in. Can be word colors or hex values. Defaults to "black"
 * @property {String} scoreFont="bold 12px Palatino Linotype" - value of the font that score will draw in. Defaults to "bold 12px Palatino Linotype"
 * @property {Number} scoreX=0 - the x coordinate of the score. Defaults to zero.
 * @property {Number} scoreY=0 - the y coordinate of the score. Defaults to 10.
 */
function Score(game){
	var self = this;
	/** Creates a Score object 
	 * @memberof Score
	 * @function Score#constructor
	 * @param {Game} game - the game to associate with the score
	 */
	self.constructor = function(game){
		GameObject.call(self, "score", null, 0, 0);
		self.isCollidable=false;
		self.score = 0;
		self.game = game;
		self.displayScore = true;
		self.scoreColor = "black";
		self.scoreFont = "bold 12px Palatino Linotype"
		self.scoreX = 0;
		self.scoreY = 10;
		self.game.score.push(self);
	}
	
	/** Creates the Score object */
	self.constructor(game);
	
	/** Sets if the score is showing on the canvas 
	 * @memberof Score
	 * @function Score#setDisplay
	 * @param {Boolean} display - true if you want score to show, false if you want it to disappear
	 */
	self.setDisplay = function(display){
		self.displayScore = display;
	}
	
	/** Sets the color of the Score
	 * @memberof Score
	 * @function Score#setColor
	 * @param {String} color - the color you want the score to be
	 */
	self.setColor = function(color){
		self.scoreColor = color;
	}
	
	/** Sets the font of the Score
	 *  @memberof Score
	 *  @function Score#setFont
	 *  @param {String} font - the font of the color
	 */
	self.setFont = function(font){
		self.scoreFont = font;
	}
	
	/** Sets the x coordinate of the Score
	 *  @memberof Score
	 *  @function Score#setX
	 *  @param {Number} x - the x coordinate of the Score
	 */
	self.setX = function(x){
		self.scoreX = x;
	}
	
	/** Sets the y coordinate of the Score
	 *  @memberof Score
	 *  @function Score#setY
	 *  @param {Number} y - the y coordinate of the Score
	 */
	self.setY = function(y){
		self.scoreY = y;
	}
	
	/** Adds a number to the score
	 *  @memberof Score
	 *  @function Score#addScore
	 *  @param {Number} score - number to add to the current score
	 */
	self.addScore = function(score){ 
		self.score += score;
	}
	
	/** Subtracts a number from the score
	 *  @memberof Score
	 *  @function Score#substractScore
	 *  @param {Number} score - number to subtract from the current score
	 */
	self.subtractScore = function(score){
		self.score -= score;
	}

	/** Gets the score
	 *  @memberof Score
	 *  @function Score#getScore
	 *  @returns the score
	 */
	self.getScore = function() {
		return self.score;
	}
	
	/** Resets the score to 0
	 *  @memberof Score
	 *  @function Score#restart
	 */
	self.restart = function() {
		self.score = 0;
	}
}

/** Represents HighScore
  * @class HighScore
  * @param {Number} max - the max number of high scores to keep track of.
  * @property {Number} highScoreMax - the maximum number of high scores to keep track of.
  * @property {Array} highScores - the array that holds the high score. Each position in the array holds another array with the value ["name", score].
  */
function HighScore(max){
	var self = this;
	
	 /** Creates a HighScore object 
	 *  @memberof HighScore
	 *  @function HighScore#constructor
	 *  @param {Number} max - the max number of high scores to keep track of.
	 */
	self.constructor = function(max){
		self.highScoreMax = max;
		self.highScores = new Array();
		for(var i = 0; i < self.highScoreMax; i++){
		 self.highScores.push(["empty",0]);	
		}	
	}
	
	/** Constructs HighScore */
	self.constructor(max);
	
	/** Returns the high score at some index
	 *  @memberof HighScore
	 *  @function HighScore#getHighScoreAt
	 *  @param {Number} index - the index to get the high score at
	 *  @returns {Number} the high score at the specified index
	 */
	self.getHighScoreAt = function(index){
		return self.highScores[index][1];
	}
	
	/** Returns the name of the high score at some index
	 *  @memberof HighScore
	 *  @function HighScore#getNameAt
	 *  @param {Number} index - the index to get the high score name at
	 *  @returns {String} the name of the high score at the specified index
	 */
	self.getNameAt = function(index){
		return self.highScores[index][0];
	}
	
	/** Checks if the score given should be in the high score list
	 *  @memberof HighScore
	 *  @function HighScore#isHighScore
	 *  @param {Number} score - the score to check if it should be in the high score list
	 *  @returns {Boolean} true if it is a high score or false if it is not
	 */
	self.isHighScore = function(score){
		for(var i = 0; i < self.highScoreMax; i++){
			if(self.getHighScoreAt(i) < score){
				return true;
			}
		}
		return false;
	}
	
	/** Adds the high score to the high score list
	 *  @memberof HighScore
	 *  @function HighScore#addHighScore
	 *  @param {string} user - the name of the user that got the high score
	 *  @param {Number} highscore - the high score
	 */
	self.addHighScore = function(user, highscore){
		for(var i = 0; i < self.highScoreMax; i++){
			if(self.getHighScoreAt(i) < highscore){
				self.highScores.splice(i, 0, [user,highscore]);
				self.highScores.pop();
				break;
			}
		}
	}
	
	/** Saves the high scores list to the local browser session
	 *  @memberof HighScore
	 *  @function HighScore#saveHighScores
	 *  @param {String} [name=""] identifier for file name to distinguish it from other games
	 */
	self.saveHighScores = function(name=""){
		localStorage.setItem("highScores_"+name, JSON.stringify(self.highScores));
	}
	
	/** Gets the high scores list from the local browser session
	 *  @memberof HighScore
	 *  @function getHighScores
	 *  @param {String} [name=""] name used when saving high scores list
	 */
	self.getHighScores = function(name=""){
		var json = localStorage.getItem("highScores_"+name);
		if (json)
			return JSON.parse(json);
		return null;
	}
}

/** The life object that places the lives into the game. This is not meant to use directly
  * @class Life
  * @ignore
  * @param {sprite} sprite - the sprite that the lives will look like
  * @param {Number} x - the x position
  * @param {Number} y - the y position
  */
function Life(sprite, x, y){
	var self = this;
	/** constructor
	* @function Life#constructor
	* @memberof Life
	* @param {sprite} sprite - the sprite that the lives will look like
	* @param {Number} x - the x position
	* @param {Number} y - the y position
	*/
	self.constructor = function(sprite, x, y){
		GameObject.call(self,"life",sprite, x, y);
	}
	self.constructor(sprite, x, y);
}

/** Creates lives that can be seen in the top left hand corner 
  * @class Lives
  * @param {Number} numberOfLives - the maximum number of lives
  * @param {sprite} sprite - the sprite that the lives will look like
  * @property {Number} startX - the starting X value to draw the live sprites at 
  * @property {array} livesArray - the array that holds the sprites of each life. 
  */
function Lives(numberOfLives, sprite){
	var self = this;
	
	/** Creates the Lives class
	  * @memberof Lives
	  * @function Lives#constructor
	  * @param {Number} numberOfLives - the maximum number of lives
	  * @param {sprite} sprite - the sprite that the lives will look like
	  */
	self.constructor = function(numberOfLives, sprite){
		GameObject.call(self,"lives",null,0,0);
		var startX = 0;
		self.livesArray = new Array();
		for(var i = 0; i < numberOfLives; i++){
			life = game.objects.push(new Life(sprite, startX, 0));
			self.livesArray.push(life);
			startX += sprite.width;
		}
		self.isCollidable = false;
	}
	
	/** Creates the Lives object */
	self.constructor(numberOfLives, sprite);
	
	/** Removes one life
	  * @memberof Lives
	  * @function Lives#loseLife
	  */
	self.loseLife = function(){
		var poppedLife = self.livesArray.pop();
		game.objects.remove(poppedLife);
	}
	
	/** Returns how many lives currently stored
	  * @memberof Lives
	  * @function Lives#amountLivesLeft
	  * @returns {Number} the number of lives left
	  */
	self.amountLivesLeft = function(){
		return self.livesArray.length;
	}
	
	/** Restarts the amount of lives to the maximum
	  * @memberof Lives
	  * @function Lives#restart
	  */
	self.restart = function(){
		var startX = 0;
		for(var i = 0; i < numberOfLives; i++){
			life = game.objects.push(new Life(sprite, startX, 0));
			self.livesArray.push(life);
			startX += sprite.width;
		}
	}
}