File:KartKingdom.js

/**
 * @module Kart Kingdom
 * @namespace springroll.pbskids
 * @requires Core
 */
(function()
{
	// Include classes
	var EventDispatcher = include('springroll.EventDispatcher');

	/**
	 * The wrapper for talking to the Kart Kingdom Minigame API.
	 * @class  KartKingdom
	 * @constructor
	 * @param {Object} config The configuration
	 * @param {String} config.gameId GUID for your minigame provided by your PBS KIDS content manager
	 * @param {String} config.elementId A string value of the ID of your game's containing element;
	 *        presumably a div element which wraps your games canvas element.
	 * @param {Object} [config.options] A configuration object which allows developers to configure
	 *        the notification bubble's position and animation timing.
	 * @param {String} [config.options.dock] "TR", "TL", "BL", "BR"
	 * @param {Boolean} [config.options.enable_notifications]
	 * @param {int} [config.options.hide_notification_delay]
	 * @param {int} [config.options.fade_notification_duration]
	 * @param {int} [config.options.slide_notification_duration]
	 */
	var KartKingdom = function(config)
	{
		EventDispatcher.call(this);

		/**
		 * Reference to the container instance
		 * @property {Bellhop} container
		 */
		this.container = null;

		/**
		 * The configuration
		 * @property {Object} config
		 */
		this.config = config;

		/**
		 * If this API is enabled
		 * @property {Boolean} enabled
		 * @readOnly
		 * @default false
		 */
		this.enabled = false;
	};

	// Reference to the prototype
	var p = EventDispatcher.extend(KartKingdom);


	//--------------------
	// PUBLIC METHODS
	//--------------------

	/**
	 * Initialize the API
	 * @method  init
	 * @param {Bellhop} container The container bellhop instance
	 * @param {Function} done When we're done
	 */
	p.init = function(container, done)
	{
		this.container = container;
		this.enabled = container.supported;

		if (!this.enabled)
		{
			return done();
		}

		// Setup event handlers for the bellhop events
		container.on('kk-ready', _onMinigameReady.bind(this))
			.on('kk-level-open', _onLevelOpen.bind(this))
			.on('kk-level-closed', _onLevelClosed.bind(this))
			.on('kk-level-reset', _onLevelReset.bind(this))
			.on('kk-resources-earned', _onResourcesEarned.bind(this))
			.on('kk-resources-complete', _onResourcesComplete.bind(this))
			.on('kk-init-done', done)
			.send('kk-init', this.config); // Initialize the kart kingdom minigame
	};

	/**
	 * Called by the producer mini-game whenever the player has accomplished something
	 * in the minigame in which they should receive a reward for the virtual world.
	 * Upon obtaining a reward, a notification will slide-down over the game in the
	 * top-right corner. This notification fades away after 2 seconds.
	 * @method event
	 * @param {String} guid is the guid string, e.g. "ccc006dc-012e-49d0-8b32-35d70df206e9",
	 * for the event/task/item which the user has completed/performed/found which should earn
	 * them a reward in/for the virtual world. To generate events for your mini-game and receive
	 * the guids for those events please contact your PBS KIDS content manager.
	 */
	p.event = function(guid)
	{
		if (!this.enabled || this.destroyed) return;
		this.container.send('kk-event', guid);
	};

	/**
	 * Checks if a player is in possession of the requested powerup. Returns false or the integer
	 * quantity of how many of the requested powerup the player possesses.
	 * @method hasPowerUp
	 * @param {String} type the name of the powerup to check, e.g. "rabbit".
	 * @param {Function} callback Payload is a either an Integer or boolean false. <br/> *Integer* -- the current amount of the powerup which
	 * the user has. Return 0 if unlocked but depleted (or not yet acquired). <br/> *Boolean* -- **false**,
	 * if the powerup has not been unlocked by the player.
	 */
	p.hasPowerUp = function(type, callback)
	{
		if (!this.enabled || this.destroyed) return;
		this.container.fetch('kk-has-power-up', function(e)
		{
			callback(e.data);
		}, type, true);
	};

	/**
	 * Consumes the requested powerup in the quantities listed for each in the **powerupsObject** object.
	 * Upon validating that the user has all of the powerups in the required amounts, the api will then
	 * make an ajax request to the virtual world to remove (consume) the powerups. The user has successfully
	 * consumed the powerups **ONLY** when the request to the virtual world returns a *200 OK* response; upon
	 * receiving this response the **onSuccessCallback** function is called to notify the minigame of the success.
	 * @method usePowerUps
	 * @param {Object} powerupsObject Object which lists the powerups to consume and in what amounts.
	 * The key-value pairs for each item in the object are the powerup's name as the key and the quantity
	 * to consume as the value, e.g. `{ "water" : 3, "rainforest-map" : 1 }`.
	 * @param {Function} onSuccessCallback A callback method to call when the virtual world backend successfully
	 * consumes the powerups for the user and the user is good to continue in the game.
	 * @param {Function} onFailureCallback A callback method to call when the ajax request to the virtual world
	 * to consume the powerups fails or otherwise does not allow the user to consume the requested powerups.
	 */
	var requestID = 1;
	p.usePowerUps = function(powerupsObject, onSuccessCallback, onFailureCallback)
	{
		if (!this.enabled || this.destroyed) return;
		if (!onSuccessCallback || typeof onSuccessCallback !== "function")
		{
			if (DEBUG) throw 'Please supply an onSuccessCallback method as the second parameter.';
			return;
		}

		var self = this;
		var successEvent = "kk-use-power-ups-success-" + requestID;
		var failureEvent = "kk-use-power-ups-failure-" + requestID;
		requestID++;

		this.container.on(successEvent, function()
		{
			onSuccessCallback();
			self.container.off(successEvent);
		});

		this.container.on(failureEvent, function()
		{
			if (onFailureCallback && typeof onFailureCallback === "function") onFailureCallback();
			self.container.off(failureEvent);
		});

		this.container.send('kk-use-power-ups',
		{
			"powerupsObject": powerupsObject,
			"successEvent": successEvent,
			"failureEvent": failureEvent
		});
	};

	/**
	 * Called by the producer mini-game when the player has completed a level/game, lost, or the
	 * game is otherwise over. Upon calling this method 1 of 3 will appear over your minigame:
	 *  1. If the player is not logged in, then they will see a screen with a link to the virtual world and the message,
	 *  	"You could be earning cool stuff for the new virtual world, Kart Kingdom!"
	 *
	 *  2. If the player is logged in but has never played in the virtual world, then they will see a screen with the message,
	 *  	"You could be earning cool stuff for the new virtual world, Kart Kingdom! Come play with other kids!"
	 *
	 *  3. If the player is logged in and has played in the virtual world before, then:
	 *  	* **a.** If they earned any rewards during this game session, then they will see a list of of the new rewards they earned.
	 *
	 *  	* **b.** If they did not earn any rewards during this game session, then no screen will appear.
	 * @method levelComplete
	 */
	p.levelComplete = function()
	{
		if (!this.enabled || this.destroyed) return;
		this.container.send('kk-level-complete');
	};

	/**
	 * Destroy the API connection
	 * @method destroy
	 */
	p.destroy = function()
	{
		if (!this.enabled || this.destroyed) return;
		if (this.container)
		{
			this.container.send('kk-destroy');
			this.container = null;
		}
		this.config = null;
		EventDispatcher.prototype.destroy.call(this);
	};



	//--------------------
	// EVENT HANDLERS
	//--------------------

	/**
	 * Handler when the game is ready
	 * @method _onMinigameReady
	 * @private
	 * @param {Object} event Bellhop event
	 */
	var _onMinigameReady = function(event)
	{
		/**
		 * Defines the value of a MinigameReady event. This event fires when a minigame
		 * instance has completed instantiation; this includes having validated the environment,
		 * checked if a user is logged in, and checked if the user has access to the virtual world.
		 * If this event is fired prior to adding an eventlistener for it, then the handler
		 * (2nd parameter) passed to the addEventListener() method will be called immediately
		 * upon event binding with a payload similar to the original event payload.
		 * @event ready
		 * @param {Boolean} isLoggedIn value stating if there is a user currently logged into pbskids.org
		 * @param {Boolean} hasPlayedVirtualWorld value stating if the logged in user has accessed the
		 *        virtual world before and has granted the virtual world access to their pbskids.org
		 *        account.
		 */
		this.trigger('ready', event.data.isLoggedIn, event.data.hasPlayedVirtualWorld);
	};

	/**
	 * Handler when the resources are earned
	 * @method _onResourcesEarned
	 * @private
	 * @param {Object} event Bellhop event
	 */
	var _onResourcesEarned = function(event)
	{
		/**
		 * Defines the value of a ResourcesEarned event. This event is fired when a player
		 * has earned resources/rewards from the virtual world's rewards system. This event
		 * ONLY occurs if a .event() call results in rewards for that player. If a .event()
		 * call is made where the potential reward is a limited resource and the player has
		 * met that limit, then the player will not earn a reward/resource and this event
		 * will not fire.
		 * @event resourcesEarned
		 * @param {Object} resources An object listing the resources earned from the previous
		 * game event. Has the form `{ resourceName : quantityEarned, resourceName2 : quantityEarned2 }`,
		 * e.g. `{ tools : 1 , water : 3 }`.
		 */
		this.trigger('resourcesEarned', event.data);
	};

	/**
	 * Handler when the resources are complete
	 * @method _onResourcesComplete
	 * @private
	 * @param {Object} event Bellhop event
	 */
	var _onResourcesComplete = function(event)
	{
		/**
		 * Defines the value of a ResourceRequestComplete event. This event fires every time a
		 * request to the rewards api has completed and either returned a response with a rewards
		 * payload, no payload, of if the requests somehow failed and could not complete. Ideally,
		 * this event should fire for each .event() call made. The payload for this jQuery event
		 * object has no additional game data.
		 * @event resourcesComplete
		 */
		this.trigger('resourcesComplete');
	};

	/**
	 * Handler for the bellhop event
	 * @method _onLevelOpen
	 * @private
	 * @param {Object} event Bellhop event
	 */
	var _onLevelOpen = function(event)
	{
		/**
		 * Defines the value of a LevelCompleteOpen event. This event is fired when the .levelcomplete()
		 * method is called and the level complete overlay opens. The payload for this jQuery event
		 * object has no additional game data.
		 * @event levelOpen
		 */
		this.trigger('levelOpen');
	};

	/**
	 * Handler for the bellhop event
	 * @method _onLevelClosed
	 * @private
	 * @param {Object} event Bellhop event
	 */
	var _onLevelClosed = function(event)
	{
		/**
		 * Defines the value of a LevelCompleteClosed event. This event is fired when the level complete
		 * overlay is closed by the user. The payload for this jQuery event object has no additional
		 * game data.
		 * @event levelClosed
		 */
		this.trigger('levelClosed');
	};

	/**
	 * Handler for the bellhop event
	 * @method _onLevelReset
	 * @private
	 * @param {Object} event Bellhop event
	 */
	var _onLevelReset = function(event)
	{
		/**
		 * Defines the value of a LevelReset event. This event is fired when the player stats have been
		 * reset and the enabled state of the notification bubble has been reset to it's initial value.
		 * The payload for this jQuery event object has no additional game data. This event occurs,
		 * * after the level complete overlay has been closed by the user and after the LevelCompleteClosed
		 * event has fired,
		 * or
		 * * after the .levelComplete() method is called, the user is logged in with virtual world access,
		 * 	and the user has NOT earned any resources/rewards for this level. This would result in a level
		 *  reset to occur upon calling the .levelComplete() method and bypassing the level complete display.
		 * @event levelReset
		 */
		this.trigger('levelReset');
	};


	// Define namespace
	namespace('springroll.pbskids').KartKingdom = KartKingdom;

}());