Source: game.js

"use strict";

var Scene = require("./scene");
var Mouse = require("./mouse");
var Accelerometer = require("./accelerometer");
var Keyboard = require("./keyboard");
var keyMap = require("./key_map");
var ImageLoader = require("./image_loader");
var SoundLoader = require("./sound_loader");
var FontLoader = require("./font_loader");
var AnimationLoader = require("./animation_loader");
var SceneManager = require("./scene_manager");
var platform = require("./platform");

function loadAssets(assetLoader, assets) {
	for (var key in assets) {
		if (assets.hasOwnProperty(key)) {
			assetLoader.load(key, assets[key]);
		}
	}
}

function makeLoadingScene(game, canvas, nextScene) {
	return new Scene(canvas, function() {
	}, function() {
		if (game.isLoaded()) {
			game.scenes.switchTo(nextScene);
		}
	}, function(context) {
		context.fillStyle = "#000000";
		context.fillRect(0, 0, canvas.width, canvas.height);

		var quarterWidth = (canvas.width / 4) |0;
		var halfWidth = (canvas.width / 2) |0;
		var halfHeight = (canvas.height / 2) |0;

		context.fillStyle = "#ffffff";
		context.fillRect(quarterWidth, halfHeight - 15, halfWidth, 30);

		context.fillStyle = "#000000";
		context.fillRect(quarterWidth + 3, halfHeight - 12, halfWidth - 6, 24);

		context.fillStyle = "#ffffff";
		var barWidth = (halfWidth - 6) * game.percentLoaded();
		context.fillRect(quarterWidth + 3, halfHeight - 12, barWidth, 24);
	});
}

function setCanvasSizeScaled(canvas) {
	var ww = window.innerWidth;
	var wh = window.innerHeight;
	var cw = canvas.width;
	var ch = canvas.height;

	if (ww >= cw && wh >= ch) {
		return;
	} else if (ww < cw && wh >= ch) {
		wh = ((ww / cw) * ch) | 0;
		canvas.style.width = ww + "px";
		canvas.style.height = wh + "px";
	} else if (ww >= cw && wh < ch) {
		ww = ((wh / ch) * cw) | 0;
		canvas.style.width = ww + "px";
		canvas.style.height = wh + "px";
	} else if (ww < cw && wh < ch) {
		if ((ww / cw) * ch > wh) {
			ww = ((wh / ch) * cw) | 0;
		} else {
			wh = ((ww / cw) * ch) | 0;
		}
		canvas.style.width = ww + "px";
		canvas.style.height = wh + "px";
	}
}

/**
 * Represents a whole game. This class contains all the inputs, outputs, and data for the game.
 * @constructor
 * @alias Splat.Game
 * @param {external:canvas} canvas The canvas on which to render the game.
 * @param {object} manifest A key-value set of attributes that describe all the external resources for the game. This references all the images, sounds, fonts, and animations.
 * @example
	var canvas = document.getElementById("canvas");
	var manifest = {
		"images": {
			"bg": "images/bg.png"
		},
		"sounds": {
			"point": "sounds/point.wav"
		},
		"fonts": [
			"pixelade": {
				"embedded-opentype": "pixelade/pixelade-webfont.eot",
				"woff": "pixelade/pixelade-webfont.woff",
				"truetype": "pixelade/pixelade-webfont.ttf",
				"svg": "pixelade/pixelade-webfont.svg#pixeladeregular"
			}
		],
		"animations": {
			"player-slide-left": {
				"strip": "images/player-slide-anim.png",
				"frames": 8,
				"msPerFrame": 100
			}
		}
	};
	var game = new Splat.Game(canvas, manifest);
 */
function Game(canvas, manifest) {
	window.addEventListener("resize", function() { setCanvasSizeScaled(canvas); });
	setCanvasSizeScaled(canvas);

	var game = this;
	var wasMuted = false;
	window.addEventListener("visibilitychange", function() {
		var scene = game.scenes.currentScene;
		if (typeof scene.visibilitychange === "function") {
			scene.visibilitychange(document.visibilityState);
			return;
		}
		if (document.visibilityState === "hidden") {
			scene.stop();
			wasMuted = game.sounds.muted;
			game.sounds.mute();
		} else {
			scene.start();
			if (!wasMuted) {
				game.sounds.unmute();
			}
		}
	});

	/**
	 * The mouse input for the game.
	 * @member {Mouse}
	 */
	this.mouse = new Mouse(canvas);
	/**
	 * The keyboard input for the game.
	 * @member {Keyboard}
	 */
	this.keyboard = new Keyboard(keyMap.US);
	/**
	 * The gamepad input for the game.
	 * @member {Gamepad}
	 */
	this.gamepad = require("./gamepad");
	/**
	 * The accelerometer input for the game.
	 * @member {Accelerometer}
	 */
	this.accelerometer = new Accelerometer();

	/**
	 * The image assets for the game.
	 * @member {ImageLoader}
	 */
	this.images = new ImageLoader();
	loadAssets(this.images, manifest.images);

	/**
	 * The sound assets for the game.
	 * @member {SoundLoader}
	 */
	this.sounds = new SoundLoader();
	loadAssets(this.sounds, manifest.sounds);

	/**
	 * The font assets for the game.
	 * @member {FontLoader}
	 */
	this.fonts = new FontLoader();
	this.fonts.load(manifest.fonts);

	/**
	 * The animation assets for the game.
	 * @member {AnimationLoader}
	 */
	this.animations = new AnimationLoader(this.images, manifest.animations);

	/**
	 * The scenes for the game.
	 * @member {SceneManager}
	 */
	this.scenes = new SceneManager();
	this.scenes.add("loading", makeLoadingScene(this, canvas, "title"));
}
/**
 * Test if all the game's assets are loaded.
 * @returns {boolean}
 */
Game.prototype.isLoaded = function() {
	return this.images.allLoaded() &&
		this.sounds.allLoaded() &&
		this.fonts.allLoaded() &&
		this.animations.allLoaded();
};
/**
 * Determine the percent of the game's assets that are loaded. This is useful for drawing a loading bar.
 * @returns {number} A number between 0 and 1
 */
Game.prototype.percentLoaded = function() {
	var totalAssets =
		this.images.totalImages +
		this.sounds.totalSounds +
		this.fonts.totalFonts;
	var loadedAssets =
		this.images.loadedImages +
		this.sounds.loadedSounds +
		this.fonts.loadedFonts;
	return loadedAssets / totalAssets;
};
/**
 * Test if the game is running within a Chrome App.
 * @returns {boolean}
 */
Game.prototype.isChromeApp = function() {
	return platform.isChromeApp();
};

module.exports = Game;